summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/webdav
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /ucb/source/ucp/webdav
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--ucb/source/ucp/webdav-neon/ContentProperties.cxx558
-rw-r--r--ucb/source/ucp/webdav-neon/ContentProperties.hxx183
-rw-r--r--ucb/source/ucp/webdav-neon/DAVAuthListener.hxx53
-rw-r--r--ucb/source/ucp/webdav-neon/DAVAuthListenerImpl.hxx72
-rw-r--r--ucb/source/ucp/webdav-neon/DAVException.hxx177
-rw-r--r--ucb/source/ucp/webdav-neon/DAVProperties.cxx204
-rw-r--r--ucb/source/ucp/webdav-neon/DAVProperties.hxx68
-rw-r--r--ucb/source/ucp/webdav-neon/DAVRequestEnvironment.hxx67
-rw-r--r--ucb/source/ucp/webdav-neon/DAVResource.hxx73
-rw-r--r--ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx1192
-rw-r--r--ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx234
-rw-r--r--ucb/source/ucp/webdav-neon/DAVSession.hxx216
-rw-r--r--ucb/source/ucp/webdav-neon/DAVSessionFactory.cxx99
-rw-r--r--ucb/source/ucp/webdav-neon/DAVSessionFactory.hxx88
-rw-r--r--ucb/source/ucp/webdav-neon/DAVTypes.cxx193
-rw-r--r--ucb/source/ucp/webdav-neon/DAVTypes.hxx211
-rw-r--r--ucb/source/ucp/webdav-neon/DateTimeHelper.cxx247
-rw-r--r--ucb/source/ucp/webdav-neon/DateTimeHelper.hxx64
-rw-r--r--ucb/source/ucp/webdav-neon/LinkSequence.cxx214
-rw-r--r--ucb/source/ucp/webdav-neon/LinkSequence.hxx52
-rw-r--r--ucb/source/ucp/webdav-neon/LockEntrySequence.cxx246
-rw-r--r--ucb/source/ucp/webdav-neon/LockEntrySequence.hxx50
-rw-r--r--ucb/source/ucp/webdav-neon/LockSequence.cxx360
-rw-r--r--ucb/source/ucp/webdav-neon/LockSequence.hxx51
-rw-r--r--ucb/source/ucp/webdav-neon/NeonHeadRequest.cxx139
-rw-r--r--ucb/source/ucp/webdav-neon/NeonHeadRequest.hxx56
-rw-r--r--ucb/source/ucp/webdav-neon/NeonInputStream.cxx136
-rw-r--r--ucb/source/ucp/webdav-neon/NeonInputStream.hxx97
-rw-r--r--ucb/source/ucp/webdav-neon/NeonLockStore.cxx241
-rw-r--r--ucb/source/ucp/webdav-neon/NeonLockStore.hxx99
-rw-r--r--ucb/source/ucp/webdav-neon/NeonPropFindRequest.cxx321
-rw-r--r--ucb/source/ucp/webdav-neon/NeonPropFindRequest.hxx66
-rw-r--r--ucb/source/ucp/webdav-neon/NeonSession.cxx2361
-rw-r--r--ucb/source/ucp/webdav-neon/NeonSession.hxx288
-rw-r--r--ucb/source/ucp/webdav-neon/NeonTypes.hxx57
-rw-r--r--ucb/source/ucp/webdav-neon/NeonUri.cxx291
-rw-r--r--ucb/source/ucp/webdav-neon/NeonUri.hxx102
-rw-r--r--ucb/source/ucp/webdav-neon/PropertyMap.hxx68
-rw-r--r--ucb/source/ucp/webdav-neon/PropfindCache.cxx89
-rw-r--r--ucb/source/ucp/webdav-neon/PropfindCache.hxx80
-rw-r--r--ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.cxx509
-rw-r--r--ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.hxx53
-rw-r--r--ucb/source/ucp/webdav-neon/ucpdav1.component34
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontent.cxx4217
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontent.hxx320
-rw-r--r--ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx653
-rw-r--r--ucb/source/ucp/webdav-neon/webdavdatasupplier.cxx487
-rw-r--r--ucb/source/ucp/webdav-neon/webdavdatasupplier.hxx81
-rw-r--r--ucb/source/ucp/webdav-neon/webdavprovider.cxx188
-rw-r--r--ucb/source/ucp/webdav-neon/webdavprovider.hxx115
-rw-r--r--ucb/source/ucp/webdav-neon/webdavresultset.cxx86
-rw-r--r--ucb/source/ucp/webdav-neon/webdavresultset.hxx60
-rw-r--r--ucb/source/ucp/webdav-neon/webdavservices.cxx67
-rw-r--r--ucb/source/ucp/webdav/AprEnv.cxx64
-rw-r--r--ucb/source/ucp/webdav/AprEnv.hxx58
-rw-r--r--ucb/source/ucp/webdav/ContentProperties.cxx593
-rw-r--r--ucb/source/ucp/webdav/ContentProperties.hxx184
-rw-r--r--ucb/source/ucp/webdav/DAVAuthListener.hxx46
-rw-r--r--ucb/source/ucp/webdav/DAVAuthListenerImpl.hxx64
-rw-r--r--ucb/source/ucp/webdav/DAVException.hxx168
-rw-r--r--ucb/source/ucp/webdav/DAVProperties.cxx222
-rw-r--r--ucb/source/ucp/webdav/DAVProperties.hxx60
-rw-r--r--ucb/source/ucp/webdav/DAVRequestEnvironment.hxx57
-rw-r--r--ucb/source/ucp/webdav/DAVResource.hxx62
-rw-r--r--ucb/source/ucp/webdav/DAVResourceAccess.cxx1116
-rw-r--r--ucb/source/ucp/webdav/DAVResourceAccess.hxx210
-rw-r--r--ucb/source/ucp/webdav/DAVSession.hxx205
-rw-r--r--ucb/source/ucp/webdav/DAVSessionFactory.cxx86
-rw-r--r--ucb/source/ucp/webdav/DAVSessionFactory.hxx72
-rw-r--r--ucb/source/ucp/webdav/DAVTypes.hxx80
-rw-r--r--ucb/source/ucp/webdav/DateTimeHelper.cxx258
-rw-r--r--ucb/source/ucp/webdav/DateTimeHelper.hxx58
-rw-r--r--ucb/source/ucp/webdav/PropertyMap.hxx58
-rw-r--r--ucb/source/ucp/webdav/SerfCallbacks.cxx111
-rw-r--r--ucb/source/ucp/webdav/SerfCallbacks.hxx69
-rw-r--r--ucb/source/ucp/webdav/SerfCopyReqProcImpl.cxx82
-rw-r--r--ucb/source/ucp/webdav/SerfCopyReqProcImpl.hxx57
-rw-r--r--ucb/source/ucp/webdav/SerfDeleteReqProcImpl.cxx67
-rw-r--r--ucb/source/ucp/webdav/SerfDeleteReqProcImpl.hxx52
-rw-r--r--ucb/source/ucp/webdav/SerfGetReqProcImpl.cxx173
-rw-r--r--ucb/source/ucp/webdav/SerfGetReqProcImpl.hxx84
-rw-r--r--ucb/source/ucp/webdav/SerfHeadReqProcImpl.cxx128
-rw-r--r--ucb/source/ucp/webdav/SerfHeadReqProcImpl.hxx67
-rw-r--r--ucb/source/ucp/webdav/SerfInputStream.cxx159
-rw-r--r--ucb/source/ucp/webdav/SerfInputStream.hxx93
-rw-r--r--ucb/source/ucp/webdav/SerfLockReqProcImpl.cxx203
-rw-r--r--ucb/source/ucp/webdav/SerfLockReqProcImpl.hxx67
-rw-r--r--ucb/source/ucp/webdav/SerfLockStore.cxx218
-rw-r--r--ucb/source/ucp/webdav/SerfLockStore.hxx91
-rw-r--r--ucb/source/ucp/webdav/SerfMkColReqProcImpl.cxx67
-rw-r--r--ucb/source/ucp/webdav/SerfMkColReqProcImpl.hxx51
-rw-r--r--ucb/source/ucp/webdav/SerfMoveReqProcImpl.cxx82
-rw-r--r--ucb/source/ucp/webdav/SerfMoveReqProcImpl.hxx57
-rw-r--r--ucb/source/ucp/webdav/SerfPostReqProcImpl.cxx126
-rw-r--r--ucb/source/ucp/webdav/SerfPostReqProcImpl.hxx76
-rw-r--r--ucb/source/ucp/webdav/SerfPropFindReqProcImpl.cxx195
-rw-r--r--ucb/source/ucp/webdav/SerfPropFindReqProcImpl.hxx77
-rw-r--r--ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.cxx189
-rw-r--r--ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.hxx58
-rw-r--r--ucb/source/ucp/webdav/SerfPutReqProcImpl.cxx90
-rw-r--r--ucb/source/ucp/webdav/SerfPutReqProcImpl.hxx60
-rw-r--r--ucb/source/ucp/webdav/SerfRequestProcessor.cxx596
-rw-r--r--ucb/source/ucp/webdav/SerfRequestProcessor.hxx191
-rw-r--r--ucb/source/ucp/webdav/SerfRequestProcessorImpl.cxx151
-rw-r--r--ucb/source/ucp/webdav/SerfRequestProcessorImpl.hxx79
-rw-r--r--ucb/source/ucp/webdav/SerfSession.cxx1489
-rw-r--r--ucb/source/ucp/webdav/SerfSession.hxx262
-rw-r--r--ucb/source/ucp/webdav/SerfUnlockReqProcImpl.cxx68
-rw-r--r--ucb/source/ucp/webdav/SerfUnlockReqProcImpl.hxx54
-rw-r--r--ucb/source/ucp/webdav/SerfUri.cxx248
-rw-r--r--ucb/source/ucp/webdav/SerfUri.hxx103
-rw-r--r--ucb/source/ucp/webdav/UCBDeadPropertyValue.cxx526
-rw-r--r--ucb/source/ucp/webdav/UCBDeadPropertyValue.hxx60
-rw-r--r--ucb/source/ucp/webdav/ucpdav1.component27
-rw-r--r--ucb/source/ucp/webdav/webdavcontent.cxx3292
-rw-r--r--ucb/source/ucp/webdav/webdavcontent.hxx269
-rw-r--r--ucb/source/ucp/webdav/webdavcontentcaps.cxx602
-rw-r--r--ucb/source/ucp/webdav/webdavdatasupplier.cxx466
-rw-r--r--ucb/source/ucp/webdav/webdavdatasupplier.hxx76
-rw-r--r--ucb/source/ucp/webdav/webdavprovider.cxx176
-rw-r--r--ucb/source/ucp/webdav/webdavprovider.hxx111
-rw-r--r--ucb/source/ucp/webdav/webdavresponseparser.cxx897
-rw-r--r--ucb/source/ucp/webdav/webdavresponseparser.hxx40
-rw-r--r--ucb/source/ucp/webdav/webdavresultset.cxx76
-rw-r--r--ucb/source/ucp/webdav/webdavresultset.hxx51
-rw-r--r--ucb/source/ucp/webdav/webdavservices.cxx56
126 files changed, 32869 insertions, 0 deletions
diff --git a/ucb/source/ucp/webdav-neon/ContentProperties.cxx b/ucb/source/ucp/webdav-neon/ContentProperties.cxx
new file mode 100644
index 000000000..12bbf4f74
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/ContentProperties.cxx
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include <memory>
+#include <com/sun/star/util/DateTime.hpp>
+#include "NeonUri.hxx"
+#include "DAVResource.hxx"
+#include "DAVProperties.hxx"
+#include "DateTimeHelper.hxx"
+#include "webdavprovider.hxx"
+#include "ContentProperties.hxx"
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+/*
+=============================================================================
+
+ Property Mapping
+
+=============================================================================
+HTTP (entity header) WebDAV (property) UCB (property)
+=============================================================================
+
+Allow
+Content-Encoding
+Content-Language getcontentlanguage
+Content-Length getcontentlength Size
+Content-Location
+Content-MD5
+Content-Range
+Content-Type getcontenttype MediaType
+Expires
+Last-Modified getlastmodified DateModified
+ creationdate DateCreated
+ resourcetype IsFolder,IsDocument,ContentType
+ displayname
+ETag (actually getetag
+a response header )
+ lockdiscovery
+ supportedlock
+ source
+ Title (always taken from URI)
+
+=============================================================================
+
+Important: HTTP headers will not be mapped to DAV properties; only to UCB
+ properties. (Content-Length,Content-Type,Last-Modified)
+*/
+
+
+// ContentProperties Implementation.
+
+
+// static member!
+uno::Any ContentProperties::m_aEmptyAny;
+
+ContentProperties::ContentProperties( const DAVResource& rResource )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ assert( !rResource.uri.isEmpty() && "ContentProperties ctor - Empty resource URI!" );
+
+ // Title
+ try
+ {
+ NeonUri aURI( rResource.uri );
+ m_aEscapedTitle = aURI.GetPathBaseName();
+
+ (*m_xProps)[ OUString("Title") ]
+ = PropertyValue(
+ uno::makeAny( aURI.GetPathBaseNameUnescaped() ), true );
+ }
+ catch ( DAVException const & )
+ {
+ (*m_xProps)[ OUString("Title") ]
+ = PropertyValue(
+ uno::makeAny(
+ OUString(
+ "*** unknown ***" ) ),
+ true );
+ }
+
+ for ( const auto& rProp : rResource.properties )
+ {
+ addProperty( rProp );
+ }
+
+ if ( rResource.uri.endsWith("/") )
+ m_bTrailingSlash = true;
+}
+
+
+ContentProperties::ContentProperties(
+ const OUString & rTitle, bool bFolder )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ (*m_xProps)[ OUString("Title") ]
+ = PropertyValue( uno::makeAny( rTitle ), true );
+ (*m_xProps)[ OUString("IsFolder") ]
+ = PropertyValue( uno::makeAny( bFolder ), true );
+ (*m_xProps)[ OUString("IsDocument") ]
+ = PropertyValue( uno::makeAny( !bFolder ), true );
+}
+
+
+ContentProperties::ContentProperties( const OUString & rTitle )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ (*m_xProps)[ OUString("Title") ]
+ = PropertyValue( uno::makeAny( rTitle ), true );
+}
+
+
+ContentProperties::ContentProperties()
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+}
+
+ContentProperties::ContentProperties(const ContentProperties& rOther)
+ : m_aEscapedTitle(rOther.m_aEscapedTitle)
+ , m_xProps(rOther.m_xProps ? new PropertyValueMap(*rOther.m_xProps) : new PropertyValueMap)
+ , m_bTrailingSlash(rOther.m_bTrailingSlash)
+{
+}
+
+
+bool ContentProperties::contains( const OUString & rName ) const
+{
+ return get( rName ) != nullptr;
+}
+
+
+const uno::Any & ContentProperties::getValue(
+ const OUString & rName ) const
+{
+ const PropertyValue * pProp = get( rName );
+ if ( pProp )
+ return pProp->value();
+ else
+ return m_aEmptyAny;
+}
+
+
+const PropertyValue * ContentProperties::get(
+ const OUString & rName ) const
+{
+ PropertyValueMap::const_iterator it = m_xProps->find( rName );
+ const PropertyValueMap::const_iterator end = m_xProps->end();
+
+ if ( it == end )
+ {
+ it = std::find_if(m_xProps->cbegin(), end,
+ [&rName](const PropertyValueMap::value_type& rEntry) {
+ return rEntry.first.equalsIgnoreAsciiCase( rName );
+ });
+ if ( it != end )
+ return &(*it).second;
+
+ return nullptr;
+ }
+ else
+ return &(*it).second;
+}
+
+
+// static
+void ContentProperties::UCBNamesToDAVNames(
+ const uno::Sequence< beans::Property > & rProps,
+ std::vector< OUString > & propertyNames )
+{
+
+ // Assemble list of DAV properties to obtain from server.
+ // Append DAV properties needed to obtain requested UCB props.
+
+
+ // DAV UCB
+ // creationdate <- DateCreated
+ // getlastmodified <- DateModified
+ // getcontenttype <- MediaType
+ // getcontentlength <- Size
+ // resourcetype <- IsFolder, IsDocument, ContentType
+ // (taken from URI) <- Title
+
+ bool bCreationDate = false;
+ bool bLastModified = false;
+ bool bContentType = false;
+ bool bContentLength = false;
+ bool bResourceType = false;
+
+ for ( const beans::Property & rProp : rProps )
+ {
+ if ( rProp.Name == "Title" )
+ {
+ // Title is always obtained from resource's URI.
+ continue;
+ }
+ else if ( rProp.Name == "DateCreated" || rProp.Name == DAVProperties::CREATIONDATE )
+ {
+ if ( !bCreationDate )
+ {
+ propertyNames.push_back( DAVProperties::CREATIONDATE );
+ bCreationDate = true;
+ }
+ }
+ else if ( rProp.Name == "DateModified" || rProp.Name == DAVProperties::GETLASTMODIFIED )
+ {
+ if ( !bLastModified )
+ {
+ propertyNames.push_back( DAVProperties::GETLASTMODIFIED );
+ bLastModified = true;
+ }
+ }
+ else if ( rProp.Name == "MediaType" || rProp.Name == DAVProperties::GETCONTENTTYPE )
+ {
+ if ( !bContentType )
+ {
+ propertyNames.push_back( DAVProperties::GETCONTENTTYPE );
+ bContentType = true;
+ }
+ }
+ else if ( rProp.Name == "Size" || rProp.Name == DAVProperties::GETCONTENTLENGTH )
+ {
+ if ( !bContentLength )
+ {
+ propertyNames.push_back( DAVProperties::GETCONTENTLENGTH );
+ bContentLength = true;
+ }
+ }
+ else if ( rProp.Name == "ContentType" || rProp.Name == "IsDocument" || rProp.Name == "IsFolder" || rProp.Name == DAVProperties::RESOURCETYPE )
+ {
+ if ( !bResourceType )
+ {
+ propertyNames.push_back( DAVProperties::RESOURCETYPE );
+ bResourceType = true;
+ }
+ }
+ else
+ {
+ propertyNames.push_back( rProp.Name );
+ }
+ }
+}
+
+
+// static
+void ContentProperties::UCBNamesToHTTPNames(
+ const uno::Sequence< beans::Property > & rProps,
+ std::vector< OUString > & propertyNames )
+{
+
+ // Assemble list of HTTP header names to obtain from server.
+ // Append HTTP headers needed to obtain requested UCB props.
+
+
+ // HTTP UCB
+ // Last-Modified <- DateModified
+ // Content-Type <- MediaType
+ // Content-Length <- Size
+
+ for ( const beans::Property & rProp : rProps )
+ {
+ if ( rProp.Name == "DateModified" )
+ {
+ propertyNames.emplace_back("Last-Modified" );
+ }
+ else if ( rProp.Name == "MediaType" )
+ {
+ propertyNames.emplace_back("Content-Type" );
+ }
+ else if ( rProp.Name == "Size" )
+ {
+ propertyNames.emplace_back("Content-Length" );
+ }
+ else
+ {
+ propertyNames.push_back( rProp.Name );
+ }
+ }
+}
+
+
+bool ContentProperties::containsAllNames(
+ const uno::Sequence< beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const
+{
+ rNamesNotContained.clear();
+
+ for ( const auto& rProp : rProps )
+ {
+ const OUString & rName = rProp.Name;
+ if ( !contains( rName ) )
+ {
+ // Not found.
+ rNamesNotContained.push_back( rName );
+ }
+ }
+
+ return rNamesNotContained.empty();
+}
+
+
+void ContentProperties::addProperties(
+ const std::vector< OUString > & rProps,
+ const ContentProperties & rContentProps )
+{
+ for ( const OUString & rName : rProps )
+ {
+ if ( !contains( rName ) ) // ignore duplicates
+ {
+ const PropertyValue * pProp = rContentProps.get( rName );
+ if ( pProp )
+ {
+ // Add it.
+ addProperty( rName, pProp->value(), pProp->isCaseSensitive() );
+ }
+ else
+ {
+ addProperty( rName, uno::Any(), false );
+ }
+ }
+ }
+}
+
+
+void ContentProperties::addProperty( const DAVPropertyValue & rProp )
+{
+ addProperty( rProp.Name, rProp.Value, rProp.IsCaseSensitive );
+}
+
+
+void ContentProperties::addProperty( const OUString & rName,
+ const css::uno::Any & rValue,
+ bool bIsCaseSensitive )
+{
+ if ( rName == DAVProperties::CREATIONDATE )
+ {
+ // Map DAV:creationdate to UCP:DateCreated
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString("DateCreated") ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ // else if ( rName.equals( DAVProperties::DISPLAYNAME ) )
+ // {
+ // }
+ // else if ( rName.equals( DAVProperties::GETCONTENTLANGUAGE ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::GETCONTENTLENGTH )
+ {
+ // Map DAV:getcontentlength to UCP:Size
+ OUString aValue;
+ rValue >>= aValue;
+
+ (*m_xProps)[ OUString("Size") ]
+ = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
+ }
+ else if ( rName.equalsIgnoreAsciiCase( "Content-Length" ) )
+ {
+ // Do NOT map Content-length entity header to DAV:getcontentlength!
+ // Only DAV resources have this property.
+
+ // Map Content-Length entity header to UCP:Size
+ OUString aValue;
+ rValue >>= aValue;
+
+ (*m_xProps)[ OUString("Size") ]
+ = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
+ }
+ else if ( rName == DAVProperties::GETCONTENTTYPE )
+ {
+ // Map DAV:getcontenttype to UCP:MediaType (1:1)
+ (*m_xProps)[ OUString("MediaType") ]
+ = PropertyValue( rValue, true );
+ }
+ else if ( rName.equalsIgnoreAsciiCase( "Content-Type" ) )
+ {
+ // Do NOT map Content-Type entity header to DAV:getcontenttype!
+ // Only DAV resources have this property.
+
+ // Map DAV:getcontenttype to UCP:MediaType (1:1)
+ (*m_xProps)[ OUString("MediaType") ]
+ = PropertyValue( rValue, true );
+ }
+ // else if ( rName.equals( DAVProperties::GETETAG ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::GETLASTMODIFIED )
+ {
+ // Map the DAV:getlastmodified entity header to UCP:DateModified
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString("DateModified") ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ else if ( rName.equalsIgnoreAsciiCase( "Last-Modified" ) )
+ {
+ // Do not map Last-Modified entity header to DAV:getlastmodified!
+ // Only DAV resources have this property.
+
+ // Map the Last-Modified entity header to UCP:DateModified
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString("DateModified") ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ // else if ( rName.equals( DAVProperties::LOCKDISCOVERY ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::RESOURCETYPE )
+ {
+ OUString aValue;
+ rValue >>= aValue;
+
+ // Map DAV:resourcetype to UCP:IsFolder, UCP:IsDocument, UCP:ContentType
+ bool bFolder = aValue.equalsIgnoreAsciiCase( "collection" );
+
+ (*m_xProps)[ OUString("IsFolder") ]
+ = PropertyValue( uno::makeAny( bFolder ), true );
+ (*m_xProps)[ OUString("IsDocument") ]
+ = PropertyValue( uno::makeAny( !bFolder ), true );
+ (*m_xProps)[ OUString("ContentType") ]
+ = PropertyValue( uno::makeAny( bFolder
+ ? OUString( WEBDAV_COLLECTION_TYPE )
+ : OUString( WEBDAV_CONTENT_TYPE ) ), true );
+ }
+ // else if ( rName.equals( DAVProperties::SOURCE ) )
+ // {
+ // }
+ // else if ( rName.equals( DAVProperties::SUPPORTEDLOCK ) )
+ // {
+ // }
+
+ // Save property.
+ (*m_xProps)[ rName ] = PropertyValue( rValue, bIsCaseSensitive );
+}
+
+
+// CachableContentProperties Implementation.
+
+
+namespace
+{
+ bool isCachable( OUString const & rName,
+ bool isCaseSensitive )
+ {
+ static const OUString aNonCachableProps [] =
+ {
+ DAVProperties::LOCKDISCOVERY,
+
+ DAVProperties::GETETAG,
+ OUString( "ETag" ),
+
+ OUString( "DateModified" ),
+ OUString( "Last-Modified" ),
+ DAVProperties::GETLASTMODIFIED,
+
+ OUString( "Size" ),
+ OUString( "Content-Length" ),
+ DAVProperties::GETCONTENTLENGTH,
+
+ OUString( "Date" )
+ };
+
+ for (const auto & rNonCachableProp : aNonCachableProps)
+ {
+ if ( isCaseSensitive )
+ {
+ if ( rName == rNonCachableProp )
+ return false;
+ }
+ else
+ if ( rName.equalsIgnoreAsciiCase( rNonCachableProp ) )
+ return false;
+ }
+ return true;
+ }
+
+} // namespace
+
+
+CachableContentProperties::CachableContentProperties(
+ const ContentProperties & rProps )
+{
+ addProperties( rProps );
+}
+
+
+void CachableContentProperties::addProperties(
+ const ContentProperties & rProps )
+{
+ const std::unique_ptr< PropertyValueMap > & props = rProps.getProperties();
+
+ for ( const auto& rProp : *props )
+ {
+ if ( isCachable( rProp.first, rProp.second.isCaseSensitive() ) )
+ m_aProps.addProperty( rProp.first,
+ rProp.second.value(),
+ rProp.second.isCaseSensitive() );
+ }
+}
+
+
+void CachableContentProperties::addProperties(
+ const std::vector< DAVPropertyValue > & rProps )
+{
+ for ( const auto& rProp : rProps )
+ {
+ if ( isCachable( rProp.Name, rProp.IsCaseSensitive ) )
+ m_aProps.addProperty( rProp );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/ContentProperties.hxx b/ucb/source/ucp/webdav-neon/ContentProperties.hxx
new file mode 100644
index 000000000..8f11e8726
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/ContentProperties.hxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#pragma once
+
+#include <config_lgpl.h>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "DAVResource.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+}
+
+namespace webdav_ucp
+{
+
+struct DAVResource;
+
+// PropertyValueMap.
+class PropertyValue
+{
+private:
+ css::uno::Any m_aValue;
+ bool m_bIsCaseSensitive;
+
+public:
+ PropertyValue()
+ : m_bIsCaseSensitive( true ) {}
+
+ PropertyValue( const css::uno::Any & rValue,
+ bool bIsCaseSensitive )
+ : m_aValue( rValue),
+ m_bIsCaseSensitive( bIsCaseSensitive ) {}
+
+ bool isCaseSensitive() const { return m_bIsCaseSensitive; }
+ const css::uno::Any & value() const { return m_aValue; }
+
+};
+
+typedef std::unordered_map< OUString, PropertyValue> PropertyValueMap;
+
+class ContentProperties
+{
+public:
+ ContentProperties();
+
+ explicit ContentProperties( const DAVResource& rResource );
+
+ // Mini props for transient contents.
+ ContentProperties( const OUString & rTitle, bool bFolder );
+
+ // Micro props for non-existing contents.
+ explicit ContentProperties( const OUString & rTitle );
+
+ ContentProperties( const ContentProperties & rOther );
+
+ bool contains( const OUString & rName ) const;
+
+ const css::uno::Any &
+ getValue( const OUString & rName ) const;
+
+ // Maps the UCB property names contained in rProps with their DAV property
+ // counterparts, if possible. All unmappable properties will be included
+ // unchanged in resulting vector.
+ // The vector filled by this method can directly be handed over to
+ // DAVResourceAccess::PROPFIND. The result from PROPFIND
+ // (vector< DAVResource >) can be used to create a ContentProperties
+ // instance which can map DAV properties back to UCB properties.
+ static void UCBNamesToDAVNames( const css::uno::Sequence< css::beans::Property > & rProps,
+ std::vector< OUString > & resources );
+
+ // Maps the UCB property names contained in rProps with their HTTP header
+ // counterparts, if possible. All unmappable properties will be included
+ // unchanged in resulting vector.
+ // The vector filled by this method can directly be handed over to
+ // DAVResourceAccess::HEAD. The result from HEAD (vector< DAVResource >)
+ // can be used to create a ContentProperties instance which can map header
+ // names back to UCB properties.
+ static void UCBNamesToHTTPNames( const css::uno::Sequence< css::beans::Property > & rProps,
+ std::vector< OUString > & resources );
+
+ // return true, if all properties contained in rProps are contained in
+ // this ContentProperties instance. Otherwise, false will be returned.
+ // rNamesNotContained contain the missing names.
+ bool containsAllNames(
+ const css::uno::Sequence< css::beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const;
+
+ // adds all properties described by rProps that are actually contained in
+ // rContentProps to this instance. In case of duplicates the value
+ // already contained in this will left unchanged.
+ void addProperties( const std::vector< OUString > & rProps,
+ const ContentProperties & rContentProps );
+
+ // overwrites probably existing entry.
+ void addProperty( const OUString & rName,
+ const css::uno::Any & rValue,
+ bool bIsCaseSensitive );
+
+ // overwrites probably existing entry.
+ void addProperty( const DAVPropertyValue & rProp );
+
+ bool isTrailingSlash() const { return m_bTrailingSlash; }
+
+ const OUString & getEscapedTitle() const { return m_aEscapedTitle; }
+
+ // Not good to expose implementation details, but this is actually an
+ // internal class.
+ const std::unique_ptr< PropertyValueMap > & getProperties() const
+ { return m_xProps; }
+
+private:
+ OUString m_aEscapedTitle;
+ std::unique_ptr< PropertyValueMap > m_xProps;
+ bool m_bTrailingSlash;
+
+ static css::uno::Any m_aEmptyAny;
+
+ ContentProperties & operator=( const ContentProperties & ) = delete;
+
+ const PropertyValue * get( const OUString & rName ) const;
+};
+
+class CachableContentProperties
+{
+private:
+ ContentProperties m_aProps;
+
+ CachableContentProperties & operator=( const CachableContentProperties & ) = delete;
+ CachableContentProperties( const CachableContentProperties & ) = delete;
+
+public:
+ explicit CachableContentProperties( const ContentProperties & rProps );
+
+ void addProperties( const ContentProperties & rProps );
+
+ void addProperties( const std::vector< DAVPropertyValue > & rProps );
+
+ bool containsAllNames(
+ const css::uno::Sequence< css::beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const
+ { return m_aProps.containsAllNames( rProps, rNamesNotContained ); }
+
+ const css::uno::Any &
+ getValue( const OUString & rName ) const
+ { return m_aProps.getValue( rName ); }
+
+ operator const ContentProperties & () const { return m_aProps; }
+};
+
+} // namespace webdav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVAuthListener.hxx b/ucb/source/ucp/webdav-neon/DAVAuthListener.hxx
new file mode 100644
index 000000000..39ca8f938
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVAuthListener.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVAUTHLISTENER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVAUTHLISTENER_HXX
+
+#include <config_lgpl.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ustring.hxx>
+
+namespace webdav_ucp
+{
+
+class DAVAuthListener : public salhelper::SimpleReferenceObject
+{
+ public:
+ virtual int authenticate(
+ const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials ) = 0;
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVAUTHLISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVAuthListenerImpl.hxx b/ucb/source/ucp/webdav-neon/DAVAuthListenerImpl.hxx
new file mode 100644
index 000000000..a92720b50
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVAuthListenerImpl.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVAUTHLISTENERIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVAUTHLISTENERIMPL_HXX
+
+#include <config_lgpl.h>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "DAVAuthListener.hxx"
+
+
+namespace webdav_ucp
+{
+
+
+
+
+ class DAVAuthListener_Impl : public DAVAuthListener
+ {
+ public:
+
+ DAVAuthListener_Impl(
+ const css::uno::Reference< css::ucb::XCommandEnvironment>& xEnv,
+ const OUString & inURL )
+ : m_xEnv( xEnv ), m_aURL( inURL )
+ {
+ }
+
+ virtual int authenticate( const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials ) override;
+ private:
+
+ const css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv;
+ const OUString m_aURL;
+
+ OUString m_aPrevPassword;
+ OUString m_aPrevUsername;
+ };
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVException.hxx b/ucb/source/ucp/webdav-neon/DAVException.hxx
new file mode 100644
index 000000000..3e1046ce2
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVException.hxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVEXCEPTION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVEXCEPTION_HXX
+
+#include <config_lgpl.h>
+#include <rtl/ustring.hxx>
+
+namespace webdav_ucp
+{
+
+
+// HTTP/WebDAV status codes
+
+
+const sal_uInt16 SC_NONE = 0;
+
+// 1xx (Informational - no errors)
+const sal_uInt16 SC_CONTINUE = 100;
+const sal_uInt16 SC_SWITCHING_PROTOCOLS = 101;
+// DAV extensions
+const sal_uInt16 SC_PROCESSING = 102;
+
+//2xx (Successful - no errors)
+const sal_uInt16 SC_OK = 200;
+const sal_uInt16 SC_CREATED = 201;
+const sal_uInt16 SC_ACCEPTED = 202;
+const sal_uInt16 SC_NON_AUTHORITATIVE_INFORMATION = 203;
+const sal_uInt16 SC_NO_CONTENT = 204;
+const sal_uInt16 SC_RESET_CONTENT = 205;
+const sal_uInt16 SC_PARTIAL_CONTENT = 206;
+// DAV extensions
+const sal_uInt16 SC_MULTISTATUS = 207;
+
+//3xx (Redirection)
+const sal_uInt16 SC_MULTIPLE_CHOICES = 300;
+const sal_uInt16 SC_MOVED_PERMANENTLY = 301;
+const sal_uInt16 SC_MOVED_TEMPORARILY = 302;
+const sal_uInt16 SC_SEE_OTHER = 303;
+const sal_uInt16 SC_NOT_MODIFIED = 304;
+const sal_uInt16 SC_USE_PROXY = 305;
+const sal_uInt16 SC_TEMPORARY_REDIRECT = 307;
+
+//4xx (Client error)
+const sal_uInt16 SC_BAD_REQUEST = 400;
+const sal_uInt16 SC_UNAUTHORIZED = 401;
+const sal_uInt16 SC_PAYMENT_REQUIRED = 402;
+const sal_uInt16 SC_FORBIDDEN = 403;
+const sal_uInt16 SC_NOT_FOUND = 404;
+const sal_uInt16 SC_METHOD_NOT_ALLOWED = 405;
+const sal_uInt16 SC_NOT_ACCEPTABLE = 406;
+const sal_uInt16 SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+const sal_uInt16 SC_REQUEST_TIMEOUT = 408;
+const sal_uInt16 SC_CONFLICT = 409;
+const sal_uInt16 SC_GONE = 410;
+const sal_uInt16 SC_LENGTH_REQUIRED = 411;
+const sal_uInt16 SC_PRECONDITION_FAILED = 412;
+const sal_uInt16 SC_REQUEST_ENTITY_TOO_LARGE = 413;
+const sal_uInt16 SC_REQUEST_URI_TOO_LONG = 414;
+const sal_uInt16 SC_UNSUPPORTED_MEDIA_TYPE = 415;
+const sal_uInt16 SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+const sal_uInt16 SC_EXPECTATION_FAILED = 417;
+// DAV extensions
+const sal_uInt16 SC_UNPROCESSABLE_ENTITY = 422;
+const sal_uInt16 SC_LOCKED = 423;
+const sal_uInt16 SC_FAILED_DEPENDENCY = 424;
+
+//5xx (Server error, general <https://tools.ietf.org/html/rfc7231#section-6.6>)
+const sal_uInt16 SC_INTERNAL_SERVER_ERROR = 500;
+const sal_uInt16 SC_NOT_IMPLEMENTED = 501;
+const sal_uInt16 SC_BAD_GATEWAY = 502;
+const sal_uInt16 SC_SERVICE_UNAVAILABLE = 503;
+const sal_uInt16 SC_GATEWAY_TIMEOUT = 504;
+const sal_uInt16 SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+// DAV extensions (<https://tools.ietf.org/html/rfc4918#section-11>)
+const sal_uInt16 SC_INSUFFICIENT_STORAGE = 507;
+
+// unofficial status codes only used internally by LO
+// used to cache the connection time out event
+const sal_uInt16 USC_CONNECTION_TIMED_OUT = 908;
+ // name resolution failed
+const sal_uInt16 USC_LOOKUP_FAILED = 909;
+const sal_uInt16 USC_AUTH_FAILED = 910;
+const sal_uInt16 USC_AUTHPROXY_FAILED = 911;
+
+
+
+class DAVException : public std::exception
+{
+ public:
+ enum ExceptionCode {
+ DAV_HTTP_ERROR = 0, // Generic error,
+ // mData = server error message,
+ // mStatusCode = HTTP status code
+ DAV_HTTP_LOOKUP, // Name lookup failed,
+ // mData = server[:port]
+ DAV_HTTP_AUTH, // User authentication failed on server,
+ // mData = server[:port]
+ DAV_HTTP_AUTHPROXY, // User authentication failed on proxy,
+ // mData = proxy server[:port]
+ DAV_HTTP_CONNECT, // Could not connect to server,
+ // mData = server[:port]
+ DAV_HTTP_TIMEOUT, // Connection timed out
+ // mData = server[:port]
+ DAV_HTTP_FAILED, // The precondition failed
+ // mData = server[:port]
+ DAV_HTTP_RETRY, // Retry request
+ // mData = server[:port]
+ DAV_HTTP_REDIRECT, // Request was redirected,
+ // mData = new URL
+ DAV_SESSION_CREATE, // session creation error,
+ // mData = server[:port]
+ DAV_INVALID_ARG, // invalid argument
+
+ DAV_LOCK_EXPIRED, // DAV lock expired
+
+ DAV_NOT_LOCKED, // not locked
+
+ DAV_LOCKED_SELF, // locked by this OOo session
+
+ DAV_LOCKED // locked by third party
+ };
+
+ private:
+ ExceptionCode mExceptionCode;
+ OUString mData;
+ sal_uInt16 mStatusCode;
+
+ public:
+ explicit DAVException( ExceptionCode inExceptionCode ) :
+ mExceptionCode( inExceptionCode ), mStatusCode( SC_NONE ) {};
+ DAVException( ExceptionCode inExceptionCode,
+ const OUString & rData ) :
+ mExceptionCode( inExceptionCode ), mData( rData ),
+ mStatusCode( SC_NONE ) {};
+ DAVException( ExceptionCode inExceptionCode,
+ const OUString & rData,
+ sal_uInt16 nStatusCode ) :
+ mExceptionCode( inExceptionCode ), mData( rData ),
+ mStatusCode( nStatusCode ) {};
+
+ const ExceptionCode & getError() const { return mExceptionCode; }
+ const OUString & getData() const { return mData; }
+ sal_uInt16 getStatus() const { return mStatusCode; }
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVEXCEPTION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVProperties.cxx b/ucb/source/ucp/webdav-neon/DAVProperties.cxx
new file mode 100644
index 000000000..5d5634936
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVProperties.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <string.h>
+#include "DAVProperties.hxx"
+
+using namespace webdav_ucp;
+
+const OUString DAVProperties::CREATIONDATE("DAV:creationdate");
+const OUString DAVProperties::DISPLAYNAME("DAV:displayname");
+const OUString DAVProperties::GETCONTENTLANGUAGE("DAV:getcontentlanguage");
+const OUString DAVProperties::GETCONTENTLENGTH("DAV:getcontentlength");
+const OUString DAVProperties::GETCONTENTTYPE("DAV:getcontenttype");
+const OUString DAVProperties::GETETAG("DAV:getetag");
+const OUString DAVProperties::GETLASTMODIFIED("DAV:getlastmodified");
+const OUString DAVProperties::LOCKDISCOVERY("DAV:lockdiscovery");
+const OUString DAVProperties::RESOURCETYPE("DAV:resourcetype");
+const OUString DAVProperties::SOURCE("DAV:source");
+const OUString DAVProperties::SUPPORTEDLOCK("DAV:supportedlock");
+
+const OUString DAVProperties::EXECUTABLE("http://apache.org/dav/props/executable");
+
+void DAVProperties::createNeonPropName( const OUString & rFullName,
+ NeonPropName & rName )
+{
+ if ( rFullName.startsWith( "DAV:" ) )
+ {
+ rName.nspace = "DAV:";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy( RTL_CONSTASCII_LENGTH( "DAV:" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "http://apache.org/dav/props/" ) )
+ {
+ rName.nspace = "http://apache.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://apache.org/dav/props/" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "http://ucb.openoffice.org/dav/props/" ) )
+ {
+ rName.nspace = "http://ucb.openoffice.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://ucb.openoffice.org/dav/props/" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "<prop:" ) )
+ {
+ // Support for 3rd party namespaces/props
+
+ OString aFullName
+ = OUStringToOString( rFullName, RTL_TEXTENCODING_UTF8 );
+
+ // Format: <prop:the_propname xmlns:prop="the_namespace">
+
+ sal_Int32 nStart = RTL_CONSTASCII_LENGTH( "<prop:" );
+ sal_Int32 nLen = aFullName.indexOf( ' ' ) - nStart;
+ rName.name = strdup( aFullName.copy( nStart, nLen ).getStr() );
+
+ nStart = aFullName.indexOf( '=', nStart + nLen ) + 2; // after ="
+ nLen = aFullName.getLength() - RTL_CONSTASCII_LENGTH( "\">" ) - nStart;
+ rName.nspace = strdup( aFullName.copy( nStart, nLen ).getStr() );
+ }
+ else
+ {
+ // Add our namespace to our own properties.
+ rName.nspace = "http://ucb.openoffice.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString( rFullName,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+}
+
+void DAVProperties::createUCBPropName( const char * nspace,
+ const char * name,
+ OUString & rFullName )
+{
+ OUString aNameSpace
+ = OStringToOUString( nspace, RTL_TEXTENCODING_UTF8 );
+ OUString aName
+ = OStringToOUString( name, RTL_TEXTENCODING_UTF8 );
+
+ if ( aNameSpace.isEmpty() )
+ {
+ // Some servers send XML without proper namespaces. Assume "DAV:"
+ // in this case, if name is a well-known dav property name.
+ // Although this is not 100% correct, it solves many problems.
+
+ if ( DAVProperties::RESOURCETYPE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::SUPPORTEDLOCK.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::LOCKDISCOVERY.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::CREATIONDATE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::DISPLAYNAME.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTLANGUAGE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTLENGTH.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTTYPE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETETAG.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETLASTMODIFIED.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::SOURCE.matchIgnoreAsciiCase( aName, 4 ) )
+ aNameSpace = "DAV:";
+ }
+
+ // Note: Concatenating strings BEFORE comparing against known namespaces
+ // is important. See RFC 2815 ( 23.4.2 Meaning of Qualified Names ).
+ rFullName = aNameSpace;
+ rFullName += aName;
+
+ if ( rFullName.startsWith( "DAV:" ) )
+ {
+ // Okay, Just concat strings.
+ }
+ else if ( rFullName.startsWith( "http://apache.org/dav/props/" ) )
+ {
+ // Okay, Just concat strings.
+ }
+ else if ( rFullName.startsWith( "http://ucb.openoffice.org/dav/props/" ) )
+ {
+ // Remove namespace from our own properties.
+ rFullName = rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://ucb.openoffice.org/dav/props/" ) );
+ }
+ else
+ {
+ // Create property name that encodes, namespace and name ( XML ).
+ rFullName = "<prop:" + aName + " xmlns:prop=\"" + aNameSpace + "\">";
+ }
+}
+
+bool DAVProperties::isUCBDeadProperty( const NeonPropName & rName )
+{
+ return ( rName.nspace &&
+ ( rtl_str_compareIgnoreAsciiCase(
+ rName.nspace, "http://ucb.openoffice.org/dav/props/" )
+ == 0 ) );
+}
+
+bool DAVProperties::isUCBSpecialProperty(
+ const OUString& rFullName, OUString& rParsedName)
+{
+ if ( !rFullName.startsWith( "<prop:" ) || !rFullName.endsWith( "\">" ) )
+ return false;
+
+ sal_Int32 nStart = strlen( "<prop:" );
+ sal_Int32 nEnd = rFullName.indexOf( ' ', nStart );
+ if ( nEnd <= nStart ) // incl. -1 for "not found"
+ return false;
+
+ OUString sPropName( rFullName.copy( nStart, nEnd - nStart ) );
+
+ // TODO skip whitespaces?
+ if ( !rFullName.match( "xmlns:prop=\"", ++nEnd ) )
+ return false;
+
+ nStart = nEnd + strlen( "xmlns:prop=\"" );
+ nEnd = rFullName.indexOf( '"', nStart );
+ if ( nEnd != rFullName.getLength() - sal_Int32( strlen( "\">" ) )
+ || nEnd == nStart )
+ {
+ return false;
+ }
+
+ rParsedName = rFullName.copy( nStart, nEnd - nStart );
+ if ( !rParsedName.endsWith( "/" ) )
+ rParsedName += "/";
+ rParsedName += sPropName;
+
+ return rParsedName.getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVProperties.hxx b/ucb/source/ucp/webdav-neon/DAVProperties.hxx
new file mode 100644
index 000000000..2064a83b4
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVProperties.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVPROPERTIES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVPROPERTIES_HXX
+
+#include <config_lgpl.h>
+#include <rtl/ustring.hxx>
+#include "NeonTypes.hxx"
+
+namespace webdav_ucp
+{
+
+struct DAVProperties
+{
+ static const OUString CREATIONDATE;
+ static const OUString DISPLAYNAME;
+ static const OUString GETCONTENTLANGUAGE;
+ static const OUString GETCONTENTLENGTH;
+ static const OUString GETCONTENTTYPE;
+ static const OUString GETETAG;
+ static const OUString GETLASTMODIFIED;
+ static const OUString LOCKDISCOVERY;
+ static const OUString RESOURCETYPE;
+ static const OUString SOURCE;
+ static const OUString SUPPORTEDLOCK;
+ static const OUString EXECUTABLE;
+
+ static void createNeonPropName( const OUString & rFullName,
+ NeonPropName & rName );
+ static void createUCBPropName ( const char * nspace,
+ const char * name,
+ OUString & rFullName );
+
+ static bool isUCBDeadProperty( const NeonPropName & rName );
+ static bool isUCBSpecialProperty( const OUString & rFullName,
+ OUString & rParsedName );
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVPROPERTIES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVRequestEnvironment.hxx b/ucb/source/ucp/webdav-neon/DAVRequestEnvironment.hxx
new file mode 100644
index 000000000..98e974d61
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVRequestEnvironment.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVREQUESTENVIRONMENT_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVREQUESTENVIRONMENT_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "DAVAuthListener.hxx"
+
+namespace webdav_ucp
+{
+ typedef std::pair< OUString, OUString > DAVRequestHeader;
+ typedef std::vector< DAVRequestHeader > DAVRequestHeaders;
+
+struct DAVRequestEnvironment
+{
+ OUString m_aRequestURI;
+ rtl::Reference< DAVAuthListener > m_xAuthListener;
+// rtl::Reference< DAVStatusListener > m_xStatusListener;
+// rtl::Reference< DAVProgressListener > m_xStatusListener;
+ DAVRequestHeaders m_aRequestHeaders;
+ uno::Reference< ucb::XCommandEnvironment > m_xEnv;
+
+DAVRequestEnvironment( const OUString & rRequestURI,
+ const rtl::Reference< DAVAuthListener > & xListener,
+ const DAVRequestHeaders & rRequestHeaders,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv)
+ : m_aRequestURI( rRequestURI ),
+ m_xAuthListener( xListener ),
+ m_aRequestHeaders( rRequestHeaders ),
+ m_xEnv( xEnv ){}
+
+ DAVRequestEnvironment() {}
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVREQUESTENVIRONMENT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVResource.hxx b/ucb/source/ucp/webdav-neon/DAVResource.hxx
new file mode 100644
index 000000000..219b06444
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVResource.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCE_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace webdav_ucp
+{
+
+struct DAVPropertyValue
+{
+ OUString Name;
+ css::uno::Any Value;
+ bool IsCaseSensitive;
+
+ DAVPropertyValue() : IsCaseSensitive( true ) {}
+};
+
+struct DAVResource
+{
+ OUString uri;
+ std::vector< DAVPropertyValue > properties;
+
+ DAVResource() {}
+ explicit DAVResource( const OUString & inUri ) : uri( inUri ) {}
+};
+
+struct DAVResourceInfo
+{
+ std::vector < OUString > properties;
+
+ bool operator==( const struct DAVResourceInfo& a ) const
+ {
+ return (properties == a.properties );
+ }
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx b/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx
new file mode 100644
index 000000000..f0576e049
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx
@@ -0,0 +1,1192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/ucb/XWebDAVCommandEnvironment.hpp>
+
+#include <ucbhelper/simpleauthenticationrequest.hxx>
+#include <comphelper/seekableinput.hxx>
+
+#include "DAVAuthListenerImpl.hxx"
+#include "DAVResourceAccess.hxx"
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+
+// DAVAuthListener_Impl Implementation.
+
+static constexpr sal_uInt32 g_nRedirectLimit = 5;
+
+// virtual
+int DAVAuthListener_Impl::authenticate(
+ const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials )
+{
+ if ( m_xEnv.is() )
+ {
+ uno::Reference< task::XInteractionHandler > xIH
+ = m_xEnv->getInteractionHandler();
+
+ if ( xIH.is() )
+ {
+ // #102871# - Supply username and password from previous try.
+ // Password container service depends on this!
+ if ( inoutUserName.isEmpty() )
+ inoutUserName = m_aPrevUsername;
+
+ if ( outPassWord.isEmpty() )
+ outPassWord = m_aPrevPassword;
+
+ rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest
+ = new ucbhelper::SimpleAuthenticationRequest(
+ m_aURL, inHostName, inRealm, inoutUserName,
+ outPassWord, bCanUseSystemCredentials );
+ xIH->handle( xRequest.get() );
+
+ rtl::Reference< ucbhelper::InteractionContinuation > xSelection
+ = xRequest->getSelection();
+
+ if ( xSelection.is() )
+ {
+ // Handler handled the request.
+ uno::Reference< task::XInteractionAbort > xAbort(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( !xAbort.is() )
+ {
+ const rtl::Reference<
+ ucbhelper::InteractionSupplyAuthentication > & xSupp
+ = xRequest->getAuthenticationSupplier();
+
+ bool bUseSystemCredentials = false;
+
+ if ( bCanUseSystemCredentials )
+ bUseSystemCredentials
+ = xSupp->getUseSystemCredentials();
+
+ if ( bUseSystemCredentials )
+ {
+ // This is the (strange) way to tell neon to use
+ // system credentials.
+ inoutUserName.clear();
+ outPassWord.clear();
+ }
+ else
+ {
+ inoutUserName = xSupp->getUserName();
+ outPassWord = xSupp->getPassword();
+ }
+
+ // #102871# - Remember username and password.
+ m_aPrevUsername = inoutUserName;
+ m_aPrevPassword = outPassWord;
+
+ // go on.
+ return 0;
+ }
+ }
+ }
+ }
+ // Abort.
+ return -1;
+}
+
+
+// DAVResourceAccess Implementation.
+
+
+DAVResourceAccess::DAVResourceAccess(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ const OUString & rURL )
+: m_aURL( rURL ),
+ m_xSessionFactory( rSessionFactory ),
+ m_xContext( rxContext )
+{
+}
+
+
+DAVResourceAccess::DAVResourceAccess( const DAVResourceAccess & rOther )
+: m_aURL( rOther.m_aURL ),
+ m_aPath( rOther.m_aPath ),
+ m_aFlags( rOther.m_aFlags ),
+ m_xSession( rOther.m_xSession ),
+ m_xSessionFactory( rOther.m_xSessionFactory ),
+ m_xContext( rOther.m_xContext ),
+ m_aRedirectURIs( rOther.m_aRedirectURIs )
+{
+}
+
+
+DAVResourceAccess & DAVResourceAccess::operator=(
+ const DAVResourceAccess & rOther )
+{
+ m_aURL = rOther.m_aURL;
+ m_aPath = rOther.m_aPath;
+ m_aFlags = rOther.m_aFlags;
+ m_xSession = rOther.m_xSession;
+ m_xSessionFactory = rOther.m_xSessionFactory;
+ m_xContext = rOther.m_xContext;
+ m_aRedirectURIs = rOther.m_aRedirectURIs;
+
+ return *this;
+}
+
+void DAVResourceAccess::OPTIONS(
+ DAVOptions & rOptions,
+ const css::uno::Reference<
+ css::ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ css::ucb::WebDAVHTTPMethod_OPTIONS,
+ aHeaders );
+
+ m_xSession->OPTIONS( getRequestURI(),
+ rOptions,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+void DAVResourceAccess::PROPFIND(
+ const Depth nDepth,
+ const std::vector< OUString > & rPropertyNames,
+ std::vector< DAVResource > & rResources,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPFIND,
+ aHeaders );
+
+ m_xSession->PROPFIND( getRequestURI(),
+ nDepth,
+ rPropertyNames,
+ rResources,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::PROPFIND(
+ const Depth nDepth,
+ std::vector< DAVResourceInfo > & rResInfo,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPFIND,
+ aHeaders );
+
+ m_xSession->PROPFIND( getRequestURI(),
+ nDepth,
+ rResInfo,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) ) ;
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::PROPPATCH(
+ const std::vector< ProppatchValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPPATCH,
+ aHeaders );
+
+ m_xSession->PROPPATCH( getRequestURI(),
+ rValues,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::HEAD(
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_HEAD,
+ aHeaders );
+
+ m_xSession->HEAD( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::GET(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ xStream = m_xSession->GET( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+void DAVResourceAccess::GET(
+ uno::Reference< io::XOutputStream > & rStream,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ m_xSession->GET( getRequestURI(),
+ rStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+uno::Reference< io::XInputStream > DAVResourceAccess::GET(
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ xStream = m_xSession->GET( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+// used as HEAD substitute when HEAD is not implemented on server
+void DAVResourceAccess::GET0(
+ DAVRequestHeaders &rRequestHeaders,
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ rRequestHeaders );
+
+ m_xSession->GET0( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ rRequestHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::GET(
+ uno::Reference< io::XOutputStream > & rStream,
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ bool bRetry;
+ int errorCount = 0;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ m_xSession->GET( getRequestURI(),
+ rStream,
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::abort()
+{
+ initialize();
+ m_xSession->abort();
+}
+
+
+namespace {
+
+ /// @throws DAVException
+ void resetInputStream( const uno::Reference< io::XInputStream > & rStream )
+ {
+ try
+ {
+ uno::Reference< io::XSeekable > xSeekable(
+ rStream, uno::UNO_QUERY );
+ if ( xSeekable.is() )
+ {
+ xSeekable->seek( 0 );
+ return;
+ }
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ }
+ catch ( io::IOException const & )
+ {
+ }
+
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+} // namespace
+
+
+void DAVResourceAccess::PUT(
+ const uno::Reference< io::XInputStream > & rStream,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rStream, m_xContext );
+
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ resetInputStream( xSeekableStream );
+
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PUT,
+ aHeaders );
+
+ m_xSession->PUT( getRequestURI(),
+ xSeekableStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::POST(
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & rInputStream,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rInputStream, m_xContext );
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ {
+ resetInputStream( xSeekableStream );
+ bRetry = false;
+ }
+
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_POST,
+ aHeaders );
+
+ xStream = m_xSession->POST( getRequestURI(),
+ rContentType,
+ rReferer,
+ xSeekableStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+
+ if ( e.getError() == DAVException::DAV_HTTP_REDIRECT )
+ {
+ // #i74980# - Upon POST redirect, do a GET.
+ return GET( xEnv );
+ }
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+void DAVResourceAccess::POST(
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & rInputStream,
+ uno::Reference< io::XOutputStream > & rOutputStream,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rInputStream, m_xContext );
+
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ {
+ resetInputStream( xSeekableStream );
+ bRetry = false;
+ }
+
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_POST,
+ aHeaders );
+
+ m_xSession->POST( getRequestURI(),
+ rContentType,
+ rReferer,
+ xSeekableStream,
+ rOutputStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+
+ if ( e.getError() == DAVException::DAV_HTTP_REDIRECT )
+ {
+ // #i74980# - Upon POST redirect, do a GET.
+ GET( rOutputStream, xEnv );
+ return;
+ }
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::MKCOL(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_MKCOL,
+ aHeaders );
+
+ m_xSession->MKCOL( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::COPY(
+ const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_COPY,
+ aHeaders );
+
+ m_xSession->COPY( rSourcePath,
+ rDestinationURI,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ),
+ bOverwrite );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::MOVE(
+ const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_MOVE,
+ aHeaders );
+
+ m_xSession->MOVE( rSourcePath,
+ rDestinationURI,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ),
+ bOverwrite );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::DESTROY(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_DELETE,
+ aHeaders );
+
+ m_xSession->DESTROY( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+// set new lock.
+void DAVResourceAccess::LOCK(
+ ucb::Lock & inLock,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_LOCK,
+ aHeaders );
+
+ m_xSession->LOCK( getRequestURI(),
+ inLock,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+void DAVResourceAccess::UNLOCK(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_UNLOCK,
+ aHeaders );
+
+ m_xSession->UNLOCK( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( const DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::setFlags( const uno::Sequence< beans::NamedValue >& rFlags )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_aFlags = rFlags;
+}
+
+
+void DAVResourceAccess::setURL( const OUString & rNewURL )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_aURL = rNewURL;
+ m_aPath.clear(); // Next initialize() will create new session.
+}
+
+
+// init dav session and path
+void DAVResourceAccess::initialize()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ if ( m_aPath.isEmpty() )
+ {
+ NeonUri aURI( m_aURL );
+ const OUString& aPath( aURI.GetPath() );
+
+ /* #134089# - Check URI */
+ if ( aPath.isEmpty() )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ /* #134089# - Check URI */
+ if ( aURI.GetHost().isEmpty() )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ if ( !m_xSession.is() || !m_xSession->CanUse( m_aURL, m_aFlags ) )
+ {
+ m_xSession.clear();
+
+ // create new webdav session
+ m_xSession
+ = m_xSessionFactory->createDAVSession( m_aURL, m_aFlags, m_xContext );
+
+ if ( !m_xSession.is() )
+ return;
+ }
+
+ // Own URI is needed to redirect cycle detection.
+ m_aRedirectURIs.push_back( aURI );
+
+ // Success.
+ m_aPath = aPath;
+
+ // Not only the path has to be encoded
+ m_aURL = aURI.GetURI();
+ }
+}
+
+
+const OUString & DAVResourceAccess::getRequestURI() const
+{
+ assert( m_xSession.is() && "DAVResourceAccess::getRequestURI - Not initialized!" );
+
+ // In case a proxy is used we have to use the absolute URI for a request.
+ if ( m_xSession->UsesProxy() )
+ return m_aURL;
+
+ return m_aPath;
+}
+
+
+// static
+void DAVResourceAccess::getUserRequestHeaders(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv,
+ const OUString & rURI,
+ ucb::WebDAVHTTPMethod eMethod,
+ DAVRequestHeaders & rRequestHeaders )
+{
+ if ( xEnv.is() )
+ {
+ uno::Reference< ucb::XWebDAVCommandEnvironment > xDAVEnv(
+ xEnv, uno::UNO_QUERY );
+
+ if ( xDAVEnv.is() )
+ {
+ const uno::Sequence< beans::StringPair > aRequestHeaders
+ = xDAVEnv->getUserRequestHeaders( rURI, eMethod );
+
+ for ( const auto& rRequestHeader : aRequestHeaders )
+ {
+ rRequestHeaders.emplace_back(
+ rRequestHeader.First,
+ rRequestHeader.Second );
+ }
+ }
+ }
+
+ // Make sure a User-Agent header is always included, as at least
+ // en.wikipedia.org:80 forces back 403 "Scripts should use an informative
+ // User-Agent string with contact information, or they may be IP-blocked
+ // without notice" otherwise:
+ if ( std::any_of(rRequestHeaders.begin(), rRequestHeaders.end(),
+ [](const DAVRequestHeader& rHeader) { return rHeader.first.equalsIgnoreAsciiCase( "User-Agent" ); }) )
+ {
+ return;
+ }
+ rRequestHeaders.emplace_back( "User-Agent", "LibreOffice" );
+}
+
+// This function member implements the control on cyclical redirections
+bool DAVResourceAccess::detectRedirectCycle(
+ const OUString& rRedirectURL )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ NeonUri aUri( rRedirectURL );
+
+ // Check for maximum number of redirections
+ // according to <https://tools.ietf.org/html/rfc7231#section-6.4>.
+ // A practical limit may be 5, due to earlier specifications:
+ // <https://tools.ietf.org/html/rfc2068#section-10.3>
+ // it can be raised keeping in mind the added net activity.
+ if( static_cast< size_t >( g_nRedirectLimit ) <= m_aRedirectURIs.size() )
+ return true;
+
+ // try to detect a cyclical redirection
+ return std::any_of(m_aRedirectURIs.begin(), m_aRedirectURIs.end(),
+ [&aUri](const NeonUri& rUri) {
+ // if equal, cyclical redirection detected
+ return aUri == rUri; });
+}
+
+
+void DAVResourceAccess::resetUri()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ if ( !m_aRedirectURIs.empty() )
+ {
+ std::vector< NeonUri >::const_iterator it = m_aRedirectURIs.begin();
+
+ NeonUri aUri( *it );
+ m_aRedirectURIs.clear();
+ setURL ( aUri.GetURI() );
+ initialize();
+ }
+}
+
+
+bool DAVResourceAccess::handleException( const DAVException & e, int errorCount )
+{
+ switch ( e.getError() )
+ {
+ case DAVException::DAV_HTTP_REDIRECT:
+ if ( !detectRedirectCycle( e.getData() ) )
+ {
+ // set new URL and path.
+ setURL( e.getData() );
+ initialize();
+ return true;
+ }
+ return false;
+ // #67048# copy & paste images doesn't display. This bug refers
+ // to an old OOo problem about getting resources from sites with a bad connection.
+ // If we have a bad connection try again. Up to three times.
+ case DAVException::DAV_HTTP_ERROR:
+ // retry up to three times, if not a client-side error (4xx error codes)
+ if ( e.getStatus() < SC_BAD_REQUEST && errorCount < 3 )
+ return true;
+ // check the server side errors
+ switch( e.getStatus() )
+ {
+ // the HTTP server side response status codes that can be retried
+ case SC_BAD_GATEWAY: // retry, can be an excessive load
+ case SC_GATEWAY_TIMEOUT: // retry, may be we get lucky
+ case SC_SERVICE_UNAVAILABLE: // retry, the service may become available
+ case SC_INSUFFICIENT_STORAGE: // space may be freed, retry
+ {
+ return errorCount < 3;
+ }
+ break;
+ // all the other HTTP server response status codes are NOT retry
+ default:
+ return false;
+ }
+ break;
+ // if connection has said retry then retry!
+ case DAVException::DAV_HTTP_RETRY:
+ return true;
+ default:
+ return false; // Abort
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx b/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx
new file mode 100644
index 000000000..30ec5256f
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCEACCESS_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCEACCESS_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/ucb/WebDAVHTTPMethod.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "DAVAuthListener.hxx"
+#include "DAVException.hxx"
+#include "DAVSession.hxx"
+#include "DAVResource.hxx"
+#include "DAVTypes.hxx"
+#include "NeonUri.hxx"
+
+namespace webdav_ucp
+{
+
+class DAVSessionFactory;
+
+class DAVResourceAccess
+{
+ osl::Mutex m_aMutex;
+ OUString m_aURL;
+ OUString m_aPath;
+ css::uno::Sequence< css::beans::NamedValue > m_aFlags;
+ rtl::Reference< DAVSession > m_xSession;
+ rtl::Reference< DAVSessionFactory > m_xSessionFactory;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ std::vector< NeonUri > m_aRedirectURIs;
+
+public:
+ DAVResourceAccess( const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ const OUString & rURL );
+ DAVResourceAccess( const DAVResourceAccess & rOther );
+
+ DAVResourceAccess & operator=( const DAVResourceAccess & rOther );
+
+ /// @throws DAVException
+ void setFlags( const css::uno::Sequence< css::beans::NamedValue >& rFlags );
+
+ /// @throws DAVException
+ void setURL( const OUString & rNewURL );
+
+ void resetUri();
+
+ const OUString & getURL() const { return m_aURL; }
+
+ const rtl::Reference< DAVSessionFactory >& getSessionFactory() const
+ { return m_xSessionFactory; }
+
+ // DAV methods
+
+ /// @throws DAVException
+ void
+ OPTIONS( DAVOptions & rOptions,
+ const css::uno::Reference<
+ css::ucb::XCommandEnvironment > & xEnv );
+
+ // allprop & named
+ /// @throws DAVException
+ void
+ PROPFIND( const Depth nDepth,
+ const std::vector< OUString > & rPropertyNames,
+ std::vector< DAVResource > & rResources,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // propnames
+ /// @throws DAVException
+ void
+ PROPFIND( const Depth nDepth,
+ std::vector< DAVResourceInfo > & rResInfo,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ PROPPATCH( const std::vector< ProppatchValue > & rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ void
+ HEAD( const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ GET( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ GET( css::uno::Reference< css::io::XOutputStream > & rStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::RuntimeException
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ GET( const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // used as HEAD substitute when HEAD is not implemented on server
+ /// @throws DAVException
+ void
+ GET0( DAVRequestHeaders & rRequestHeaders,
+ const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ GET( css::uno::Reference< css::io::XOutputStream > & rStream,
+ const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::RuntimeException
+ /// @throws DAVException
+ void
+ PUT( const css::uno::Reference< css::io::XInputStream > & rStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::RuntimeException
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & rInputStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::RuntimeException
+ /// @throws DAVException
+ void
+ POST( const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & rInputStream,
+ css::uno::Reference< css::io::XOutputStream > & rOutputStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ void
+ MKCOL( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ COPY( const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ MOVE( const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ DESTROY( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // set new lock.
+ /// @throws DAVException
+ void
+ LOCK( css::ucb::Lock & inLock,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ UNLOCK( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ abort();
+
+ // helper
+ static void
+ getUserRequestHeaders(
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv,
+ const OUString & rURI,
+ css::ucb::WebDAVHTTPMethod eMethod,
+ DAVRequestHeaders & rRequestHeaders );
+
+ /// @throws DAVException
+ bool handleException( const DAVException & e, int errorCount );
+
+private:
+ const OUString & getRequestURI() const;
+ /// @throws DAVException
+ bool detectRedirectCycle( const OUString& rRedirectURL );
+ /// @throws DAVException
+ void initialize();
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVRESOURCEACCESS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVSession.hxx b/ucb/source/ucp/webdav-neon/DAVSession.hxx
new file mode 100644
index 000000000..72eaf39bd
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVSession.hxx
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSION_HXX
+
+#include <config_lgpl.h>
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include "DAVProperties.hxx"
+#include "DAVResource.hxx"
+#include "DAVSessionFactory.hxx"
+#include "DAVTypes.hxx"
+#include "DAVRequestEnvironment.hxx"
+
+namespace com::sun::star::beans {
+ struct NamedValue;
+}
+
+namespace com::sun::star::ucb {
+ struct Lock;
+}
+
+namespace webdav_ucp
+{
+
+class DAVSession
+{
+public:
+ void acquire()
+ {
+ osl_atomic_increment( &m_nRefCount );
+ }
+
+ void release()
+ {
+ if ( osl_atomic_decrement( &m_nRefCount ) == 0 )
+ {
+ m_xFactory->releaseElement( this );
+ delete this;
+ }
+ }
+
+ virtual bool CanUse( const OUString & inPath,
+ const css::uno::Sequence< css::beans::NamedValue >& rFlags ) = 0;
+
+ virtual bool UsesProxy() = 0;
+
+ // DAV methods
+
+ /// @throws std::exception
+ virtual void OPTIONS( const OUString & inPath,
+ DAVOptions& rOptions,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // allprop & named
+ /// @throws std::exception
+ virtual void PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropertyNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // propnames
+ /// @throws std::exception
+ virtual void PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream >& o,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // used as HEAD substitute when HEAD is not implemented on server
+ /// @throws std::exception
+ virtual void
+ GET0( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream >& o,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void PUT( const OUString & inPath,
+ const css::uno::Reference< css::io::XInputStream >& s,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ css::uno::Reference< css::io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void COPY( const OUString & inSource,
+ const OUString & inDestination,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverwrite ) = 0;
+
+ /// @throws std::exception
+ virtual void MOVE( const OUString & inSource,
+ const OUString & inDestination,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverwrite ) = 0;
+
+ /// @throws std::exception
+ virtual void DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // set new lock.
+ /// @throws std::exception
+ virtual void LOCK( const OUString & inPath,
+ css::ucb::Lock & inLock,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void UNLOCK( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws std::exception
+ virtual void abort() = 0;
+
+protected:
+ rtl::Reference< DAVSessionFactory > m_xFactory;
+
+ explicit DAVSession( rtl::Reference< DAVSessionFactory > const & rFactory )
+ : m_xFactory( rFactory ), m_nRefCount( 0 ) {}
+
+ virtual ~DAVSession() {}
+
+private:
+ DAVSessionFactory::Map::iterator m_aContainerIt;
+ oslInterlockedCount m_nRefCount;
+
+ friend class DAVSessionFactory;
+ friend struct std::default_delete< DAVSession >;
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVSessionFactory.cxx b/ucb/source/ucp/webdav-neon/DAVSessionFactory.cxx
new file mode 100644
index 000000000..fd6fca081
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVSessionFactory.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <memory>
+#include "DAVSessionFactory.hxx"
+#include "NeonSession.hxx"
+#include "NeonUri.hxx"
+#include <osl/diagnose.h>
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+DAVSessionFactory::~DAVSessionFactory()
+{
+}
+
+rtl::Reference< DAVSession > DAVSessionFactory::createDAVSession(
+ const OUString & inUri,
+ const uno::Sequence< beans::NamedValue >& rFlags,
+ const uno::Reference< uno::XComponentContext > & rxContext )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ m_xContext = rxContext;
+
+ if (!m_xProxyDecider)
+ m_xProxyDecider.reset( new ucbhelper::InternetProxyDecider( rxContext ) );
+
+ Map::iterator aIt = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&inUri, &rFlags](const Map::value_type& rEntry) { return rEntry.second->CanUse( inUri, rFlags ); });
+
+ if ( aIt == m_aMap.end() )
+ {
+ NeonUri aURI( inUri );
+
+ std::unique_ptr<DAVSession> xElement(
+ new NeonSession(this, inUri, rFlags, *m_xProxyDecider));
+
+ aIt = m_aMap.emplace( inUri, xElement.get() ).first;
+ aIt->second->m_aContainerIt = aIt;
+ xElement.release();
+ return aIt->second;
+ }
+ else if ( osl_atomic_increment( &aIt->second->m_nRefCount ) > 1 )
+ {
+ rtl::Reference< DAVSession > xElement( aIt->second );
+ osl_atomic_decrement( &aIt->second->m_nRefCount );
+ return xElement;
+ }
+ else
+ {
+ osl_atomic_decrement( &aIt->second->m_nRefCount );
+ aIt->second->m_aContainerIt = m_aMap.end();
+
+ // If URL scheme is different from http or https we definitely
+ // have to use a proxy and therefore can optimize the getProxy
+ // call a little:
+ NeonUri aURI( inUri );
+
+ aIt->second = new NeonSession(this, inUri, rFlags, *m_xProxyDecider);
+ aIt->second->m_aContainerIt = aIt;
+ return aIt->second;
+ }
+}
+
+void DAVSessionFactory::releaseElement( DAVSession const * pElement )
+{
+ OSL_ASSERT( pElement );
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( pElement->m_aContainerIt != m_aMap.end() )
+ m_aMap.erase( pElement->m_aContainerIt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVSessionFactory.hxx b/ucb/source/ucp/webdav-neon/DAVSessionFactory.hxx
new file mode 100644
index 000000000..d74e05aeb
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVSessionFactory.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSIONFACTORY_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSIONFACTORY_HXX
+
+#ifdef min
+#undef min // GNU libstdc++ <memory> includes <limit> which defines methods called min...
+#endif
+#include <config_lgpl.h>
+#include <map>
+#include <memory>
+#include <osl/mutex.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ref.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <ucbhelper/proxydecider.hxx>
+
+using namespace com::sun::star;
+
+namespace com::sun::star::beans {
+ struct NamedValue;
+}
+
+namespace com::sun::star::lang {
+ class XMultiServiceFactory;
+}
+
+namespace webdav_ucp
+{
+
+class DAVSession;
+
+class DAVSessionFactory : public salhelper::SimpleReferenceObject
+{
+public:
+ virtual ~DAVSessionFactory() override;
+
+ /// @throws DAVException
+ rtl::Reference< DAVSession >
+ createDAVSession( const OUString & inUri,
+ const ::uno::Sequence< css::beans::NamedValue >& rFlags,
+ const ::uno::Reference< ::uno::XComponentContext >& rxContext );
+
+ const ::uno::Reference< ::uno::XComponentContext >& getComponentContext() const { return m_xContext; }
+private:
+ typedef std::map< OUString, DAVSession * > Map;
+
+ Map m_aMap;
+ osl::Mutex m_aMutex;
+ std::unique_ptr< ucbhelper::InternetProxyDecider > m_xProxyDecider;
+
+ ::uno::Reference< ::uno::XComponentContext > m_xContext;
+
+ void releaseElement( DAVSession const * pElement );
+
+ friend class DAVSession;
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVSESSIONFACTORY_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DAVTypes.cxx b/ucb/source/ucp/webdav-neon/DAVTypes.cxx
new file mode 100644
index 000000000..6cf4a01b3
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVTypes.cxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+#include <osl/time.h>
+
+#include "DAVTypes.hxx"
+#include "../inc/urihelper.hxx"
+#include "NeonUri.hxx"
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+// DAVOptions implementation
+
+DAVOptions::DAVOptions() :
+ m_isClass1( false ),
+ m_isClass2( false ),
+ m_isClass3( false ),
+ m_isHeadAllowed( true ),
+ m_isLocked( false ),
+ m_aAllowedMethods(),
+ m_nStaleTime( 0 ),
+ m_nRequestedTimeLife( 0 ),
+ m_sURL(),
+ m_sRedirectedURL(),
+ m_nHttpResponseStatusCode( 0 ),
+ m_sHttpResponseStatusText()
+{
+}
+
+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 ),
+ m_nRequestedTimeLife( rOther.m_nRequestedTimeLife ),
+ m_sURL( rOther.m_sURL ),
+ m_sRedirectedURL( rOther.m_sRedirectedURL),
+ m_nHttpResponseStatusCode( rOther.m_nHttpResponseStatusCode ),
+ m_sHttpResponseStatusText( rOther.m_sHttpResponseStatusText )
+{
+}
+
+DAVOptions::~DAVOptions()
+{
+}
+
+DAVOptions & DAVOptions::operator=( const DAVOptions& rOpts )
+{
+ m_isClass1 = rOpts.m_isClass1;
+ m_isClass2 = rOpts.m_isClass2;
+ m_isClass3 = rOpts.m_isClass3;
+ m_isLocked = rOpts.m_isLocked;
+ m_isHeadAllowed = rOpts.m_isHeadAllowed;
+ m_aAllowedMethods = rOpts.m_aAllowedMethods;
+ m_nStaleTime = rOpts.m_nStaleTime;
+ m_nRequestedTimeLife = rOpts.m_nRequestedTimeLife;
+ m_sURL = rOpts.m_sURL;
+ m_sRedirectedURL = rOpts.m_sRedirectedURL;
+ m_nHttpResponseStatusCode = rOpts.m_nHttpResponseStatusCode;
+ m_sHttpResponseStatusText = rOpts.m_sHttpResponseStatusText;
+ return *this;
+}
+
+bool DAVOptions::operator==( const DAVOptions& rOpts ) const
+{
+ return
+ m_isClass1 == rOpts.m_isClass1 &&
+ m_isClass2 == rOpts.m_isClass2 &&
+ m_isClass3 == rOpts.m_isClass3 &&
+ m_isLocked == rOpts.m_isLocked &&
+ m_isHeadAllowed == rOpts.m_isHeadAllowed &&
+ m_aAllowedMethods == rOpts.m_aAllowedMethods &&
+ m_nStaleTime == rOpts.m_nStaleTime &&
+ m_nRequestedTimeLife == rOpts.m_nRequestedTimeLife &&
+ m_sURL == rOpts.m_sURL &&
+ m_sRedirectedURL == rOpts.m_sRedirectedURL &&
+ m_nHttpResponseStatusCode == rOpts.m_nHttpResponseStatusCode &&
+ m_sHttpResponseStatusText == rOpts.m_sHttpResponseStatusText;
+}
+
+
+// DAVOptionsCache implementation
+
+DAVOptionsCache::DAVOptionsCache()
+{
+}
+
+DAVOptionsCache::~DAVOptionsCache()
+{
+}
+
+bool DAVOptionsCache::getDAVOptions( const OUString & rURL, DAVOptions & rDAVOptions )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( NeonUri::unescape( rURL ) ) );
+ normalizeURLLastChar( aEncodedUrl );
+
+ // search the URL in the static map
+ DAVOptionsMap::iterator it = m_aTheCache.find( aEncodedUrl );
+ if ( it == m_aTheCache.end() )
+ return false;
+ else
+ {
+ // check if the capabilities are stale, before restoring
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ if ( (*it).second.getStaleTime() < t1.Seconds )
+ {
+ // if stale, remove from cache, do not restore
+ m_aTheCache.erase( it );
+ return false;
+ // return false instead
+ }
+ rDAVOptions = (*it).second;
+ return true;
+ }
+}
+
+void DAVOptionsCache::removeDAVOptions( const OUString & rURL )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( NeonUri::unescape( rURL ) ) );
+ normalizeURLLastChar( aEncodedUrl );
+
+ DAVOptionsMap::iterator it = m_aTheCache.find( aEncodedUrl );
+ if ( it != m_aTheCache.end() )
+ {
+ m_aTheCache.erase( it );
+ }
+}
+
+void DAVOptionsCache::addDAVOptions( DAVOptions & rDAVOptions, const sal_uInt32 nLifeTime )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aURL( rDAVOptions.getURL() );
+
+ OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( NeonUri::unescape( aURL ) ) );
+ normalizeURLLastChar( aEncodedUrl );
+ rDAVOptions.setURL( aEncodedUrl );
+
+// unchanged, it may be used to access a server
+ OUString aRedirURL( rDAVOptions.getRedirectedURL() );
+ rDAVOptions.setRedirectedURL( aRedirURL );
+
+ // check if already cached
+ DAVOptionsMap::iterator it = m_aTheCache.find( aEncodedUrl );
+ if ( it != m_aTheCache.end() )
+ { // already in cache, check LifeTime
+ if ( (*it).second.getRequestedTimeLife() == nLifeTime )
+ return; // same lifetime, do nothing
+ }
+ // not in cache, add it
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ rDAVOptions.setStaleTime( t1.Seconds + nLifeTime );
+
+ m_aTheCache[ aEncodedUrl ] = rDAVOptions;
+}
+
+void DAVOptionsCache::setHeadAllowed( const OUString & rURL, const bool HeadAllowed )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( NeonUri::unescape( rURL ) ) );
+ normalizeURLLastChar( aEncodedUrl );
+
+ DAVOptionsMap::iterator 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;
+ }
+ // check if the resource was present on server
+ (*it).second.setHeadAllowed( HeadAllowed );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/ucb/source/ucp/webdav-neon/DAVTypes.hxx b/ucb/source/ucp/webdav-neon/DAVTypes.hxx
new file mode 100644
index 000000000..0f6a34a3d
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DAVTypes.hxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVTYPES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVTYPES_HXX
+
+#include <config_lgpl.h>
+#include <map>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace webdav_ucp
+{
+/* Excerpt from RFC 4918
+ <https://tools.ietf.org/html/rfc4918#section-18>
+
+ 18.1 Class 1
+
+ A class 1 compliant resource MUST meet all "MUST" requirements in all
+ sections of this document.
+
+ Class 1 compliant resources MUST return, at minimum, the value "1" in
+ the DAV header on all responses to the OPTIONS method.
+
+ 18.2 Class 2
+
+ A class 2 compliant resource MUST meet all class 1 requirements and
+ support the LOCK method, the DAV:supportedlock property, the DAV:
+ lockdiscovery property, the Time-Out response header and the Lock-
+ Token request header. A class 2 compliant resource SHOULD also
+ support the Timeout request header and the 'owner' XML element.
+
+ Class 2 compliant resources MUST return, at minimum, the values "1"
+ and "2" in the DAV header on all responses to the OPTIONS method.
+
+ 18.3. Class 3
+
+ A resource can explicitly advertise its support for the revisions to
+ [RFC2518] made in this document. Class 1 MUST be supported as well.
+ Class 2 MAY be supported. Advertising class 3 support in addition to
+ class 1 and 2 means that the server supports all the requirements in
+ this specification. Advertising class 3 and class 1 support, but not
+ class 2, means that the server supports all the requirements in this
+ specification except possibly those that involve locking support.
+
+*/
+
+ class DAVOptions final
+ {
+ private:
+ bool m_isClass1;
+ bool m_isClass2;
+ bool m_isClass3;
+ /// for server that do not implement it
+ bool m_isHeadAllowed;
+ /// Internally used to maintain the locked state of the resource, only if it's a Class 2 resource
+ bool m_isLocked;
+ /// contains the methods allowed on this resource
+ OUString m_aAllowedMethods;
+
+ /// target time when this capability becomes stale
+ sal_uInt32 m_nStaleTime;
+ sal_uInt32 m_nRequestedTimeLife;
+ OUString m_sURL;
+ OUString m_sRedirectedURL;
+
+ /// The cached HTT response status code. It's 0 if the code was dealt with and there is no need to cache it
+ sal_uInt16 m_nHttpResponseStatusCode;
+ /// The cached string with the server returned HTTP response status code string, corresponds to m_nHttpResponseStatusCode.
+ OUString m_sHttpResponseStatusText;
+
+ public:
+ DAVOptions();
+
+ DAVOptions( const DAVOptions & rOther );
+
+ ~DAVOptions();
+
+ bool isClass1() const { return m_isClass1; };
+ void setClass1( bool Class1 = true ) { m_isClass1 = Class1; };
+
+ bool isClass2() const { return m_isClass2; };
+ void setClass2( bool Class2 = true ) { m_isClass2 = Class2; };
+
+ bool isClass3() const { return m_isClass3; };
+ void setClass3( bool Class3 = true ) { m_isClass3 = Class3; };
+
+ bool isHeadAllowed() const { 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; };
+
+ sal_uInt32 getRequestedTimeLife() const { return m_nRequestedTimeLife; };
+ void setRequestedTimeLife( const sal_uInt32 nRequestedTimeLife ) { m_nRequestedTimeLife = nRequestedTimeLife; };
+
+ const OUString & getURL() const { return m_sURL; };
+ void setURL( const OUString & sURL ) { m_sURL = sURL; };
+
+ const OUString & getRedirectedURL() const { return m_sRedirectedURL; };
+ void setRedirectedURL( const OUString & sRedirectedURL ) { m_sRedirectedURL = sRedirectedURL; };
+
+ void setAllowedMethods( const OUString & aAllowedMethods ) { m_aAllowedMethods = aAllowedMethods; } ;
+ const OUString & getAllowedMethods() const { return m_aAllowedMethods; } ;
+ bool isLockAllowed() const { return ( m_aAllowedMethods.indexOf( "LOCK" ) != -1 ); };
+
+ void setLocked( bool locked = true ) { m_isLocked = locked; } ;
+ bool isLocked() const { return m_isLocked; };
+
+ sal_uInt16 getHttpResponseStatusCode() const { return m_nHttpResponseStatusCode; };
+ void setHttpResponseStatusCode( const sal_uInt16 nHttpResponseStatusCode ) { m_nHttpResponseStatusCode = nHttpResponseStatusCode; };
+
+ const OUString & getHttpResponseStatusText() const { return m_sHttpResponseStatusText; };
+ void setHttpResponseStatusText( const OUString & rHttpResponseStatusText ) { m_sHttpResponseStatusText = rHttpResponseStatusText; };
+
+ void init() {
+ m_isClass1 = false;
+ m_isClass2 = false;
+ m_isClass3 = false;
+ m_isHeadAllowed = true;
+ m_isLocked = false;
+ m_aAllowedMethods.clear();
+ m_nStaleTime = 0;
+ m_nRequestedTimeLife = 0;
+ m_sURL.clear();
+ m_sRedirectedURL.clear();
+ m_nHttpResponseStatusCode = 0;
+ m_sHttpResponseStatusText.clear();
+ };
+
+ DAVOptions & operator=( const DAVOptions& rOpts );
+ bool operator==( const DAVOptions& rOpts ) const;
+
+ };
+
+ // TODO: the OUString key element in std::map needs to be changed with a URI representation
+ // along with a specific compare (std::less) implementation, as suggested in
+ // <https://tools.ietf.org/html/rfc3986#section-6>, to find by URI and not by string comparison
+ typedef std::map< OUString, DAVOptions,
+ std::less< OUString > > DAVOptionsMap;
+
+ class DAVOptionsCache
+ {
+ DAVOptionsMap m_aTheCache;
+ osl::Mutex m_aMutex;
+ public:
+ explicit DAVOptionsCache();
+ ~DAVOptionsCache();
+
+ bool getDAVOptions( const OUString & rURL, DAVOptions & rDAVOptions );
+ void removeDAVOptions( const OUString & rURL );
+ void addDAVOptions( DAVOptions & rDAVOptions, const sal_uInt32 nLifeTime );
+
+ void setHeadAllowed( const OUString & rURL, bool HeadAllowed = true );
+
+ private:
+
+ /// remove the last '/' in aUrl, if it exists
+ static void normalizeURLLastChar( OUString& aUrl ) {
+ if ( aUrl.getLength() > 1 &&
+ ( ( aUrl.lastIndexOf( '/' ) + 1 ) == aUrl.getLength() ) )
+ aUrl = aUrl.copy(0, aUrl.getLength() - 1 );
+ };
+ };
+
+ enum Depth { DAVZERO = 0, DAVONE = 1, DAVINFINITY = -1 };
+
+ enum ProppatchOperation { PROPSET = 0, PROPREMOVE = 1 };
+
+ struct ProppatchValue
+ {
+ ProppatchOperation operation;
+ OUString name;
+ css::uno::Any value;
+
+ ProppatchValue( const ProppatchOperation o,
+ const OUString & n,
+ const css::uno::Any & v )
+ : operation( o ), name( n ), value( v ) {}
+ };
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DAVTYPES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DateTimeHelper.cxx b/ucb/source/ucp/webdav-neon/DateTimeHelper.cxx
new file mode 100644
index 000000000..88ca23f16
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DateTimeHelper.cxx
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <osl/time.h>
+#include <com/sun/star/util/DateTime.hpp>
+#include "DateTimeHelper.hxx"
+
+using namespace com::sun::star::util;
+
+using namespace webdav_ucp;
+
+
+bool DateTimeHelper::ISO8601_To_DateTime (const OUString& s,
+ DateTime& dateTime)
+{
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ int year, month, day, hours, minutes, off_hours, off_minutes, fix;
+ double seconds;
+
+ // 2001-01-01T12:30:00Z
+ int n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lfZ",
+ &year, &month, &day, &hours, &minutes, &seconds );
+ if ( n == 6 )
+ {
+ fix = 0;
+ }
+ else
+ {
+ // 2001-01-01T12:30:00+03:30
+ n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lf+%02d:%02d",
+ &year, &month, &day, &hours, &minutes, &seconds,
+ &off_hours, &off_minutes );
+ if ( n == 8 )
+ {
+ fix = - off_hours * 3600 - off_minutes * 60;
+ }
+ else
+ {
+ // 2001-01-01T12:30:00-03:30
+ n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lf-%02d:%02d",
+ &year, &month, &day, &hours, &minutes, &seconds,
+ &off_hours, &off_minutes );
+ if ( n == 8 )
+ {
+ fix = off_hours * 3600 + off_minutes * 60;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // Convert to local time...
+
+ oslDateTime aDateTime;
+ aDateTime.NanoSeconds = 0;
+ aDateTime.Seconds = sal::static_int_cast< sal_uInt16 >(seconds); // 0-59
+ aDateTime.Minutes = sal::static_int_cast< sal_uInt16 >(minutes); // 0-59
+ aDateTime.Hours = sal::static_int_cast< sal_uInt16 >(hours); // 0-23
+ aDateTime.Day = sal::static_int_cast< sal_uInt16 >(day); // 1-31
+ aDateTime.DayOfWeek = 0; // 0-6, 0 = Sunday
+ aDateTime.Month = sal::static_int_cast< sal_uInt16 >(month); // 1-12
+ aDateTime.Year = sal::static_int_cast< sal_Int16 >(year);
+
+ TimeValue aTimeValue;
+ if ( osl_getTimeValueFromDateTime( &aDateTime, &aTimeValue ) )
+ {
+ aTimeValue.Seconds += fix;
+
+ if ( osl_getLocalTimeFromSystemTime( &aTimeValue, &aTimeValue ) )
+ {
+ if ( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) )
+ {
+ dateTime.Year = aDateTime.Year;
+ dateTime.Month = aDateTime.Month;
+ dateTime.Day = aDateTime.Day;
+ dateTime.Hours = aDateTime.Hours;
+ dateTime.Minutes = aDateTime.Minutes;
+ dateTime.Seconds = aDateTime.Seconds;
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+sal_Int32 DateTimeHelper::convertMonthToInt (const OUString& month)
+{
+ if (month == "Jan")
+ return 1;
+ else if (month == "Feb")
+ return 2;
+ else if (month == "Mar")
+ return 3;
+ else if (month == "Apr")
+ return 4;
+ else if (month == "May")
+ return 5;
+ else if (month == "Jun")
+ return 6;
+ else if (month == "Jul")
+ return 7;
+ else if (month == "Aug")
+ return 8;
+ else if (month == "Sep")
+ return 9;
+ else if (month == "Oct")
+ return 10;
+ else if (month == "Nov")
+ return 11;
+ else if (month == "Dec")
+ return 12;
+ else
+ return 0;
+}
+
+bool DateTimeHelper::RFC2068_To_DateTime (const OUString& s,
+ DateTime& dateTime)
+{
+ int year;
+ int day;
+ int hours;
+ int minutes;
+ int seconds;
+ char string_month[3 + 1];
+ char string_day[3 + 1];
+
+ sal_Int32 found = s.indexOf (',');
+ if (found != -1)
+ {
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ // RFC 1123
+ found = sscanf (aDT.getStr(), "%3s, %2d %3s %4d %2d:%2d:%2d GMT",
+ string_day, &day, string_month, &year, &hours, &minutes, &seconds);
+ if (found != 7)
+ {
+ // RFC 1036
+ found = sscanf (aDT.getStr(), "%3s, %2d-%3s-%2d %2d:%2d:%2d GMT",
+ string_day, &day, string_month, &year, &hours, &minutes, &seconds);
+ }
+ found = (found == 7) ? 1 : 0;
+ }
+ else
+ {
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ // ANSI C's asctime () format
+ found = sscanf (aDT.getStr(), "%3s %3s %d %2d:%2d:%2d %4d",
+ string_day, string_month,
+ &day, &hours, &minutes, &seconds, &year);
+ found = (found == 7) ? 1 : 0;
+ }
+
+ if (found)
+ {
+ found = 0;
+
+ int month = DateTimeHelper::convertMonthToInt (
+ OUString::createFromAscii (string_month));
+ if (month)
+ {
+ // Convert to local time...
+
+ oslDateTime aDateTime;
+ aDateTime.NanoSeconds = 0;
+ aDateTime.Seconds = sal::static_int_cast< sal_uInt16 >(seconds);
+ // 0-59
+ aDateTime.Minutes = sal::static_int_cast< sal_uInt16 >(minutes);
+ // 0-59
+ aDateTime.Hours = sal::static_int_cast< sal_uInt16 >(hours);
+ // 0-23
+ aDateTime.Day = sal::static_int_cast< sal_uInt16 >(day);
+ // 1-31
+ aDateTime.DayOfWeek = 0; //dayofweek; // 0-6, 0 = Sunday
+ aDateTime.Month = sal::static_int_cast< sal_uInt16 >(month);
+ // 1-12
+ aDateTime.Year = sal::static_int_cast< sal_Int16 >(year);
+
+ TimeValue aTimeValue;
+ if ( osl_getTimeValueFromDateTime( &aDateTime,
+ &aTimeValue ) )
+ {
+ if ( osl_getLocalTimeFromSystemTime( &aTimeValue,
+ &aTimeValue ) )
+ {
+ if ( osl_getDateTimeFromTimeValue( &aTimeValue,
+ &aDateTime ) )
+ {
+ dateTime.Year = aDateTime.Year;
+ dateTime.Month = aDateTime.Month;
+ dateTime.Day = aDateTime.Day;
+ dateTime.Hours = aDateTime.Hours;
+ dateTime.Minutes = aDateTime.Minutes;
+ dateTime.Seconds = aDateTime.Seconds;
+
+ found = 1;
+ }
+ }
+ }
+ }
+ }
+
+ return found != 0;
+}
+
+bool DateTimeHelper::convert (const OUString& s, DateTime& dateTime)
+{
+ if (ISO8601_To_DateTime (s, dateTime))
+ return true;
+ else if (RFC2068_To_DateTime (s, dateTime))
+ return true;
+ else
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/DateTimeHelper.hxx b/ucb/source/ucp/webdav-neon/DateTimeHelper.hxx
new file mode 100644
index 000000000..5abf6d400
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/DateTimeHelper.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DATETIMEHELPER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_DATETIMEHELPER_HXX
+
+#include <config_lgpl.h>
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star::util {
+ struct DateTime;
+}
+
+
+namespace webdav_ucp
+{
+
+class DateTimeHelper
+{
+private:
+ static sal_Int32 convertMonthToInt (const OUString& );
+
+ static bool ISO8601_To_DateTime (const OUString&,
+ css::util::DateTime& );
+
+ static bool RFC2068_To_DateTime (const OUString&,
+ css::util::DateTime& );
+
+public:
+ static bool convert (const OUString&,
+ css::util::DateTime& );
+};
+
+} // namespace webdav_ucp
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LinkSequence.cxx b/ucb/source/ucp/webdav-neon/LinkSequence.cxx
new file mode 100644
index 000000000..ea49352d9
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LinkSequence.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <config_lgpl.h>
+#include <string.h>
+#include <ne_xml.h>
+#include <memory>
+
+#include "LinkSequence.hxx"
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+namespace {
+
+struct LinkSequenceParseContext
+{
+ std::unique_ptr<ucb::Link> pLink;
+ bool hasSource;
+ bool hasDestination;
+
+ LinkSequenceParseContext()
+ : hasSource( false ), hasDestination( false ) {}
+};
+
+}
+
+#define STATE_TOP (1)
+
+#define STATE_LINK (STATE_TOP)
+#define STATE_DST (STATE_TOP + 1)
+#define STATE_SRC (STATE_TOP + 2)
+
+
+extern "C" {
+
+static int LinkSequence_startelement_callback(
+ void *,
+ int parent,
+ const char * /*nspace*/,
+ const char *name,
+ const char ** )
+{
+ if ( name != nullptr )
+ {
+ switch ( parent )
+ {
+ case NE_XML_STATEROOT:
+ if ( strcmp( name, "link" ) == 0 )
+ return STATE_LINK;
+ break;
+
+ case STATE_LINK:
+ if ( strcmp( name, "dst" ) == 0 )
+ return STATE_DST;
+ else if ( strcmp( name, "src" ) == 0 )
+ return STATE_SRC;
+ break;
+ }
+ }
+ return NE_XML_DECLINE;
+}
+
+
+static int LinkSequence_chardata_callback(
+ void *userdata,
+ int state,
+ const char *buf,
+ size_t len )
+{
+ LinkSequenceParseContext * pCtx
+ = static_cast< LinkSequenceParseContext * >( userdata );
+ if ( !pCtx->pLink )
+ pCtx->pLink.reset( new ucb::Link );
+
+ switch ( state )
+ {
+ case STATE_DST:
+ pCtx->pLink->Destination
+ = OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ pCtx->hasDestination = true;
+ break;
+
+ case STATE_SRC:
+ pCtx->pLink->Source
+ = OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ pCtx->hasSource = true;
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+
+static int LinkSequence_endelement_callback(
+ void *userdata,
+ int state,
+ const char *,
+ const char * )
+{
+ LinkSequenceParseContext * pCtx
+ = static_cast< LinkSequenceParseContext * >( userdata );
+ if ( !pCtx->pLink )
+ pCtx->pLink.reset( new ucb::Link );
+
+ switch ( state )
+ {
+ case STATE_LINK:
+ if ( !pCtx->hasDestination || !pCtx->hasSource )
+ return 1; // abort
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+}
+
+// static
+bool LinkSequence::createFromXML( const OString & rInData,
+ uno::Sequence< ucb::Link > & rOutData )
+{
+ const sal_Int32 TOKEN_LENGTH = 7; // </link>
+ bool success = true;
+
+ // rInData may contain multiple <link>...</link> tags.
+ sal_Int32 nCount = 0;
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = rInData.indexOf( "</link>" );
+ while ( nEnd > -1 )
+ {
+ ne_xml_parser * parser = ne_xml_create();
+ if ( !parser )
+ {
+ success = false;
+ break;
+ }
+
+ LinkSequenceParseContext aCtx;
+ ne_xml_push_handler( parser,
+ LinkSequence_startelement_callback,
+ LinkSequence_chardata_callback,
+ LinkSequence_endelement_callback,
+ &aCtx );
+
+ ne_xml_parse( parser,
+ rInData.getStr() + nStart,
+ nEnd - nStart + TOKEN_LENGTH );
+
+ success = !ne_xml_failed( parser );
+
+ ne_xml_destroy( parser );
+
+ if ( !success )
+ break;
+
+ if ( aCtx.pLink )
+ {
+ nCount++;
+ if ( nCount > rOutData.getLength() )
+ rOutData.realloc( rOutData.getLength() + 1 );
+
+ rOutData[ nCount - 1 ] = *aCtx.pLink;
+ }
+
+ nStart = nEnd + TOKEN_LENGTH;
+ nEnd = rInData.indexOf( "</link>", nStart );
+ }
+
+ return success;
+}
+
+
+// static
+bool LinkSequence::toXML( const uno::Sequence< ucb::Link > & rInData,
+ OUString & rOutData )
+{
+ // <link><src>value</src><dst>value</dst></link><link><src>...
+
+ for ( const auto& rLink : rInData )
+ {
+ rOutData += "<link><src>";
+ rOutData += rLink.Source;
+ rOutData += "</src><dst>";
+ rOutData += rLink.Destination;
+ rOutData += "</dst></link>";
+ }
+ return rInData.hasElements();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LinkSequence.hxx b/ucb/source/ucp/webdav-neon/LinkSequence.hxx
new file mode 100644
index 000000000..8c88610dd
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LinkSequence.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LINKSEQUENCE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LINKSEQUENCE_HXX
+
+#include <rtl/string.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/ucb/Link.hpp>
+
+namespace webdav_ucp
+{
+
+class LinkSequence
+{
+public:
+ static bool createFromXML( const OString & rInData,
+ css::uno::Sequence< css::ucb::Link > & rOutData );
+ static bool toXML( const css::uno::Sequence< css::ucb::Link > & rInData,
+ OUString & rOutData );
+};
+
+}
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LINKSEQUENCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LockEntrySequence.cxx b/ucb/source/ucp/webdav-neon/LockEntrySequence.cxx
new file mode 100644
index 000000000..61be4420c
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LockEntrySequence.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <config_lgpl.h>
+#include <string.h>
+#include <ne_xml.h>
+#include "LockEntrySequence.hxx"
+#include <memory>
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+namespace {
+
+struct LockEntrySequenceParseContext
+{
+ std::unique_ptr<ucb::LockEntry> pEntry;
+ bool hasScope;
+ bool hasType;
+
+ LockEntrySequenceParseContext()
+ : hasScope( false ), hasType( false ) {}
+};
+
+}
+
+#define STATE_TOP (1)
+
+#define STATE_LOCKENTRY (STATE_TOP)
+#define STATE_LOCKSCOPE (STATE_TOP + 1)
+#define STATE_EXCLUSIVE (STATE_TOP + 2)
+#define STATE_SHARED (STATE_TOP + 3)
+#define STATE_LOCKTYPE (STATE_TOP + 4)
+#define STATE_WRITE (STATE_TOP + 5)
+
+
+extern "C" {
+
+static int LockEntrySequence_startelement_callback(
+ void *,
+ int parent,
+ const char * /*nspace*/,
+ const char *name,
+ const char ** )
+{
+ if ( name != nullptr )
+ {
+ switch ( parent )
+ {
+ case NE_XML_STATEROOT:
+ if ( strcmp( name, "lockentry" ) == 0 )
+ return STATE_LOCKENTRY;
+ break;
+
+ case STATE_LOCKENTRY:
+ if ( strcmp( name, "lockscope" ) == 0 )
+ return STATE_LOCKSCOPE;
+ else if ( strcmp( name, "locktype" ) == 0 )
+ return STATE_LOCKTYPE;
+
+#define IIS_BUGS_WORKAROUND
+
+#ifdef IIS_BUGS_WORKAROUND
+ /* IIS (6) returns XML violating RFC 4918
+ for DAV:supportedlock property value.
+
+ <lockentry>
+ <write></write>
+ <shared></shared>
+ </lockentry>
+ <lockentry>
+ <write></write>
+ <exclusive></exclusive>
+ </lockentry>
+
+ Bother...
+ */
+ else if ( strcmp( name, "exclusive" ) == 0 )
+ return STATE_EXCLUSIVE;
+ else if ( strcmp( name, "shared" ) == 0 )
+ return STATE_SHARED;
+ else if ( strcmp( name, "write" ) == 0 )
+ return STATE_WRITE;
+#endif
+ break;
+
+ case STATE_LOCKSCOPE:
+ if ( strcmp( name, "exclusive" ) == 0 )
+ return STATE_EXCLUSIVE;
+ else if ( strcmp( name, "shared" ) == 0 )
+ return STATE_SHARED;
+ break;
+
+ case STATE_LOCKTYPE:
+ if ( strcmp( name, "write" ) == 0 )
+ return STATE_WRITE;
+ break;
+ }
+ }
+ return NE_XML_DECLINE;
+}
+
+
+static int LockEntrySequence_chardata_callback(
+ void *,
+ int,
+ const char *,
+ size_t )
+{
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+
+static int LockEntrySequence_endelement_callback(
+ void *userdata,
+ int state,
+ const char *,
+ const char * )
+{
+ LockEntrySequenceParseContext * pCtx
+ = static_cast< LockEntrySequenceParseContext * >( userdata );
+ if ( !pCtx->pEntry )
+ pCtx->pEntry.reset( new ucb::LockEntry );
+
+ switch ( state )
+ {
+ case STATE_EXCLUSIVE:
+ pCtx->pEntry->Scope = ucb::LockScope_EXCLUSIVE;
+ pCtx->hasScope = true;
+ break;
+
+ case STATE_SHARED:
+ pCtx->pEntry->Scope = ucb::LockScope_SHARED;
+ pCtx->hasScope = true;
+ break;
+
+ case STATE_WRITE:
+ pCtx->pEntry->Type = ucb::LockType_WRITE;
+ pCtx->hasType = true;
+ break;
+
+ case STATE_LOCKSCOPE:
+ if ( !pCtx->hasScope )
+ return 1; // abort
+ break;
+
+ case STATE_LOCKTYPE:
+ if ( !pCtx->hasType )
+ return 1; // abort
+ break;
+
+ case STATE_LOCKENTRY:
+ if ( !pCtx->hasType || !pCtx->hasScope )
+ return 1; // abort
+ break;
+
+ default:
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+}
+
+// static
+bool LockEntrySequence::createFromXML( const OString & rInData,
+ uno::Sequence<
+ ucb::LockEntry > & rOutData )
+{
+ const sal_Int32 TOKEN_LENGTH = 12; // </lockentry>
+ bool success = true;
+
+ // rInData may contain multiple <lockentry>...</lockentry> tags.
+ sal_Int32 nCount = 0;
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = rInData.indexOf( "</lockentry>" );
+ while ( nEnd > -1 )
+ {
+ ne_xml_parser * parser = ne_xml_create();
+ if ( !parser )
+ {
+ success = false;
+ break;
+ }
+
+ LockEntrySequenceParseContext aCtx;
+ ne_xml_push_handler( parser,
+ LockEntrySequence_startelement_callback,
+ LockEntrySequence_chardata_callback,
+ LockEntrySequence_endelement_callback,
+ &aCtx );
+
+ ne_xml_parse( parser,
+ rInData.getStr() + nStart,
+ nEnd - nStart + TOKEN_LENGTH );
+
+ success = !ne_xml_failed( parser );
+
+ ne_xml_destroy( parser );
+
+ if ( !success )
+ break;
+
+ if ( aCtx.pEntry )
+ {
+ nCount++;
+ if ( nCount > rOutData.getLength() )
+ rOutData.realloc( rOutData.getLength() + 2 );
+
+ rOutData[ nCount - 1 ] = *aCtx.pEntry;
+ }
+
+ nStart = nEnd + TOKEN_LENGTH;
+ nEnd = rInData.indexOf( "</lockentry>", nStart );
+ }
+
+ rOutData.realloc( nCount );
+ return success;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LockEntrySequence.hxx b/ucb/source/ucp/webdav-neon/LockEntrySequence.hxx
new file mode 100644
index 000000000..87f173203
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LockEntrySequence.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKENTRYSEQUENCE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKENTRYSEQUENCE_HXX
+
+#include <rtl/string.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/ucb/LockEntry.hpp>
+
+namespace webdav_ucp
+{
+
+class LockEntrySequence
+{
+public:
+ static bool createFromXML( const OString & rInData,
+ css::uno::Sequence< css::ucb::LockEntry > & rOutData );
+};
+
+}
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKENTRYSEQUENCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LockSequence.cxx b/ucb/source/ucp/webdav-neon/LockSequence.cxx
new file mode 100644
index 000000000..b9399c60d
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LockSequence.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <config_lgpl.h>
+#include <string.h>
+#include <ne_xml.h>
+#include "LockSequence.hxx"
+#include <memory>
+#include <sal/log.hxx>
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+namespace {
+
+struct LockSequenceParseContext
+{
+ std::unique_ptr<ucb::Lock> pLock;
+ bool hasLockScope;
+ bool hasLockType;
+ bool hasDepth;
+ bool hasHREF;
+ bool hasTimeout;
+
+ LockSequenceParseContext()
+ : hasLockScope( false ), hasLockType( false ),
+ hasDepth( false ), hasHREF( false ), hasTimeout( false ) {}
+};
+
+}
+
+#define STATE_TOP (1)
+
+#define STATE_ACTIVELOCK (STATE_TOP)
+#define STATE_LOCKSCOPE (STATE_TOP + 1)
+#define STATE_LOCKTYPE (STATE_TOP + 2)
+#define STATE_DEPTH (STATE_TOP + 3)
+#define STATE_OWNER (STATE_TOP + 4)
+#define STATE_TIMEOUT (STATE_TOP + 5)
+#define STATE_LOCKTOKEN (STATE_TOP + 6)
+#define STATE_EXCLUSIVE (STATE_TOP + 7)
+#define STATE_SHARED (STATE_TOP + 8)
+#define STATE_WRITE (STATE_TOP + 9)
+#define STATE_HREF (STATE_TOP + 10)
+
+
+extern "C" {
+
+static int LockSequence_startelement_callback(
+ void *,
+ int parent,
+ const char * /*nspace*/,
+ const char *name,
+ const char ** )
+{
+ if ( name != nullptr )
+ {
+ switch ( parent )
+ {
+ case NE_XML_STATEROOT:
+ if ( strcmp( name, "activelock" ) == 0 )
+ return STATE_ACTIVELOCK;
+ break;
+
+ case STATE_ACTIVELOCK:
+ if ( strcmp( name, "lockscope" ) == 0 )
+ return STATE_LOCKSCOPE;
+ else if ( strcmp( name, "locktype" ) == 0 )
+ return STATE_LOCKTYPE;
+ else if ( strcmp( name, "depth" ) == 0 )
+ return STATE_DEPTH;
+ else if ( strcmp( name, "owner" ) == 0 )
+ return STATE_OWNER;
+ else if ( strcmp( name, "timeout" ) == 0 )
+ return STATE_TIMEOUT;
+ else if ( strcmp( name, "locktoken" ) == 0 )
+ return STATE_LOCKTOKEN;
+ break;
+
+ case STATE_LOCKSCOPE:
+ if ( strcmp( name, "exclusive" ) == 0 )
+ return STATE_EXCLUSIVE;
+ else if ( strcmp( name, "shared" ) == 0 )
+ return STATE_SHARED;
+ break;
+
+ case STATE_LOCKTYPE:
+ if ( strcmp( name, "write" ) == 0 )
+ return STATE_WRITE;
+ break;
+
+ case STATE_LOCKTOKEN:
+ if ( strcmp( name, "href" ) == 0 )
+ return STATE_HREF;
+ break;
+
+ case STATE_OWNER:
+ // owner elem contains ANY. Accept anything; no state change.
+ return STATE_OWNER;
+ }
+ }
+ return NE_XML_DECLINE;
+}
+
+
+static int LockSequence_chardata_callback(
+ void *userdata,
+ int state,
+ const char *buf,
+ size_t len )
+{
+ LockSequenceParseContext * pCtx
+ = static_cast< LockSequenceParseContext * >( userdata );
+ if ( !pCtx->pLock )
+ pCtx->pLock.reset( new ucb::Lock );
+
+ // Beehive sends XML values containing trailing newlines.
+ if ( buf[ len - 1 ] == 0x0a )
+ len--;
+
+ switch ( state )
+ {
+ case STATE_DEPTH:
+ if ( rtl_str_compareIgnoreAsciiCase_WithLength(
+ buf, len, "0", 1 ) == 0 )
+ {
+ pCtx->pLock->Depth = ucb::LockDepth_ZERO;
+ pCtx->hasDepth = true;
+ }
+ else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
+ buf, len, "1", 1 ) == 0 )
+ {
+ pCtx->pLock->Depth = ucb::LockDepth_ONE;
+ pCtx->hasDepth = true;
+ }
+ else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
+ buf, len, "infinity", 8 ) == 0 )
+ {
+ pCtx->pLock->Depth = ucb::LockDepth_INFINITY;
+ pCtx->hasDepth = true;
+ }
+ else
+ SAL_WARN( "ucb.ucp.webdav", "LockSequence_chardata_callback - Unknown depth!" );
+ break;
+
+ case STATE_OWNER:
+ {
+ // collect raw XML data... (owner contains ANY)
+ OUString aValue;
+ pCtx->pLock->Owner >>= aValue;
+ aValue += OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ pCtx->pLock->Owner <<= aValue;
+ break;
+ }
+
+ case STATE_TIMEOUT:
+
+ // RFC2518, RFC2616:
+
+ // TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other)
+ // DAVTimeOutVal = 1*digit
+ // Other = "Extend" field-value
+ // field-value = *( field-content | LWS )
+ // field-content = <the OCTETs making up the field-value
+ // and consisting of either *TEXT or combinations
+ // of token, separators, and quoted-string>
+ //
+ // RFC4918, <http://tools.ietf.org/html/rfc4918#section-10.7>
+ // "The timeout value for TimeType "Second" MUST
+ // NOT be greater than 2^32-1."
+
+ if ( rtl_str_compareIgnoreAsciiCase_WithLength(
+ buf, len, "Infinite", 8 ) == 0 )
+ {
+ pCtx->pLock->Timeout = sal_Int64( -1 );
+ pCtx->hasTimeout = true;
+ }
+ else if ( rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
+ buf, len, "Second-", 7, 7 ) == 0 )
+ {
+ pCtx->pLock->Timeout
+ = OString( buf + 7, len - 7 ).toInt64();
+ pCtx->hasTimeout = true;
+ }
+// else if ( rtl_str_shortenedCompareIgnoreCase_WithLength(
+// buf, len, "Extend", 6, 6 ) == 0 )
+// {
+// @@@
+// }
+ else
+ {
+ pCtx->pLock->Timeout = sal_Int64( -1 );
+ pCtx->hasTimeout = true;
+ SAL_WARN( "ucb.ucp.webdav", "LockSequence_chardata_callback - Unknown timeout!" );
+ }
+ break;
+
+ case STATE_HREF:
+ {
+ // collect hrefs.
+ sal_Int32 nPos = pCtx->pLock->LockTokens.getLength();
+ pCtx->pLock->LockTokens.realloc( nPos + 1 );
+ pCtx->pLock->LockTokens[ nPos ]
+ = OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ pCtx->hasHREF = true;
+ break;
+ }
+
+ }
+
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+
+static int LockSequence_endelement_callback(
+ void *userdata,
+ int state,
+ const char *,
+ const char * )
+{
+ LockSequenceParseContext * pCtx
+ = static_cast< LockSequenceParseContext * >( userdata );
+ if ( !pCtx->pLock )
+ pCtx->pLock.reset( new ucb::Lock );
+
+ switch ( state )
+ {
+ case STATE_EXCLUSIVE:
+ pCtx->pLock->Scope = ucb::LockScope_EXCLUSIVE;
+ pCtx->hasLockScope = true;
+ break;
+
+ case STATE_SHARED:
+ pCtx->pLock->Scope = ucb::LockScope_SHARED;
+ pCtx->hasLockScope = true;
+ break;
+
+ case STATE_WRITE:
+ pCtx->pLock->Type = ucb::LockType_WRITE;
+ pCtx->hasLockType = true;
+ break;
+
+ case STATE_DEPTH:
+ if ( !pCtx->hasDepth )
+ return 1; // abort
+ break;
+
+ case STATE_HREF:
+ if ( !pCtx->hasHREF )
+ return 1; // abort
+ break;
+
+ case STATE_TIMEOUT:
+ if ( !pCtx->hasTimeout )
+ return 1; // abort
+ break;
+
+ case STATE_LOCKSCOPE:
+ if ( !pCtx->hasLockScope )
+ return 1; // abort
+ break;
+
+ case STATE_LOCKTYPE:
+ if ( !pCtx->hasLockType )
+ return 1; // abort
+ break;
+
+ case STATE_ACTIVELOCK:
+ if ( !pCtx->hasLockType || !pCtx->hasDepth )
+ return 1; // abort
+ break;
+
+ default:
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+}
+
+// static
+bool LockSequence::createFromXML( const OString & rInData,
+ uno::Sequence< ucb::Lock > & rOutData )
+{
+ const sal_Int32 TOKEN_LENGTH = 13; // </activelock>
+ bool success = true;
+
+ // rInData may contain multiple <activelock>...</activelock> tags.
+ sal_Int32 nCount = 0;
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = rInData.indexOf( "</activelock>" );
+ while ( nEnd > -1 )
+ {
+ ne_xml_parser * parser = ne_xml_create();
+ if ( !parser )
+ {
+ success = false;
+ break;
+ }
+
+ LockSequenceParseContext aCtx;
+ ne_xml_push_handler( parser,
+ LockSequence_startelement_callback,
+ LockSequence_chardata_callback,
+ LockSequence_endelement_callback,
+ &aCtx );
+
+ ne_xml_parse( parser,
+ rInData.getStr() + nStart,
+ nEnd - nStart + TOKEN_LENGTH );
+
+ success = !ne_xml_failed( parser );
+
+ ne_xml_destroy( parser );
+
+ if ( !success )
+ break;
+
+ if ( aCtx.pLock )
+ {
+ nCount++;
+ if ( nCount > rOutData.getLength() )
+ rOutData.realloc( rOutData.getLength() + 1 );
+
+ rOutData[ nCount - 1 ] = *aCtx.pLock;
+ }
+
+ nStart = nEnd + TOKEN_LENGTH;
+ nEnd = rInData.indexOf( "</activelock>", nStart );
+ }
+
+ return success;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/LockSequence.hxx b/ucb/source/ucp/webdav-neon/LockSequence.hxx
new file mode 100644
index 000000000..a35ddee99
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/LockSequence.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKSEQUENCE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKSEQUENCE_HXX
+
+#include <config_lgpl.h>
+#include <rtl/string.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/ucb/Lock.hpp>
+
+namespace webdav_ucp
+{
+
+class LockSequence
+{
+public:
+ static bool createFromXML( const OString & rInData,
+ css::uno::Sequence< css::ucb::Lock > & rOutData );
+};
+
+}
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_LOCKSEQUENCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonHeadRequest.cxx b/ucb/source/ucp/webdav-neon/NeonHeadRequest.cxx
new file mode 100644
index 000000000..68f24f440
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonHeadRequest.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include "NeonHeadRequest.hxx"
+#include "NeonSession.hxx"
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+namespace {
+
+void process_headers( ne_request * req,
+ DAVResource & rResource,
+ const std::vector< OUString > & rHeaderNames )
+{
+ void * cursor = nullptr;
+ const char * name, *value;
+
+#if defined SAL_LOG_INFO
+ {
+ for ( const auto& rHeader : rHeaderNames )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "HEAD - requested header: " << rHeader );
+ }
+ }
+#endif
+ while ( ( cursor = ne_response_header_iterate( req, cursor,
+ &name, &value ) ) != nullptr ) {
+ // The HTTP header `field-name` must be a `token`, which can only contain a subset of ASCII;
+ // assume that Neon will already have rejected any invalid data, so that it is guaranteed
+ // that `name` is ASCII-only:
+ OUString aHeaderName( OUString::createFromAscii( name ) );
+ // The HTTP header `field-value` may contain obsolete (as per RFC 7230) `obs-text` non-ASCII
+ // %x80-FF octets, lets preserve them as individual characters in `aHeaderValue` by treating
+ // `value` as ISO 8859-1:
+ OUString aHeaderValue(value, strlen(value), RTL_TEXTENCODING_ISO_8859_1);
+
+ SAL_INFO( "ucb.ucp.webdav", "HEAD - received header: " << aHeaderName << ":" << aHeaderValue);
+
+ // Note: Empty vector means that all headers are requested.
+ bool bIncludeIt = rHeaderNames.empty();
+
+ if ( !bIncludeIt )
+ {
+ // Check whether this header was requested.
+ auto it = std::find_if(rHeaderNames.begin(), rHeaderNames.end(),
+ [&aHeaderName](const OUString& rName) {
+ // header names are case insensitive
+ return rName.equalsIgnoreAsciiCase( aHeaderName );
+ });
+
+ if ( it != rHeaderNames.end() )
+ {
+ aHeaderName = *it;
+ bIncludeIt = true;
+ }
+ }
+
+ if ( bIncludeIt )
+ {
+ // Create & set the PropertyValue
+ DAVPropertyValue thePropertyValue;
+ // header names are case insensitive, so are the
+ // corresponding property names
+ thePropertyValue.Name = aHeaderName.toAsciiLowerCase();
+ thePropertyValue.IsCaseSensitive = false;
+ thePropertyValue.Value <<= aHeaderValue;
+
+ // Add the newly created PropertyValue
+ rResource.properties.push_back( thePropertyValue );
+ }
+ }
+}
+
+} // namespace
+
+NeonHeadRequest::NeonHeadRequest( HttpSession * inSession,
+ const OUString & inPath,
+ const std::vector< OUString > &
+ inHeaderNames,
+ DAVResource & ioResource,
+ int & nError )
+{
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ // Create and dispatch HEAD request. Install catcher for all response
+ // header fields.
+ ne_request * req = ne_request_create( inSession,
+ "HEAD",
+ OUStringToOString(
+ inPath,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ nError = ne_request_dispatch( req );
+ }
+
+ process_headers( req, ioResource, inHeaderNames );
+
+ if ( nError == NE_OK && ne_get_status( req )->klass != 2 )
+ nError = NE_ERROR;
+
+ ne_request_destroy( req );
+}
+
+NeonHeadRequest::~NeonHeadRequest()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonHeadRequest.hxx b/ucb/source/ucp/webdav-neon/NeonHeadRequest.hxx
new file mode 100644
index 000000000..06a28af03
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonHeadRequest.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONHEADREQUEST_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONHEADREQUEST_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+#include "NeonTypes.hxx"
+#include "DAVResource.hxx"
+
+namespace webdav_ucp
+{
+
+class NeonHeadRequest
+{
+public:
+ // named / allprop
+ NeonHeadRequest( HttpSession* inSession,
+ const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ int & nError );
+ ~NeonHeadRequest();
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONHEADREQUEST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonInputStream.cxx b/ucb/source/ucp/webdav-neon/NeonInputStream.cxx
new file mode 100644
index 000000000..848dc2b81
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonInputStream.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "NeonInputStream.hxx"
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <string.h>
+
+using namespace cppu;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace webdav_ucp;
+
+NeonInputStream::NeonInputStream()
+: mLen( 0 ),
+ mPos( 0 )
+{
+}
+
+NeonInputStream::~NeonInputStream()
+{
+}
+
+// Allows the caller to add some data to the "end" of the stream
+void NeonInputStream::AddToStream( const char * inBuf, sal_Int32 inLen )
+{
+ mInputBuffer.realloc( sal::static_int_cast<sal_Int32>(mLen) + inLen );
+ memcpy( mInputBuffer.getArray() + mLen, inBuf, inLen );
+ mLen += inLen;
+}
+
+Any NeonInputStream::queryInterface( const Type &type )
+{
+ Any aRet = ::cppu::queryInterface( type,
+ static_cast< XInputStream * >( this ),
+ static_cast< XSeekable * >( this ) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( type );
+}
+
+// "Reads" the specified number of bytes from the stream
+sal_Int32 SAL_CALL NeonInputStream::readBytes(
+ css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ // Work out how much we're actually going to write
+ sal_Int32 theBytes2Read = nBytesToRead;
+ sal_Int32 theBytesLeft = sal::static_int_cast<sal_Int32>(mLen - mPos);
+ if ( theBytes2Read > theBytesLeft )
+ theBytes2Read = theBytesLeft;
+
+ // Realloc buffer.
+ aData.realloc( theBytes2Read );
+
+ // Write the data
+ memcpy(
+ aData.getArray(), mInputBuffer.getConstArray() + mPos, theBytes2Read );
+
+ // Update our stream position for next time
+ mPos += theBytes2Read;
+
+ return theBytes2Read;
+}
+
+sal_Int32 SAL_CALL NeonInputStream::readSomeBytes(
+ css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ // Warning: What should this be doing ?
+ return readBytes( aData, nMaxBytesToRead );
+}
+
+// Moves the current stream position forward
+void SAL_CALL NeonInputStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ mPos += nBytesToSkip;
+ if ( mPos >= mLen )
+ mPos = mLen;
+}
+
+// Returns the number of unread bytes currently remaining on the stream
+sal_Int32 SAL_CALL NeonInputStream::available( )
+{
+ return std::min<sal_Int64>(SAL_MAX_INT32, mLen - mPos);
+}
+
+void SAL_CALL NeonInputStream::closeInput()
+{
+}
+
+void SAL_CALL NeonInputStream::seek( sal_Int64 location )
+{
+ if ( location < 0 )
+ throw css::lang::IllegalArgumentException();
+
+ if ( location > mLen )
+ throw css::lang::IllegalArgumentException();
+
+ mPos = location;
+}
+
+sal_Int64 SAL_CALL NeonInputStream::getPosition()
+{
+ return mPos;
+}
+
+sal_Int64 SAL_CALL NeonInputStream::getLength()
+{
+ return mLen;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonInputStream.hxx b/ucb/source/ucp/webdav-neon/NeonInputStream.hxx
new file mode 100644
index 000000000..e496d72bd
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonInputStream.hxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONINPUTSTREAM_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONINPUTSTREAM_HXX
+
+#include <config_lgpl.h>
+#include <sal/types.h>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+
+
+namespace webdav_ucp
+{
+
+// A simple XInputStream implementation provided specifically for use
+// by the DAVSession::GET method.
+class NeonInputStream : public css::io::XInputStream,
+ public css::io::XSeekable,
+ public ::cppu::OWeakObject
+{
+ private:
+ css::uno::Sequence< sal_Int8 > mInputBuffer;
+ sal_Int64 mLen;
+ sal_Int64 mPos;
+
+ public:
+ NeonInputStream();
+ virtual ~NeonInputStream() override;
+
+ // Add some data to the end of the stream
+ void AddToStream( const char * inBuf, sal_Int32 inLen );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & type ) override;
+
+ virtual void SAL_CALL acquire()
+ throw () override
+ { OWeakObject::acquire(); }
+
+ virtual void SAL_CALL release()
+ throw() override
+ { OWeakObject::release(); }
+
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes(
+ css::uno::Sequence< sal_Int8 > & aData,
+ sal_Int32 nBytesToRead ) override;
+
+ virtual sal_Int32 SAL_CALL readSomeBytes(
+ css::uno::Sequence< sal_Int8 > & aData,
+ sal_Int32 nMaxBytesToRead ) override;
+
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+
+ virtual sal_Int32 SAL_CALL available() override;
+
+ virtual void SAL_CALL closeInput() override;
+
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+
+ virtual sal_Int64 SAL_CALL getPosition() override;
+
+ virtual sal_Int64 SAL_CALL getLength() override;
+};
+
+} // namespace webdav_ucp
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONINPUTSTREAM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonLockStore.cxx b/ucb/source/ucp/webdav-neon/NeonLockStore.cxx
new file mode 100644
index 000000000..a4d25b4b1
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonLockStore.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <ne_uri.h>
+#include <rtl/ustring.hxx>
+#include <osl/time.h>
+#include <osl/thread.hxx>
+#include <sal/log.hxx>
+#include <salhelper/thread.hxx>
+#include "NeonSession.hxx"
+#include "NeonLockStore.hxx"
+
+using namespace webdav_ucp;
+
+namespace webdav_ucp {
+
+class TickerThread : public salhelper::Thread
+{
+ bool m_bFinish;
+ NeonLockStore & m_rLockStore;
+
+public:
+
+ explicit TickerThread( NeonLockStore & rLockStore )
+ : Thread( "NeonTickerThread" ), m_bFinish( false )
+ , m_rLockStore( rLockStore )
+ {
+ }
+
+ void finish() { m_bFinish = true; }
+
+private:
+
+ virtual void execute() override;
+};
+
+} // namespace webdav_ucp
+
+void TickerThread::execute()
+{
+ SAL_INFO( "ucb.ucp.webdav", "TickerThread: start." );
+
+ // we have to go through the loop more often to be able to finish ~quickly
+ const int nNth = 25;
+
+ int nCount = nNth;
+ while ( !m_bFinish )
+ {
+ if (--nCount < 0)
+ {
+ m_rLockStore.refreshLocks();
+ nCount = nNth;
+ }
+
+ salhelper::Thread::wait(TimeValue(0, 1000000000 / nNth));
+ }
+
+ SAL_INFO( "ucb.ucp.webdav", "TickerThread: stop." );
+}
+
+NeonLockStore::NeonLockStore()
+ : m_pNeonLockStore( ne_lockstore_create() )
+{
+ /*
+ * ne_lockstore_create() never returns a NULL; neon calls abort() in case of an out-of-memory
+ * situation.
+ * Please see:
+ * <http://www.webdav.org/neon/doc/html/refneon.html>
+ * topic title "Memory handling", copied here verbatim:
+ *
+ * "neon does not attempt to cope gracefully with an out-of-memory situation;
+ * instead, by default, the abort function is called to immediately terminate
+ * the process. An application may register a custom function which will be
+ * called before abort in such a situation; see ne_oom_callback."
+ */
+}
+
+NeonLockStore::~NeonLockStore()
+{
+ {
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ stopTicker(aGuard);
+ } // actually no threads should even try to access members now
+
+ // release active locks, if any.
+ SAL_WARN_IF( !m_aLockInfoMap.empty(), "ucb.ucp.webdav", "NeonLockStore::~NeonLockStore - Releasing active locks!" );
+
+ for ( auto& rLockInfo : m_aLockInfoMap )
+ {
+ NeonLock * pLock = rLockInfo.first;
+ rLockInfo.second.xSession->UNLOCK( pLock );
+
+ ne_lockstore_remove( m_pNeonLockStore, pLock );
+ ne_lock_destroy( pLock );
+ }
+
+ ne_lockstore_destroy( m_pNeonLockStore );
+}
+
+void NeonLockStore::startTicker()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !m_pTickerThread.is() )
+ {
+ m_pTickerThread = new TickerThread( *this );
+ m_pTickerThread->launch();
+ }
+}
+
+void NeonLockStore::stopTicker(osl::ClearableMutexGuard & rGuard)
+{
+ rtl::Reference<TickerThread> pTickerThread;
+
+ if (m_pTickerThread.is())
+ {
+ m_pTickerThread->finish(); // needs mutex
+ // the TickerThread may run refreshLocks() at most once after this
+ pTickerThread = m_pTickerThread;
+ m_pTickerThread.clear();
+ }
+
+ rGuard.clear();
+
+ if (pTickerThread.is() && pTickerThread->getIdentifier() != osl::Thread::getCurrentIdentifier())
+ pTickerThread->join(); // without m_aMutex locked (to prevent deadlock)
+}
+
+void NeonLockStore::registerSession( HttpSession * pHttpSession )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ne_lockstore_register( m_pNeonLockStore, pHttpSession );
+}
+
+NeonLock * NeonLockStore::findByUri( OUString const & rUri )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ne_uri aUri;
+ ne_uri_parse( OUStringToOString(
+ rUri, RTL_TEXTENCODING_UTF8 ).getStr(), &aUri );
+ return ne_lockstore_findbyuri( m_pNeonLockStore, &aUri );
+}
+
+void NeonLockStore::addLock( NeonLock * pLock,
+ rtl::Reference< NeonSession > const & xSession,
+ sal_Int32 nLastChanceToSendRefreshRequest )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ne_lockstore_add( m_pNeonLockStore, pLock );
+ m_aLockInfoMap[ pLock ]
+ = LockInfo( xSession, nLastChanceToSendRefreshRequest );
+
+ startTicker();
+}
+
+void NeonLockStore::removeLock( NeonLock * pLock )
+{
+ osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ m_aLockInfoMap.erase( pLock );
+ ne_lockstore_remove( m_pNeonLockStore, pLock );
+
+ if ( m_aLockInfoMap.empty() )
+ stopTicker(aGuard);
+}
+
+void NeonLockStore::removeLockDeferred(NeonLock* pLock)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ m_aRemoveDeferred.push_back(pLock);
+}
+
+void NeonLockStore::refreshLocks()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ for ( auto& rLockInfo : m_aLockInfoMap )
+ {
+ LockInfo & rInfo = rLockInfo.second;
+ if ( rInfo.nLastChanceToSendRefreshRequest != -1 )
+ {
+ // 30 seconds or less remaining until lock expires?
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ if ( rInfo.nLastChanceToSendRefreshRequest - 30
+ <= sal_Int32( t1.Seconds ) )
+ {
+ // refresh the lock.
+ sal_Int32 nlastChanceToSendRefreshRequest = -1;
+ if ( rInfo.xSession->LOCK(
+ rLockInfo.first,
+ /* out param */ nlastChanceToSendRefreshRequest ) )
+ {
+ rInfo.nLastChanceToSendRefreshRequest
+ = nlastChanceToSendRefreshRequest;
+ }
+ else
+ {
+ // refresh failed. stop auto-refresh.
+ rInfo.nLastChanceToSendRefreshRequest = -1;
+ }
+ }
+ }
+ }
+ // removeLock will not need to actually release the lock, because this is run from TickerThread
+ for (auto pLock : m_aRemoveDeferred)
+ removeLock(pLock);
+ m_aRemoveDeferred.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonLockStore.hxx b/ucb/source/ucp/webdav-neon/NeonLockStore.hxx
new file mode 100644
index 000000000..fa64cb320
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonLockStore.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONLOCKSTORE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONLOCKSTORE_HXX
+
+#include <config_lgpl.h>
+#include <map>
+
+#include <ne_locks.h>
+
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include "NeonTypes.hxx"
+
+namespace webdav_ucp
+{
+
+class TickerThread;
+class NeonSession;
+
+struct LockInfo
+{
+ rtl::Reference< NeonSession > xSession;
+ sal_Int32 nLastChanceToSendRefreshRequest;
+
+ LockInfo()
+ : nLastChanceToSendRefreshRequest( -1 ) {}
+
+ LockInfo( rtl::Reference< NeonSession > const & _xSession,
+ sal_Int32 _nLastChanceToSendRefreshRequest )
+ : xSession( _xSession ),
+ nLastChanceToSendRefreshRequest( _nLastChanceToSendRefreshRequest ) {}
+
+};
+
+typedef std::map< NeonLock *, LockInfo > LockInfoMap;
+
+class NeonLockStore
+{
+ osl::Mutex m_aMutex;
+ ne_lock_store * m_pNeonLockStore;
+ rtl::Reference< TickerThread > m_pTickerThread;
+ LockInfoMap m_aLockInfoMap;
+ std::vector<NeonLock*> m_aRemoveDeferred;
+
+public:
+ NeonLockStore();
+ ~NeonLockStore();
+
+ void registerSession( HttpSession * pHttpSession );
+
+ NeonLock * findByUri( OUString const & rUri );
+
+ void addLock( NeonLock * pLock,
+ rtl::Reference< NeonSession > const & xSession,
+ // time in seconds since Jan 1 1970
+ // -1: infinite lock, no refresh
+ sal_Int32 nLastChanceToSendRefreshRequest );
+
+ void removeLock( NeonLock * pLock );
+ void removeLockDeferred(NeonLock* pLock);
+
+ void refreshLocks();
+
+private:
+ void startTicker();
+ void stopTicker(osl::ClearableMutexGuard & rGuard);
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONLOCKSTORE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonPropFindRequest.cxx b/ucb/source/ucp/webdav-neon/NeonPropFindRequest.cxx
new file mode 100644
index 000000000..cf03274c5
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonPropFindRequest.cxx
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <osl/mutex.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include "NeonSession.hxx"
+#include "NeonTypes.hxx"
+#include "DAVProperties.hxx"
+#include "NeonPropFindRequest.hxx"
+#include "LinkSequence.hxx"
+#include "LockSequence.hxx"
+#include "LockEntrySequence.hxx"
+#include "UCBDeadPropertyValue.hxx"
+#include <memory>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace std;
+using namespace webdav_ucp;
+
+
+namespace
+{
+ // strip "DAV:" namespace from XML snippets to avoid
+ // parser error (undeclared namespace) later on.
+ OString stripDavNamespace( const OString & in )
+ {
+ const OString inXML( in.toAsciiLowerCase() );
+
+ OStringBuffer buf;
+ sal_Int32 start = 0;
+ sal_Int32 end = inXML.indexOf( "dav:" );
+ while ( end != -1 )
+ {
+ if ( inXML[ end - 1 ] == '<' ||
+ inXML[ end - 1 ] == '/' )
+ {
+ // copy from original buffer - preserve case.
+ buf.append( in.copy( start, end - start ) );
+ }
+ else
+ {
+ // copy from original buffer - preserve case.
+ buf.append( in.copy( start, end - start + 4 ) );
+ }
+ start = end + 4;
+ end = inXML.indexOf( "dav:", start );
+ }
+ buf.append( inXML.copy( start ) );
+
+ return buf.makeStringAndClear();
+ }
+}
+
+extern "C" {
+
+static int NPFR_propfind_iter( void* userdata,
+ const NeonPropName* pname,
+ const char* value,
+ const HttpStatus* status )
+{
+ /*
+ HTTP Response Status Classes:
+
+ - 1: Informational - Request received, continuing process
+
+ - 2: Success - The action was successfully received,
+ understood, and accepted
+
+ - 3: Redirection - Further action must be taken in order to
+ complete the request
+
+ - 4: Client Error - The request contains bad syntax or cannot
+ be fulfilled
+
+ - 5: Server Error - The server failed to fulfill an apparently
+ valid request
+ */
+
+ if ( status->klass > 2 )
+ return 0; // Error getting this property. Go on.
+
+ // Create & set the PropertyValue
+ DAVPropertyValue thePropertyValue;
+ thePropertyValue.IsCaseSensitive = true;
+
+ SAL_WARN_IF( !pname->nspace, "ucb.ucp.webdav", "NPFR_propfind_iter - No XML namespace!" );
+
+ DAVProperties::createUCBPropName( pname->nspace,
+ pname->name,
+ thePropertyValue.Name );
+ bool bHasValue = false;
+ if ( DAVProperties::isUCBDeadProperty( *pname ) )
+ {
+ // DAV dead property added by WebDAV UCP?
+ if ( UCBDeadPropertyValue::createFromXML(
+ value, thePropertyValue.Value ) )
+ {
+ SAL_WARN_IF( !thePropertyValue.Value.hasValue(),
+ "ucb.ucp.webdav", "NPFR_propfind_iter - No value for UCBDeadProperty!" );
+ bHasValue = true;
+ }
+ }
+
+ if ( !bHasValue )
+ {
+ if ( rtl_str_compareIgnoreAsciiCase(
+ pname->name, "resourcetype" ) == 0 )
+ {
+ OString aValue( value );
+ aValue = aValue.trim(); // #107358# remove leading/trailing spaces
+ if ( !aValue.isEmpty() )
+ {
+ aValue = stripDavNamespace( aValue ).toAsciiLowerCase();
+ if ( aValue.startsWith("<collection") )
+ {
+ thePropertyValue.Value
+ <<= OUString("collection");
+ }
+ }
+
+ if ( !thePropertyValue.Value.hasValue() )
+ {
+ // Take over the value exactly as supplied by the server.
+ thePropertyValue.Value <<= OUString::createFromAscii( value );
+ }
+ }
+ else if ( rtl_str_compareIgnoreAsciiCase(
+ pname->name, "supportedlock" ) == 0 )
+ {
+ Sequence< LockEntry > aEntries;
+ LockEntrySequence::createFromXML(
+ stripDavNamespace( value ), aEntries );
+ thePropertyValue.Value <<= aEntries;
+ }
+ else if ( rtl_str_compareIgnoreAsciiCase(
+ pname->name, "lockdiscovery" ) == 0 )
+ {
+ Sequence< Lock > aLocks;
+ LockSequence::createFromXML(
+ stripDavNamespace( value ), aLocks );
+ thePropertyValue.Value <<= aLocks;
+ }
+ else if ( rtl_str_compareIgnoreAsciiCase( pname->name, "source" ) == 0 )
+ {
+ Sequence< Link > aLinks;
+ LinkSequence::createFromXML(
+ stripDavNamespace( value ), aLinks );
+ thePropertyValue.Value <<= aLinks;
+ }
+ else
+ {
+ thePropertyValue.Value
+ <<= OStringToOUString( value, RTL_TEXTENCODING_UTF8 );
+ }
+ }
+
+ // Add the newly created PropertyValue
+ DAVResource* theResource = static_cast< DAVResource * >( userdata );
+ theResource->properties.push_back( thePropertyValue );
+
+ return 0; // Go on.
+}
+
+static void NPFR_propfind_results( void* userdata,
+ const ne_uri* uri,
+ const NeonPropFindResultSet* set )
+{
+ // @@@ href is not the uri! DAVResource ctor wants uri!
+
+ DAVResource theResource(
+ OStringToOUString( uri->path, RTL_TEXTENCODING_UTF8 ) );
+
+ ne_propset_iterate( set, NPFR_propfind_iter, &theResource );
+
+ // Add entry to resources list.
+ vector< DAVResource > * theResources
+ = static_cast< vector< DAVResource > * >( userdata );
+ theResources->push_back( theResource );
+}
+
+static int NPFR_propnames_iter( void* userdata,
+ const NeonPropName* pname,
+ const char* /*value*/,
+ const HttpStatus* /*status*/ )
+{
+ OUString aFullName;
+ DAVProperties::createUCBPropName( pname->nspace,
+ pname->name,
+ aFullName );
+
+ DAVResourceInfo* theResource = static_cast< DAVResourceInfo * >( userdata );
+ theResource->properties.push_back( aFullName );
+ return 0;
+}
+
+static void NPFR_propnames_results( void* userdata,
+ const ne_uri* /*uri*/,
+ const NeonPropFindResultSet* results )
+{
+ // @@@ href is not the uri! DAVResourceInfo ctor wants uri!
+ // Create entry for the resource.
+ DAVResourceInfo theResource;
+
+ // Fill entry.
+ ne_propset_iterate( results, NPFR_propnames_iter, &theResource );
+
+ // Add entry to resources list.
+ vector< DAVResourceInfo > * theResources
+ = static_cast< vector< DAVResourceInfo > * >( userdata );
+ theResources->push_back( theResource );
+}
+
+}
+
+NeonPropFindRequest::NeonPropFindRequest( HttpSession* inSession,
+ const char* inPath,
+ const Depth inDepth,
+ const vector< OUString >& inPropNames,
+ vector< DAVResource >& ioResources,
+ int & nError )
+{
+ // Generate the list of properties we're looking for
+ int thePropCount = inPropNames.size();
+ if ( thePropCount > 0 )
+ {
+ std::unique_ptr<NeonPropName[]> thePropNames(new NeonPropName[ thePropCount + 1 ]);
+ int theIndex;
+
+ for ( theIndex = 0; theIndex < thePropCount; theIndex ++ )
+ {
+ // Split fullname into namespace and name!
+ DAVProperties::createNeonPropName(
+ inPropNames[ theIndex ], thePropNames[ theIndex ] );
+ }
+ thePropNames[ theIndex ].nspace = nullptr;
+ thePropNames[ theIndex ].name = nullptr;
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ nError = ne_simple_propfind( inSession,
+ inPath,
+ inDepth,
+ thePropNames.get(),
+ NPFR_propfind_results,
+ &ioResources );
+ }
+
+ for ( theIndex = 0; theIndex < thePropCount; theIndex ++ )
+ free( const_cast<char *>(thePropNames[ theIndex ].name) );
+ }
+ else
+ {
+ // ALLPROP
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ nError = ne_simple_propfind( inSession,
+ inPath,
+ inDepth,
+ nullptr, // 0 == allprop
+ NPFR_propfind_results,
+ &ioResources );
+ }
+
+ // #87585# - Sometimes neon lies (because some servers lie).
+ if ( ( nError == NE_OK ) && ioResources.empty() )
+ nError = NE_ERROR;
+}
+
+NeonPropFindRequest::NeonPropFindRequest(
+ HttpSession* inSession,
+ const char* inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ int & nError )
+{
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ nError = ne_propnames( inSession,
+ inPath,
+ inDepth,
+ NPFR_propnames_results,
+ &ioResInfo );
+ }
+
+ // #87585# - Sometimes neon lies (because some servers lie).
+ if ( ( nError == NE_OK ) && ioResInfo.empty() )
+ nError = NE_ERROR;
+}
+
+NeonPropFindRequest::~NeonPropFindRequest( )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonPropFindRequest.hxx b/ucb/source/ucp/webdav-neon/NeonPropFindRequest.hxx
new file mode 100644
index 000000000..b435a80ba
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonPropFindRequest.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONPROPFINDREQUEST_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONPROPFINDREQUEST_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "NeonTypes.hxx"
+#include "DAVTypes.hxx"
+#include "DAVResource.hxx"
+
+namespace webdav_ucp
+{
+
+class NeonPropFindRequest
+{
+public:
+ // named / allprop
+ NeonPropFindRequest( HttpSession* inSession,
+ const char* inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ int & nError );
+ // propnames
+ NeonPropFindRequest( HttpSession* inSession,
+ const char* inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ int & nError );
+
+ ~NeonPropFindRequest();
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONPROPFINDREQUEST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonSession.cxx b/ucb/source/ucp/webdav-neon/NeonSession.cxx
new file mode 100644
index 000000000..7fac08345
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonSession.cxx
@@ -0,0 +1,2361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+#include <unordered_map>
+#include <vector>
+#include <string.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/time.h>
+#include <ne_socket.h>
+#include <ne_auth.h>
+#include <ne_redirect.h>
+#include <ne_ssl.h>
+
+// old neon versions forgot to set this
+extern "C" {
+#include <ne_compress.h>
+}
+
+#include <libxml/parser.h>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/simplecertificatevalidationrequest.hxx>
+
+#include "DAVAuthListener.hxx"
+#include "NeonTypes.hxx"
+#include "NeonSession.hxx"
+#include "NeonInputStream.hxx"
+#include "NeonPropFindRequest.hxx"
+#include "NeonHeadRequest.hxx"
+#include "NeonUri.hxx"
+#include "LinkSequence.hxx"
+#include "UCBDeadPropertyValue.hxx"
+
+#include <officecfg/Inet.hxx>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/security/CertificateContainerStatus.hpp>
+#include <com/sun/star/security/CertificateContainer.hpp>
+#include <com/sun/star/security/XCertificateContainer.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+#ifndef EOL
+# define EOL "\r\n"
+#endif
+
+namespace {
+
+struct RequestData
+{
+ // POST
+ OUString aContentType;
+ OUString aReferer;
+
+ RequestData() {}
+ RequestData( const OUString & rContentType,
+ const OUString & rReferer )
+ : aContentType( rContentType ), aReferer( rReferer ) {}
+};
+
+struct equalPtr
+{
+ bool operator()( const ne_request* p1, const ne_request* p2 ) const
+ {
+ return p1 == p2;
+ }
+};
+
+struct hashPtr
+{
+ size_t operator()( const ne_request* p ) const
+ {
+ return reinterpret_cast<size_t>(p);
+ }
+};
+
+}
+
+typedef std::unordered_map
+<
+ ne_request*,
+ RequestData,
+ hashPtr,
+ equalPtr
+>
+RequestDataMap;
+
+static sal_uInt16 makeStatusCode( const OUString & rStatusText )
+{
+ // Extract status code from session error string. Unfortunately
+ // neon provides no direct access to the status code...
+
+ if ( rStatusText.getLength() < 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - status text string to short!" );
+ return 0;
+ }
+
+ sal_Int32 nPos = rStatusText.indexOf( ' ' );
+ if ( nPos == -1 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "makeStatusCode - wrong status text format!" );
+ return 0;
+ }
+
+ return sal_uInt16( rStatusText.copy( 0, nPos ).toInt32() );
+}
+
+static bool noKeepAlive( const uno::Sequence< beans::NamedValue >& rFlags )
+{
+ if ( !rFlags.hasElements() )
+ return false;
+
+ // find "KeepAlive" property
+ const beans::NamedValue* pValue(
+ std::find_if(rFlags.begin(), rFlags.end(),
+ [] (beans::NamedValue const& rNV) { return rNV.Name == "KeepAlive"; } ));
+ return pValue != rFlags.end() && !pValue->Value.get<bool>();
+}
+
+namespace {
+
+struct NeonRequestContext
+{
+ uno::Reference< io::XOutputStream > xOutputStream;
+ rtl::Reference< NeonInputStream > xInputStream;
+ const std::vector< OUString > * pHeaderNames;
+ DAVResource * pResource;
+
+ explicit NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm )
+ : xOutputStream( xOutStrm ),
+ pHeaderNames( nullptr ), pResource( nullptr ) {}
+
+ explicit NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm )
+ : xInputStream( xInStrm ),
+ pHeaderNames( nullptr ), pResource( nullptr ) {}
+
+ NeonRequestContext( uno::Reference< io::XOutputStream > const & xOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : xOutputStream( xOutStrm ),
+ pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
+
+ NeonRequestContext( const rtl::Reference< NeonInputStream > & xInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : xInputStream( xInStrm ),
+ pHeaderNames( &inHeaderNames ), pResource( &ioResource ) {}
+
+ void ResponseBlockReader(const char * inBuf, size_t inLen)
+ {
+ if (xInputStream.is())
+ xInputStream->AddToStream( inBuf, inLen );
+ }
+
+ void ResponseBlockWriter(const char * inBuf, size_t inLen)
+ {
+ if (xOutputStream.is())
+ {
+ const uno::Sequence< sal_Int8 > aSeq( reinterpret_cast<sal_Int8 const *>(inBuf), inLen );
+ xOutputStream->writeBytes( aSeq );
+ }
+ }
+
+};
+
+}
+
+// A simple Neon response_block_reader for use with an XInputStream
+extern "C" {
+
+static int NeonSession_ResponseBlockReader(void * inUserData,
+ const char * inBuf,
+ size_t inLen )
+{
+ // neon sometimes calls this function with (inLen == 0)...
+ if ( inLen > 0 )
+ {
+ NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
+ pCtx->ResponseBlockReader(inBuf, inLen);
+ }
+ return 0;
+}
+
+// A simple Neon response_block_reader for use with an XOutputStream
+static int NeonSession_ResponseBlockWriter( void * inUserData,
+ const char * inBuf,
+ size_t inLen )
+{
+ // neon calls this function with (inLen == 0)...
+ if ( inLen > 0 )
+ {
+ NeonRequestContext * pCtx = static_cast<NeonRequestContext*>(inUserData);
+ pCtx->ResponseBlockWriter(inBuf, inLen);
+ }
+ return 0;
+}
+
+static int NeonSession_NeonAuth( void * inUserData,
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ const char * inAuthProtocol,
+#endif
+ const char * inRealm,
+ int attempt,
+ char * inoutUserName,
+ char * inoutPassWord )
+{
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be copied into the buffers
+ * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
+ * on the first call to the callback, and increases by one each time
+ * an attempt to authenticate fails.
+ *
+ * The callback must return zero to indicate that authentication
+ * should be attempted with the username/password, or non-zero to
+ * cancel the request. (if non-zero, username and password are
+ * ignored.) */
+
+ NeonSession * theSession = static_cast<NeonSession*>(inUserData);
+ const char * pAuthProtocol;
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ pAuthProtocol = inAuthProtocol;
+#else
+ pAuthProtocol = nullptr;
+#endif
+ return theSession->NeonAuth(pAuthProtocol, inRealm, attempt, inoutUserName, inoutPassWord);
+}
+
+}
+
+int NeonSession::NeonAuth(const char* inAuthProtocol, const char* inRealm,
+ int attempt, char* inoutUserName, char * inoutPassWord)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+/* The callback used to request the username and password in the given
+ * realm. The username and password must be copied into the buffers
+ * which are both of size NE_ABUFSIZ. The 'attempt' parameter is zero
+ * on the first call to the callback, and increases by one each time
+ * an attempt to authenticate fails.
+ *
+ * The callback must return zero to indicate that authentication
+ * should be attempted with the username/password, or non-zero to
+ * cancel the request. (if non-zero, username and password are
+ * ignored.) */
+
+ DAVAuthListener * pListener
+ = getRequestEnvironment().m_xAuthListener.get();
+ if ( !pListener )
+ {
+ // abort
+ return -1;
+ }
+ OUString theUserName;
+ OUString thePassWord;
+
+ if ( attempt == 0 )
+ {
+ // neon does not handle username supplied with request URI (for
+ // instance when doing FTP over proxy - last checked: 0.23.5 )
+
+ try
+ {
+ NeonUri uri( getRequestEnvironment().m_aRequestURI );
+ const OUString& aUserInfo( uri.GetUserInfo() );
+ if ( !aUserInfo.isEmpty() )
+ {
+ sal_Int32 nPos = aUserInfo.indexOf( '@' );
+ if ( nPos == -1 )
+ {
+ theUserName = aUserInfo;
+ }
+ else
+ {
+ theUserName = aUserInfo.copy( 0, nPos );
+ thePassWord = aUserInfo.copy( nPos + 1 );
+ }
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // abort
+ return -1;
+ }
+ }
+ else
+ {
+ // username buffer is prefilled with user name from last attempt.
+ theUserName = OUString::createFromAscii( inoutUserName );
+ // @@@ Neon does not initialize password buffer (last checked: 0.22.0).
+ //thePassWord = OUString::createFromAscii( inoutPassWord );
+ }
+
+#if defined NE_FEATURE_SSPI && ! defined SYSTEM_NEON
+ const bool bCanUseSystemCreds
+ = (attempt == 0) && // avoid endless loops
+ ne_has_support( NE_FEATURE_SSPI ) && // Windows-only feature.
+ ( ( ne_strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
+ ( ne_strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
+#else
+ (void)inAuthProtocol;
+ const bool bCanUseSystemCreds = false;
+#endif
+
+ int theRetVal = pListener->authenticate(
+ OUString::createFromAscii( inRealm ),
+ getHostName(),
+ theUserName,
+ thePassWord,
+ bCanUseSystemCreds);
+
+ OString aUser( OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ) );
+ if ( aUser.getLength() > ( NE_ABUFSIZ - 1 ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - username too long!" );
+ return -1;
+ }
+
+ OString aPass( OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ) );
+ if ( aPass.getLength() > ( NE_ABUFSIZ - 1 ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "NeonSession_NeonAuth - password too long!" );
+ return -1;
+ }
+
+ // #100211# - checked
+ strcpy( inoutUserName, aUser.getStr() );
+ strcpy( inoutPassWord, aPass.getStr() );
+
+ return theRetVal;
+}
+
+namespace {
+ OUString GetHostnamePart( const OUString& _rRawString )
+ {
+ OUString sPart;
+ OUString sPartId("CN=");
+ sal_Int32 nContStart = _rRawString.indexOf( sPartId );
+ if ( nContStart != -1 )
+ {
+ nContStart += sPartId.getLength();
+ sal_Int32 nContEnd = _rRawString.indexOf( ',', nContStart );
+ sPart = nContEnd != -1 ?
+ _rRawString.copy(nContStart, nContEnd - nContStart) :
+ _rRawString.copy(nContStart);
+ }
+ return sPart;
+ }
+} // namespace
+
+extern "C" {
+
+static int NeonSession_CertificationNotify( void *userdata,
+ int,
+ const ne_ssl_certificate *cert )
+{
+ NeonSession * pSession = static_cast< NeonSession * >( userdata );
+ return pSession->CertificationNotify(cert);
+}
+
+}
+
+int NeonSession::CertificationNotify(const ne_ssl_certificate *cert)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ OSL_ASSERT( cert );
+
+ uno::Reference< security::XCertificateContainer > xCertificateContainer;
+ try
+ {
+ xCertificateContainer = security::CertificateContainer::create( getComponentContext() );
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( !xCertificateContainer.is() )
+ return 1;
+
+ char * dn = ne_ssl_readable_dname( ne_ssl_cert_subject( cert ) );
+ OUString cert_subject( dn, strlen( dn ), RTL_TEXTENCODING_UTF8, 0 );
+
+ ne_free( dn );
+
+ security::CertificateContainerStatus certificateContainer(
+ xCertificateContainer->hasCertificate(
+ getHostName(), cert_subject ) );
+
+ if ( certificateContainer != security::CertificateContainerStatus_NOCERT )
+ return
+ certificateContainer == security::CertificateContainerStatus_TRUSTED
+ ? 0
+ : 1;
+
+ uno::Reference< xml::crypto::XSEInitializer > xSEInitializer;
+ try
+ {
+ xSEInitializer = xml::crypto::SEInitializer::create( getComponentContext() );
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( !xSEInitializer.is() )
+ return 1;
+
+ uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext(
+ xSEInitializer->createSecurityContext( OUString() ) );
+
+ uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv(
+ xSecurityContext->getSecurityEnvironment() );
+
+ //The end entity certificate
+ char * eeCertB64 = ne_ssl_cert_export( cert );
+
+ OString sEECertB64( eeCertB64 );
+
+ uno::Reference< security::XCertificate > xEECert(
+ xSecurityEnv->createCertificateFromAscii(
+ OStringToOUString( sEECertB64, RTL_TEXTENCODING_ASCII_US ) ) );
+
+ ne_free( eeCertB64 );
+ eeCertB64 = nullptr;
+
+ std::vector< uno::Reference< security::XCertificate > > vecCerts;
+ const ne_ssl_certificate * issuerCert = cert;
+ do
+ {
+ //get the intermediate certificate
+ //the returned value is const ! Therefore it does not need to be freed
+ //with ne_ssl_cert_free, which takes a non-const argument
+ issuerCert = ne_ssl_cert_signedby( issuerCert );
+ if ( nullptr == issuerCert )
+ break;
+
+ char * imCertB64 = ne_ssl_cert_export( issuerCert );
+ OString sInterMediateCertB64( imCertB64 );
+ ne_free( imCertB64 );
+
+ uno::Reference< security::XCertificate> xImCert(
+ xSecurityEnv->createCertificateFromAscii(
+ OStringToOUString( sInterMediateCertB64, RTL_TEXTENCODING_ASCII_US ) ) );
+ if ( xImCert.is() )
+ vecCerts.push_back( xImCert );
+ }
+ while ( true );
+
+ sal_Int64 certValidity = xSecurityEnv->verifyCertificate( xEECert,
+ ::comphelper::containerToSequence( vecCerts ) );
+
+ if ( isDomainMatch(
+ GetHostnamePart( xEECert->getSubjectName() ) ) )
+ {
+ // if host name matched with certificate then look if the
+ // certificate was ok
+ if( certValidity == security::CertificateValidity::VALID )
+ return 0;
+ }
+
+ const uno::Reference< ucb::XCommandEnvironment > xEnv(
+ getRequestEnvironment().m_xEnv );
+ if ( xEnv.is() )
+ {
+ uno::Reference< task::XInteractionHandler > xIH(
+ xEnv->getInteractionHandler() );
+ if ( xIH.is() )
+ {
+ rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
+ xRequest( new ucbhelper::SimpleCertificateValidationRequest(
+ static_cast<sal_Int32>(certValidity), xEECert, getHostName() ) );
+ xIH->handle( xRequest.get() );
+
+ rtl::Reference< ucbhelper::InteractionContinuation > xSelection
+ = xRequest->getSelection();
+
+ if ( xSelection.is() )
+ {
+ uno::Reference< task::XInteractionApprove > xApprove(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( xApprove.is() )
+ {
+ xCertificateContainer->addCertificate(
+ getHostName(), cert_subject, true );
+ return 0;
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate(
+ getHostName(), cert_subject, false );
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate(
+ getHostName(), cert_subject, false );
+ return 1;
+ }
+ }
+ return 1;
+}
+
+extern "C" {
+
+static void NeonSession_PreSendRequest( ne_request * req,
+ void * userdata,
+ ne_buffer * headers )
+{
+ // userdata -> value returned by 'create'
+ NeonSession * pSession = static_cast< NeonSession * >( userdata );
+ if (!pSession)
+ return;
+ pSession->PreSendRequest(req, headers);
+}
+
+}
+
+void NeonSession::PreSendRequest(ne_request* req, ne_buffer* headers)
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ // If there is a proxy server in between, it shall never use
+ // cached data. We always want 'up-to-date' data.
+ ne_buffer_concat( headers, "Pragma: no-cache", EOL, nullptr );
+ // alternative, but understood by HTTP 1.1 servers only:
+ // ne_buffer_concat( headers, "Cache-Control: max-age=0", EOL, NULL );
+
+ const RequestDataMap * pRequestData
+ = static_cast< const RequestDataMap* >(
+ getRequestData() );
+
+ RequestDataMap::const_iterator it = pRequestData->find( req );
+ if ( it != pRequestData->end() )
+ {
+ if ( !(*it).second.aContentType.isEmpty() )
+ {
+ char * pData = headers->data;
+ if ( strstr( pData, "Content-Type:" ) == nullptr )
+ {
+ OString aType
+ = OUStringToOString( (*it).second.aContentType,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, "Content-Type: ",
+ aType.getStr(), EOL, nullptr );
+ }
+ }
+
+ if ( !(*it).second.aReferer.isEmpty() )
+ {
+ char * pData = headers->data;
+ if ( strstr( pData, "Referer:" ) == nullptr )
+ {
+ OString aReferer
+ = OUStringToOString( (*it).second.aReferer,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, "Referer: ",
+ aReferer.getStr(), EOL, nullptr );
+ }
+ }
+ }
+
+ const DAVRequestHeaders & rHeaders
+ = getRequestEnvironment().m_aRequestHeaders;
+
+ for ( const auto& rHeader : rHeaders )
+ {
+ OString aHeader
+ = OUStringToOString( rHeader.first,
+ RTL_TEXTENCODING_UTF8 );
+ OString aValue
+ = OUStringToOString( rHeader.second,
+ RTL_TEXTENCODING_UTF8 );
+ ne_buffer_concat( headers, aHeader.getStr(), ": ",
+ aValue.getStr(), EOL, nullptr );
+ }
+}
+
+//See https://bugzilla.redhat.com/show_bug.cgi?id=544619#c4
+//neon is threadsafe, but uses gnutls which is only thread-safe
+//if initialized to be thread-safe. cups, unfortunately, generally
+//initializes it first, and as non-thread-safe, leaving the entire
+//stack unsafe
+namespace webdav_ucp
+{
+ osl::Mutex& getGlobalNeonMutex()
+ {
+ static osl::Mutex aMutex;
+ return aMutex;
+ }
+}
+
+// static members
+bool NeonSession::m_bGlobalsInited = false;
+NeonLockStore NeonSession::m_aNeonLockStore;
+
+NeonSession::NeonSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
+ const OUString& inUri,
+ const uno::Sequence< beans::NamedValue >& rFlags,
+ const ucbhelper::InternetProxyDecider & rProxyDecider )
+ : DAVSession( rSessionFactory )
+ , m_nProxyPort( 0 )
+ , m_aFlags( rFlags )
+ , m_pHttpSession( nullptr )
+ , m_pRequestData( new RequestDataMap )
+ , m_rProxyDecider( rProxyDecider )
+{
+ NeonUri theUri( inUri );
+ m_aScheme = theUri.GetScheme();
+ m_aHostName = theUri.GetHost();
+ m_nPort = theUri.GetPort();
+ SAL_INFO( "ucb.ucp.webdav", "NeonSession ctor - URL <" << inUri << ">" );
+}
+
+NeonSession::~NeonSession( )
+{
+ if ( m_pHttpSession )
+ {
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ne_session_destroy( m_pHttpSession );
+ }
+ m_pHttpSession = nullptr;
+ }
+ delete static_cast< RequestDataMap * >( m_pRequestData );
+}
+
+void NeonSession::Init( const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ m_aEnv = rEnv;
+ Init();
+}
+
+void NeonSession::Init()
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ bool bCreateNewSession = m_bNeedNewSession;
+ m_bNeedNewSession = false;
+
+ if ( m_pHttpSession == nullptr )
+ {
+ // Ensure that Neon sockets are initialized
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ if (!m_bGlobalsInited )
+ {
+ if ( ne_sock_init() != 0 )
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ // #122205# - libxml2 needs to be initialized once if used by
+ // multithreaded programs like OOo.
+ xmlInitParser();
+#if OSL_DEBUG_LEVEL > 0
+ // for more debug flags see ne_utils.h; NE_DEBUGGING must be defined
+ // while compiling neon in order to actually activate neon debug
+ // output.
+ ne_debug_init( stderr, NE_DBG_FLUSH
+ | NE_DBG_HTTP
+ // | NE_DBG_HTTPBODY
+ // | NE_DBG_HTTPAUTH
+ // | NE_DBG_XML
+ // | NE_DBG_XMLPARSE
+ | NE_DBG_LOCKS
+ | NE_DBG_SSL
+ );
+#endif
+ m_bGlobalsInited = true;
+ }
+
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ // Not yet initialized. Create new session.
+ bCreateNewSession = true;
+ }
+ else
+ {
+ // #112271# Check whether proxy settings are still valid (They may
+ // change at any time). If not, create new Neon session.
+
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ if ( ( rProxyCfg.aName != m_aProxyName )
+ || ( rProxyCfg.nPort != m_nProxyPort ) )
+ {
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ bCreateNewSession = true;
+ }
+
+ if (bCreateNewSession)
+ {
+ // new session needed, destroy old first
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ne_session_destroy( m_pHttpSession );
+ }
+ m_pHttpSession = nullptr;
+ }
+ }
+
+ if ( !bCreateNewSession )
+ return;
+
+ const sal_Int32 nConnectTimeoutMax = 180;
+ const sal_Int32 nConnectTimeoutMin = 2;
+ const sal_Int32 nReadTimeoutMax = 180;
+ const sal_Int32 nReadTimeoutMin = 20;
+
+ // @@@ For FTP over HTTP proxy inUserInfo is needed to be able to
+ // build the complete request URI (including user:pass), but
+ // currently (0.22.0) neon does not allow to pass the user info
+ // to the session
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ m_pHttpSession = ne_session_create(
+ OUStringToOString( m_aScheme, RTL_TEXTENCODING_UTF8 ).getStr(),
+ /* theUri.GetUserInfo(),
+ @@@ for FTP via HTTP proxy, but not supported by Neon */
+ OUStringToOString( m_aHostName, RTL_TEXTENCODING_UTF8 ).getStr(),
+ m_nPort );
+ }
+
+ if ( m_pHttpSession == nullptr )
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ // Register the session with the lock store
+ m_aNeonLockStore.registerSession( m_pHttpSession );
+
+ if ( m_aScheme.equalsIgnoreAsciiCase("https") )
+ {
+ // Set a failure callback for certificate check
+ ne_ssl_set_verify(
+ m_pHttpSession, NeonSession_CertificationNotify, this);
+
+ // Tell Neon to tell the SSL library used (OpenSSL or
+ // GnuTLS, I guess) to use a default set of root
+ // certificates.
+ ne_ssl_trust_default_ca(m_pHttpSession);
+ }
+
+ // Add hooks (i.e. for adding additional headers to the request)
+ ne_hook_pre_send( m_pHttpSession, NeonSession_PreSendRequest, this );
+
+ if ( !m_aProxyName.isEmpty() )
+ {
+ ne_session_proxy( m_pHttpSession,
+ OUStringToOString(
+ m_aProxyName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ m_nProxyPort );
+ }
+
+ // avoid KeepAlive?
+ if ( noKeepAlive(m_aFlags) )
+ ne_set_session_flag( m_pHttpSession, NE_SESSFLAG_PERSIST, 0 );
+
+ // Register for redirects.
+ ne_redirect_register( m_pHttpSession );
+
+ // authentication callbacks.
+ ne_add_server_auth( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
+ ne_add_proxy_auth ( m_pHttpSession, NE_AUTH_ALL, NeonSession_NeonAuth, this );
+
+ // set timeout to connect
+ // if connect_timeout is not set, neon returns NE_CONNECT when the TCP socket default
+ // timeout elapses
+ // with connect_timeout set neon returns NE_TIMEOUT if elapsed when the connection
+ // didn't succeed
+ // grab it from configuration
+ uno::Reference< uno::XComponentContext > rContext = m_xFactory->getComponentContext();
+
+ // set the timeout (in seconds) used when making a connection
+ sal_Int32 nConnectTimeout = officecfg::Inet::Settings::ConnectTimeout::get( rContext );
+ ne_set_connect_timeout( m_pHttpSession,
+ std::max( nConnectTimeoutMin,
+ std::min( nConnectTimeout, nConnectTimeoutMax ) ) );
+
+ // provides a read time out facility as well
+ // set the timeout (in seconds) used when reading from a socket.
+ sal_Int32 nReadTimeout = officecfg::Inet::Settings::ReadTimeout::get( rContext );
+ ne_set_read_timeout( m_pHttpSession,
+ std::max( nReadTimeoutMin,
+ std::min( nReadTimeout, nReadTimeoutMax ) ) );
+
+}
+
+bool NeonSession::CanUse( const OUString & inUri,
+ const uno::Sequence< beans::NamedValue >& rFlags )
+{
+ try
+ {
+ NeonUri theUri( inUri );
+ if ( ( theUri.GetPort() == m_nPort ) &&
+ ( theUri.GetHost() == m_aHostName ) &&
+ ( theUri.GetScheme() == m_aScheme ) &&
+ ( rFlags == m_aFlags ) )
+ return true;
+ }
+ catch ( DAVException const & )
+ {
+ return false;
+ }
+ return false;
+}
+
+bool NeonSession::UsesProxy()
+{
+ Init();
+ return !m_aProxyName.isEmpty() ;
+}
+
+void NeonSession::OPTIONS( const OUString & inPath,
+ DAVOptions & rOptions, // contains the name+values of every header
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ SAL_INFO( "ucb.ucp.webdav", "OPTIONS - relative URL <" << inPath << ">" );
+
+ rOptions.init();
+
+ Init( rEnv );
+ int theRetVal;
+
+ ne_request *req = ne_request_create(m_pHttpSession, "OPTIONS", OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr());
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ theRetVal = ne_request_dispatch(req);
+ }
+
+ //check if http error is in the 200 class (no error)
+ if (theRetVal == NE_OK && ne_get_status(req)->klass != 2) {
+ theRetVal = NE_ERROR;
+ }
+
+ if ( theRetVal == NE_OK )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ OUString aHeaderName(OUString(name, strlen(name), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase());
+ OUString aHeaderValue(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
+
+ // display the single header
+ SAL_INFO( "ucb.ucp.webdav", "OPTIONS - received header: " << aHeaderName << ":" << aHeaderValue );
+
+ if ( aHeaderName == "allow" )
+ {
+ rOptions.setAllowedMethods( aHeaderValue );
+ }
+ else if ( aHeaderName == "dav" )
+ {
+ // check type of dav capability
+ // need to parse the value, token separator: ","
+ // see <http://tools.ietf.org/html/rfc4918#section-10.1>,
+ // <http://tools.ietf.org/html/rfc4918#section-18>,
+ // and <http://tools.ietf.org/html/rfc7230#section-3.2>
+ // we detect the class (1, 2 and 3), other elements (token, URL)
+ // are not used for now
+ // silly parser written using OUString, not very efficient
+ // but quick and easy to write...
+ sal_Int32 nFromIndex = 0;
+ sal_Int32 nNextIndex = 0;
+ while( ( nNextIndex = aHeaderValue.indexOf( ",", nFromIndex ) ) != -1 )
+ { // found a comma
+ // try to convert from nFromIndex to nNextIndex -1 in a number
+ // if this is 1 or 2 or 3, use for class setting
+ sal_Int32 nClass =
+ aHeaderValue.copy( nFromIndex, nNextIndex - nFromIndex ).toInt32();
+ switch( nClass )
+ {
+ case 1:
+ rOptions.setClass1();
+ break;
+ case 2:
+ rOptions.setClass2();
+ break;
+ case 3:
+ rOptions.setClass3();
+ break;
+ default:
+ ;
+ }
+ // next starting point
+ nFromIndex = nNextIndex + 1;
+ }
+ // check for last fragment
+ if ( nFromIndex < aHeaderValue.getLength() )
+ {
+ sal_Int32 nClass = aHeaderValue.copy( nFromIndex ).toInt32();
+ switch( nClass )
+ {
+ case 1:
+ rOptions.setClass1();
+ break;
+ case 2:
+ rOptions.setClass2();
+ break;
+ case 3:
+ rOptions.setClass3();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ // if applicable, check for lock state:
+ if( rOptions.isClass2() || rOptions.isClass3() )
+ {
+ //dav with lock possible, check for locked state
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) != nullptr )
+ {
+ // we own a lock for this URL,
+ // set locked state
+ rOptions.setLocked();
+ }
+ }
+ }
+
+ ne_request_destroy(req);
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv )
+{
+
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+#if defined SAL_LOG_INFO
+ { //debug
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
+ for(const auto& rPropName : inPropNames)
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - property requested: " << rPropName );
+ }
+ } //debug
+#endif
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonPropFindRequest theRequest( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ inDepth,
+ inPropNames,
+ ioResources,
+ theRetVal );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - relative URL: <" << inPath << "> Depth: " << inDepth );
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonPropFindRequest theRequest( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ inDepth,
+ ioResInfo,
+ theRetVal );
+
+#if defined SAL_LOG_INFO
+ { //debug
+ for ( const auto& rResInfo : ioResInfo )
+ {
+ for ( const auto& rProp : rResInfo.properties )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - returned property (name only): " << rProp );
+ }
+ }
+ } //debug
+#endif
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv )
+{
+ SAL_INFO( "ucb.ucp.webdav", "PROPPATCH - relative URL <" << inPath << ">" );
+
+ /* @@@ Which standard live properties can be set by the client?
+ This is a known WebDAV RFC issue ( verified: 04/10/2001 )
+ --> http://www.ics.uci.edu/pub/ietf/webdav/protocol/issues.html
+
+ mod_dav implementation:
+
+ creationdate r ( File System prop )
+ displayname w
+ getcontentlanguage r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
+ getcontentlength r ( File System prop )
+ getcontenttype r ( #ifdef DAV_DISABLE_WRITEABLE_PROPS )
+ getetag r ( File System prop )
+ getlastmodified r ( File System prop )
+ lockdiscovery r
+ resourcetype r
+ source w
+ supportedlock r
+ executable w ( #ifndef WIN32 )
+
+ All dead properties are of course writable.
+ */
+
+ int theRetVal = NE_OK;
+
+ int n; // for the "for" loop
+
+ // Generate the list of properties we want to set.
+ int nPropCount = inValues.size();
+ std::unique_ptr<ne_proppatch_operation[]> pItems(
+ new ne_proppatch_operation[ nPropCount + 1 ]);
+ for ( n = 0; n < nPropCount; ++n )
+ {
+ const ProppatchValue & rValue = inValues[ n ];
+
+ // Split fullname into namespace and name!
+ ne_propname * pName = new ne_propname;
+ DAVProperties::createNeonPropName( rValue.name, *pName );
+ pItems[ n ].name = pName;
+
+ if ( rValue.operation == PROPSET )
+ {
+ pItems[ n ].type = ne_propset;
+
+ OUString aStringValue;
+ if ( DAVProperties::isUCBDeadProperty( *pName ) )
+ {
+ // DAV dead property added by WebDAV UCP?
+ if ( !UCBDeadPropertyValue::toXML( rValue.value,
+ aStringValue ) )
+ {
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ else if ( !( rValue.value >>= aStringValue ) )
+ {
+ // complex properties...
+ if ( rValue.name == DAVProperties::SOURCE )
+ {
+ uno::Sequence< ucb::Link > aLinks;
+ if ( rValue.value >>= aLinks )
+ {
+ LinkSequence::toXML( aLinks, aStringValue );
+ }
+ else
+ {
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "PROPPATCH - Unsupported type!" );
+ // Error!
+ pItems[ n ].value = nullptr;
+ theRetVal = NE_ERROR;
+ nPropCount = n + 1;
+ break;
+ }
+ }
+ pItems[ n ].value
+ = strdup( OUStringToOString( aStringValue,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else
+ {
+ pItems[ n ].type = ne_propremove;
+ pItems[ n ].value = nullptr;
+ }
+ }
+
+ if ( theRetVal == NE_OK )
+ {
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ pItems[ n ].name = nullptr;
+
+ theRetVal = ne_proppatch( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ pItems.get() );
+ }
+
+ for ( n = 0; n < nPropCount; ++n )
+ {
+ free( const_cast<char *>(pItems[ n ].name->name) );
+ delete pItems[ n ].name;
+ free( const_cast<char *>(pItems[ n ].value) );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "HEAD - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = NE_OK;
+ NeonHeadRequest theRequest( m_pHttpSession,
+ inPath,
+ inHeaderNames,
+ ioResource,
+ theRetVal );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockReader,
+ false,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::GET( const OUString & inPath,
+ uno::Reference< io::XOutputStream > & ioOutputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ NeonRequestContext aCtx( ioOutputStream );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockWriter,
+ false,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockReader,
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::GET0( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream, inHeaderNames, ioResource );
+ int theRetVal = GET0( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::GET( const OUString & inPath,
+ uno::Reference< io::XOutputStream > & ioOutputStream,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "GET - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+
+ NeonRequestContext aCtx( ioOutputStream, inHeaderNames, ioResource );
+ int theRetVal = GET( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ NeonSession_ResponseBlockWriter,
+ true,
+ &aCtx );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::PUT( const OUString & inPath,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "PUT - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ int theRetVal = PUT( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ aDataToSend.getLength() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+uno::Reference< io::XInputStream >
+NeonSession::POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ rtl::Reference< NeonInputStream > xInputStream( new NeonInputStream );
+ NeonRequestContext aCtx( xInputStream );
+ int theRetVal = POST( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ NeonSession_ResponseBlockReader,
+ &aCtx,
+ rContentType,
+ rReferer );
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+void NeonSession::POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ uno::Reference< io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "POST - relative URL <" << inPath << ">" );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ Init( rEnv );
+
+ NeonRequestContext aCtx( oOutputStream );
+ int theRetVal = POST( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr(),
+ reinterpret_cast< const char * >(
+ aDataToSend.getConstArray() ),
+ NeonSession_ResponseBlockWriter,
+ &aCtx,
+ rContentType,
+ rReferer );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "MKCOL - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = ne_mkcol( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+void NeonSession::COPY( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "COPY - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
+
+ Init( rEnv );
+
+ NeonUri theSourceUri( inSourceURL );
+ NeonUri theDestinationUri( inDestinationURL );
+
+ int theRetVal = ne_copy( m_pHttpSession,
+ inOverWrite ? 1 : 0,
+ NE_DEPTH_INFINITE,
+ OUStringToOString(
+ theSourceUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString(
+ theDestinationUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inSourceURL, rEnv );
+}
+
+void NeonSession::MOVE( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "MOVE - inSourceURL: "<<inSourceURL<<" inDestinationURL: "<<inDestinationURL);
+
+ Init( rEnv );
+
+ NeonUri theSourceUri( inSourceURL );
+ NeonUri theDestinationUri( inDestinationURL );
+ int theRetVal = ne_move( m_pHttpSession,
+ inOverWrite ? 1 : 0,
+ OUStringToOString(
+ theSourceUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString(
+ theDestinationUri.GetPath(),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inSourceURL, rEnv );
+}
+
+void NeonSession::DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "DESTROY - relative URL <" << inPath << ">" );
+
+ Init( rEnv );
+
+ int theRetVal = ne_delete( m_pHttpSession,
+ OUStringToOString(
+ inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+namespace
+{
+ sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
+ int timeout )
+ {
+ TimeValue aEnd;
+ osl_getSystemTime( &aEnd );
+
+ // Try to estimate a safe absolute time for sending the
+ // lock refresh request.
+ sal_Int32 lastChanceToSendRefreshRequest = -1;
+ if ( timeout != NE_TIMEOUT_INFINITE )
+ {
+ sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
+ if ( calltime <= timeout )
+ {
+ lastChanceToSendRefreshRequest = rStart.Seconds + timeout;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "LOCK - no chance to refresh lock before timeout!" );
+ }
+ }
+ return lastChanceToSendRefreshRequest;
+ }
+
+} // namespace
+
+// Set new lock
+void NeonSession::LOCK( const OUString & inPath,
+ ucb::Lock & rLock,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - relative URL: <" << inPath << ">" );
+
+ // before issuing the lock command,
+ // better check first if we already have one on this href
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) != nullptr )
+ {
+ // 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
+ * (allowing a server to pick a default). token, owner and uri are
+ * unset. */
+ NeonLock * theLock = ne_lock_create();
+
+ // Set the lock uri
+ ne_uri aUri;
+ ne_uri_parse( OUStringToOString( makeAbsoluteURL( inPath ),
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ &aUri );
+ theLock->uri = aUri;
+
+ // Set the lock depth
+ switch( rLock.Depth )
+ {
+ case ucb::LockDepth_ZERO:
+ theLock->depth = NE_DEPTH_ZERO;
+ break;
+ case ucb::LockDepth_ONE:
+ theLock->depth = NE_DEPTH_ONE;
+ break;
+ case ucb::LockDepth_INFINITY:
+ theLock->depth = NE_DEPTH_INFINITE;
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ // Set the lock scope
+ switch ( rLock.Scope )
+ {
+ case ucb::LockScope_EXCLUSIVE:
+ theLock->scope = ne_lockscope_exclusive;
+ break;
+ case ucb::LockScope_SHARED:
+ theLock->scope = ne_lockscope_shared;
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ // Set the lock timeout
+ theLock->timeout = static_cast<long>(rLock.Timeout);
+
+ // Set the lock owner
+ OUString aValue;
+ rLock.Owner >>= aValue;
+ theLock->owner =
+ ne_strdup( OUStringToOString( aValue,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ TimeValue startCall;
+ osl_getSystemTime( &startCall );
+
+ int theRetVal = ne_lock( m_pHttpSession, theLock );
+
+ if ( theRetVal == NE_OK )
+ {
+ m_aNeonLockStore.addLock( theLock,
+ this,
+ lastChanceToSendRefreshRequest(
+ startCall, theLock->timeout ) );
+
+ uno::Sequence< OUString > aTokens( 1 );
+ aTokens[ 0 ] = OUString::createFromAscii( theLock->token );
+ rLock.LockTokens = aTokens;
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Created lock for <" << makeAbsoluteURL( inPath )
+ << "> token: <" << theLock->token << "> timeout: " << theLock->timeout << " sec.");
+ }
+ else
+ {
+ ne_lock_destroy( theLock );
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (create) - Obtaining lock for <"
+ << makeAbsoluteURL( inPath ) << " failed!" );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+// Refresh existing lock
+bool NeonSession::LOCK( NeonLock * pLock,
+ sal_Int32 & rlastChanceToSendRefreshRequest )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+ }
+#endif
+
+ // refresh existing lock.
+
+ TimeValue startCall;
+ osl_getSystemTime( &startCall );
+
+ // save the current requested timeout, because ne_lock_refresh uses
+ // pLock->timeout as an out parameter. This prevents a feedback-loop,
+ // where we would request a shorter timeout on each refresh.
+ long timeout = pLock->timeout;
+ const int theRetVal = ne_lock_refresh(m_pHttpSession, pLock);
+ if (theRetVal == NE_OK)
+ {
+ rlastChanceToSendRefreshRequest
+ = lastChanceToSendRefreshRequest( startCall, pLock->timeout );
+
+ SAL_INFO( "ucb.ucp.webdav", "LOCK (refresh) - Lock successfully refreshed." );
+ pLock->timeout = timeout;
+ return true;
+ }
+ else
+ {
+#if defined SAL_LOG_WARN
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_WARN( "ucb.ucp.webdav", "LOCK (refresh) - not refreshed! Relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+#endif
+ if (theRetVal == NE_AUTH)
+ {
+ // tdf#126279: see handling of NE_AUTH in HandleError
+ m_bNeedNewSession = true;
+ m_aNeonLockStore.removeLockDeferred(pLock);
+ }
+ return false;
+ }
+}
+
+void NeonSession::UNLOCK( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ // get the neon lock from lock store
+ NeonLock * theLock
+ = m_aNeonLockStore.findByUri( makeAbsoluteURL( inPath ) );
+ if ( !theLock )
+ throw DAVException( DAVException::DAV_NOT_LOCKED );
+
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK - relative URL: <" << inPath << "> token: <" << theLock->token << ">" );
+ Init( rEnv );
+
+ int theRetVal = ne_unlock( m_pHttpSession, theLock );
+
+ if ( theRetVal == NE_OK )
+ {
+ m_aNeonLockStore.removeLock( theLock );
+ ne_lock_destroy( theLock );
+ }
+ else
+ {
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK - Unlocking of <"
+ << makeAbsoluteURL( inPath ) << "> failed." );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+}
+
+bool NeonSession::UNLOCK( NeonLock * pLock )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << ">" );
+ ne_free( p );
+ }
+#endif
+
+ const int theRetVal = ne_unlock(m_pHttpSession, pLock);
+ if (theRetVal == NE_OK)
+ {
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> succeeded." );
+ ne_free( p );
+ }
+#endif
+ return true;
+ }
+ else
+ {
+#if defined SAL_LOG_INFO
+ {
+ char * p = ne_uri_unparse( &(pLock->uri) );
+ SAL_INFO( "ucb.ucp.webdav", "UNLOCK (from store) - relative URL: <" << p << "> token: <" << pLock->token << "> failed!" );
+ ne_free( p );
+ }
+#endif
+ if (theRetVal == NE_AUTH)
+ {
+ // tdf#126279: see handling of NE_AUTH in HandleError
+ m_bNeedNewSession = true;
+ }
+ return false;
+ }
+}
+
+void NeonSession::abort()
+{
+ SAL_INFO( "ucb.ucp.webdav", "neon commands cannot be aborted" );
+}
+
+ucbhelper::InternetProxyServer NeonSession::getProxySettings() const
+{
+ if ( m_aScheme == "http" || m_aScheme == "https" )
+ {
+ return m_rProxyDecider.getProxy( m_aScheme,
+ m_aHostName,
+ m_nPort );
+ }
+ else
+ {
+ return m_rProxyDecider.getProxy( m_aScheme,
+ OUString() /* not used */,
+ -1 /* not used */ );
+ }
+}
+
+namespace {
+
+bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
+ const char * token )
+{
+ return std::any_of(rLocks.begin(), rLocks.end(), [&token](const ucb::Lock& rLock) {
+ const uno::Sequence< OUString > & rTokens = rLock.LockTokens;
+ return std::any_of(rTokens.begin(), rTokens.end(),
+ [&token](const OUString& rToken) { return rToken.equalsAscii( token ); });
+ });
+}
+
+} // namespace
+
+bool NeonSession::removeExpiredLocktoken( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv )
+{
+ NeonLock * theLock = m_aNeonLockStore.findByUri( inURL );
+ if ( !theLock )
+ return false;
+
+ // do a lockdiscovery to check whether this lock is still valid.
+ try
+ {
+ // @@@ Alternative: use ne_lock_discover() => less overhead
+
+ std::vector< DAVResource > aResources;
+ std::vector< OUString > aPropNames;
+ aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
+
+ PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
+
+ if ( aResources.empty() )
+ return false;
+
+ for ( const auto& rProp : aResources[ 0 ].properties )
+ {
+ if ( rProp.Name == DAVProperties::LOCKDISCOVERY )
+ {
+ uno::Sequence< ucb::Lock > aLocks;
+ if ( !( rProp.Value >>= aLocks ) )
+ return false;
+
+ if ( !containsLocktoken( aLocks, theLock->token ) )
+ {
+ // expired!
+ break;
+ }
+
+ // still valid.
+ return false;
+ }
+ }
+
+ // No lockdiscovery prop in propfind result / locktoken not found
+ // in propfind result -> not locked
+ SAL_WARN( "ucb.ucp.webdav", "Removing expired lock token for <" << inURL
+ << "> token: " << theLock->token );
+
+ m_aNeonLockStore.removeLock( theLock );
+ ne_lock_destroy( theLock );
+ return true;
+ }
+ catch ( DAVException const & )
+ {
+ }
+ return false;
+}
+
+// Common error handler
+void NeonSession::HandleError( int nError,
+ const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ // Map error code to DAVException.
+ switch ( nError )
+ {
+ case NE_OK:
+ return;
+
+ case NE_ERROR: // Generic error
+ {
+ const char* sErr = ne_get_error(m_pHttpSession);
+ OUString aText(sErr, strlen(sErr), osl_getThreadTextEncoding());
+
+ sal_uInt16 code = makeStatusCode( aText );
+
+ SAL_WARN( "ucb.ucp.webdav", "Neon returned NE_ERROR, http response status code was: " << code << " '" << aText << "'" );
+ if ( SC_BAD_REQUEST <= code && code < SC_INTERNAL_SERVER_ERROR )
+ {
+ // error codes in the range 4xx
+ switch ( code )
+ {
+ case SC_LOCKED:
+ {
+ if ( m_aNeonLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) == nullptr )
+ {
+ // locked by 3rd party
+ throw DAVException( DAVException::DAV_LOCKED );
+ }
+ else
+ {
+ // locked by ourself
+ throw DAVException( DAVException::DAV_LOCKED_SELF );
+ }
+ }
+ break;
+ case SC_PRECONDITION_FAILED:
+ case SC_BAD_REQUEST:
+ {
+ // Special handling for 400 and 412 status codes, which may indicate
+ // that a lock previously obtained by us has been released meanwhile
+ // by the server. Unfortunately, RFC is not clear at this point,
+ // thus server implementations behave different...
+ if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
+ throw DAVException( DAVException::DAV_LOCK_EXPIRED );
+ }
+ break;
+ case SC_REQUEST_TIMEOUT:
+ {
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+ }
+ break;
+ case SC_UNAUTHORIZED: // User authentication failed on server
+ {
+ throw DAVException( DAVException::DAV_HTTP_AUTH,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+ }
+ break;
+ case SC_GONE:
+ case SC_LENGTH_REQUIRED:
+ case SC_REQUEST_ENTITY_TOO_LARGE:
+ case SC_REQUEST_URI_TOO_LONG:
+ case SC_UNSUPPORTED_MEDIA_TYPE:
+ case SC_REQUESTED_RANGE_NOT_SATISFIABLE:
+ case SC_EXPECTATION_FAILED:
+ case SC_UNPROCESSABLE_ENTITY:
+ case SC_FAILED_DEPENDENCY:
+ case SC_CONFLICT:
+ case SC_NOT_ACCEPTABLE:
+ case SC_PAYMENT_REQUIRED:
+ case SC_PROXY_AUTHENTICATION_REQUIRED:
+ default:
+ // set 400 error, if not one of others
+ code = SC_BAD_REQUEST;
+ [[fallthrough]];
+ case SC_FORBIDDEN:
+ case SC_NOT_FOUND:
+ case SC_METHOD_NOT_ALLOWED:
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ break;
+ }
+ }
+ else if ( SC_INTERNAL_SERVER_ERROR <= code )
+ {
+ // deal with HTTP response status codes higher then 500
+ // error codes in the range 5xx, server errors
+ // but there exists unofficial code in the range 1000 and up
+ // for example see:
+ // <https://support.cloudflare.com/hc/en-us/sections/200820298-Error-Pages> (retrieved 2016-10-05)
+ switch ( code )
+ {
+ // the error codes case before the default case are not actively
+ // managed by LO
+ case SC_BAD_GATEWAY:
+ case SC_SERVICE_UNAVAILABLE:
+ case SC_GATEWAY_TIMEOUT:
+ case SC_HTTP_VERSION_NOT_SUPPORTED:
+ case SC_INSUFFICIENT_STORAGE:
+ default:
+ // set 500 error, if not one of others
+ // expand the error code
+ code = SC_INTERNAL_SERVER_ERROR;
+ [[fallthrough]];
+ case SC_INTERNAL_SERVER_ERROR:
+ case SC_NOT_IMPLEMENTED:
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ break;
+ }
+ }
+ else
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ }
+ break;
+ 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 ) );
+
+ case NE_AUTH: // User authentication failed on server
+ // m_pHttpSession could get invalidated, e.g., as result of clean_session called in
+ // ah_post_send in case when auth_challenge failed, which invalidates the auth_session
+ // which we established in Init(): the auth_session's sspi_host gets disposed, and
+ // next attempt to authenticate would crash in continue_sspi trying to dereference it
+ m_bNeedNewSession = true;
+ throw DAVException( DAVException::DAV_HTTP_AUTH,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_PROXYAUTH: // User authentication failed on proxy
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_AUTHPROXY" );
+ throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
+ NeonUri::makeConnectionEndPointString(
+ m_aProxyName, m_nProxyPort ) );
+
+ case NE_CONNECT: // Could not connect to server
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_CONNECT" );
+ throw DAVException( DAVException::DAV_HTTP_CONNECT,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_TIMEOUT: // Connection timed out
+ SAL_WARN( "ucb.ucp.webdav", "DAVException::DAV_HTTP_TIMEOUT" );
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ NeonUri::makeConnectionEndPointString(
+ 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 ) );
+
+ case NE_RETRY: // Retry request (ne_end_request ONLY)
+ throw DAVException( DAVException::DAV_HTTP_RETRY,
+ NeonUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_REDIRECT:
+ {
+ NeonUri aUri( ne_redirect_location( m_pHttpSession ) );
+ SAL_INFO( "ucb.ucp.webdav", "DAVException::DAV_HTTP_REDIRECT: new URI: " << aUri.GetURI() );
+ throw DAVException(
+ DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
+ }
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Unknown Neon error code!" );
+ const char* sErr = ne_get_error(m_pHttpSession);
+ throw DAVException( DAVException::DAV_HTTP_ERROR,
+ OUString(sErr, strlen(sErr), osl_getThreadTextEncoding()) );
+ }
+ }
+}
+
+namespace {
+
+void runResponseHeaderHandler( void * userdata,
+ const char * value )
+{
+ OUString aHeader(value, strlen(value), RTL_TEXTENCODING_ASCII_US);
+ sal_Int32 nPos = aHeader.indexOf( ':' );
+
+ if ( nPos != -1 )
+ {
+ OUString aHeaderName( aHeader.copy( 0, nPos ) );
+
+ NeonRequestContext * pCtx
+ = static_cast< NeonRequestContext * >( userdata );
+
+ // Note: Empty vector means that all headers are requested.
+ bool bIncludeIt = pCtx->pHeaderNames->empty();
+
+ if ( !bIncludeIt )
+ {
+ // Check whether this header was requested.
+ auto it = std::find_if(pCtx->pHeaderNames->cbegin(), pCtx->pHeaderNames->cend(),
+ [&aHeaderName](const OUString& rName) {
+ // header names are case insensitive
+ return rName.equalsIgnoreAsciiCase( aHeaderName ); });
+
+ if ( it != pCtx->pHeaderNames->end() )
+ {
+ aHeaderName = *it;
+ bIncludeIt = true;
+ }
+ }
+
+ if ( bIncludeIt )
+ {
+ // Create & set the PropertyValue
+ DAVPropertyValue thePropertyValue;
+ // header names are case insensitive, so are the
+ // corresponding property names.
+ thePropertyValue.Name = aHeaderName.toAsciiLowerCase();
+ thePropertyValue.IsCaseSensitive = false;
+
+ if ( nPos < aHeader.getLength() )
+ thePropertyValue.Value <<= aHeader.copy( nPos + 1 ).trim();
+
+ // Add the newly created PropertyValue
+ pCtx->pResource->properties.push_back( thePropertyValue );
+ }
+ }
+}
+
+} // namespace
+
+int NeonSession::GET( ne_session * sess,
+ const char * uri,
+ ne_block_reader reader,
+ bool getheaders,
+ void * userdata )
+{
+ //struct get_context ctx;
+ ne_request * req = ne_request_create( sess, "GET", uri );
+ int ret;
+
+ ne_decompress * dc
+ = ne_decompress_reader( req, ne_accept_2xx, reader, userdata );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( getheaders )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ char buffer[8192];
+
+ SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
+ ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
+ runResponseHeaderHandler(userdata, buffer);
+ }
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ if ( dc != nullptr )
+ ne_decompress_destroy(dc);
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::GET0( ne_session * sess,
+ const char * uri,
+ bool getheaders,
+ void * userdata )
+{
+ //struct get_context ctx;
+ ne_request * req = ne_request_create( sess, "GET", uri );
+ int ret;
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( getheaders )
+ {
+ void *cursor = nullptr;
+ const char *name, *value;
+ while ( ( cursor = ne_response_header_iterate(
+ req, cursor, &name, &value ) ) != nullptr )
+ {
+ char buffer[8192];
+
+ SAL_INFO( "ucb.ucp.webdav", "GET - received header: " << name << ": " << value );
+ ne_snprintf(buffer, sizeof buffer, "%s: %s", name, value);
+ runResponseHeaderHandler(userdata, buffer);
+ }
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::PUT( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ size_t size)
+{
+ ne_request * req = ne_request_create( sess, "PUT", uri );
+ int ret;
+
+ // tdf#99246
+ // extract the path of uri
+ // ne_lock_using_resource below compares path, ignores all the rest.
+ // in case of Web proxy active, this function uri parameter is instead absolute
+ ne_uri aUri;
+ ne_uri_parse( uri, &aUri );
+ ne_lock_using_resource( req, aUri.path, 0 );
+ ne_lock_using_parent( req, uri );
+
+ ne_set_request_body_buffer( req, buffer, size );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+ return ret;
+}
+
+int NeonSession::POST( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ ne_block_reader reader,
+ void * userdata,
+ const OUString & rContentType,
+ const OUString & rReferer )
+{
+ ne_request * req = ne_request_create( sess, "POST", uri );
+ //struct get_context ctx;
+ int ret;
+
+ RequestDataMap * pData = nullptr;
+
+ if ( !rContentType.isEmpty() || !rReferer.isEmpty() )
+ {
+ // Remember contenttype and referer. Data will be added to HTTP request
+ // header in 'PreSendRequest' callback.
+ pData = static_cast< RequestDataMap* >( m_pRequestData );
+ (*pData)[ req ] = RequestData( rContentType, rReferer );
+ }
+
+ //ctx.total = -1;
+ //ctx.fd = fd;
+ //ctx.error = 0;
+ //ctx.session = sess;
+
+ ///* Read the value of the Content-Length header into ctx.total */
+ //ne_add_response_header_handler( req, "Content-Length",
+ // ne_handle_numeric_header, &ctx.total );
+
+ ne_add_response_body_reader( req, ne_accept_2xx, reader, userdata );
+
+ ne_set_request_body_buffer( req, buffer, strlen( buffer ) );
+
+ {
+ osl::Guard< osl::Mutex > theGlobalGuard(getGlobalNeonMutex());
+ ret = ne_request_dispatch( req );
+ }
+
+ //if ( ctx.error )
+ // ret = NE_ERROR;
+ //else
+ if ( ret == NE_OK && ne_get_status( req )->klass != 2 )
+ ret = NE_ERROR;
+
+ ne_request_destroy( req );
+
+ if ( pData )
+ {
+ // Remove request data from session's list.
+ RequestDataMap::iterator it = pData->find( req );
+ if ( it != pData->end() )
+ pData->erase( it );
+ }
+
+ return ret;
+}
+
+bool
+NeonSession::getDataFromInputStream(
+ const uno::Reference< io::XInputStream > & xStream,
+ uno::Sequence< sal_Int8 > & rData,
+ bool bAppendTrailingZeroByte )
+{
+ if ( xStream.is() )
+ {
+ uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
+ if ( xSeekable.is() )
+ {
+ try
+ {
+ sal_Int32 nSize
+ = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
+ sal_Int32 nRead
+ = xStream->readBytes( rData, nSize );
+
+ if ( nRead == nSize )
+ {
+ if ( bAppendTrailingZeroByte )
+ {
+ rData.realloc( nSize + 1 );
+ rData[ nSize ] = sal_Int8( 0 );
+ }
+ return true;
+ }
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // readBytes
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // readBytes
+ }
+ catch ( io::IOException const & )
+ {
+ // getLength, readBytes
+ }
+ }
+ else
+ {
+ try
+ {
+ uno::Sequence< sal_Int8 > aBuffer;
+ sal_Int32 nPos = 0;
+
+ sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
+ while ( nRead > 0 )
+ {
+ if ( rData.getLength() < ( nPos + nRead ) )
+ rData.realloc( nPos + nRead );
+
+ aBuffer.realloc( nRead );
+ memcpy( static_cast<void*>( rData.getArray() + nPos ),
+ static_cast<const void*>(aBuffer.getConstArray()),
+ nRead );
+ nPos += nRead;
+
+ aBuffer.realloc( 0 );
+ nRead = xStream->readSomeBytes( aBuffer, 65536 );
+ }
+
+ if ( bAppendTrailingZeroByte )
+ {
+ rData.realloc( nPos + 1 );
+ rData[ nPos ] = sal_Int8( 0 );
+ }
+ return true;
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // readBytes
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // readBytes
+ }
+ catch ( io::IOException const & )
+ {
+ // readBytes
+ }
+ }
+ }
+ return false;
+}
+
+bool
+NeonSession::isDomainMatch( const OUString& certHostName )
+{
+ OUString hostName = getHostName();
+
+ if (hostName.equalsIgnoreAsciiCase( certHostName ) )
+ return true;
+
+ if ( certHostName.startsWith( "*" ) &&
+ hostName.getLength() >= certHostName.getLength() )
+ {
+ OUString cmpStr = certHostName.copy( 1 );
+
+ if ( hostName.matchIgnoreAsciiCase(
+ cmpStr, hostName.getLength() - cmpStr.getLength() ) )
+ return true;
+ }
+ return false;
+}
+
+OUString NeonSession::makeAbsoluteURL( OUString const & rURL ) const
+{
+ try
+ {
+ // Is URL relative or already absolute?
+ if ( !rURL.isEmpty() && rURL[ 0 ] != '/' )
+ {
+ // absolute.
+ return rURL;
+ }
+ else
+ {
+ ne_uri aUri = {};
+
+ ne_fill_server_uri( m_pHttpSession, &aUri );
+ aUri.path
+ = ne_strdup( OUStringToOString(
+ rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+ NeonUri aNeonUri( &aUri );
+ ne_uri_free( &aUri );
+ return aNeonUri.GetURI();
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ // error.
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonSession.hxx b/ucb/source/ucp/webdav-neon/NeonSession.hxx
new file mode 100644
index 000000000..51b477373
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonSession.hxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONSESSION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONSESSION_HXX
+
+#include <config_lgpl.h>
+#include <vector>
+#include <osl/mutex.hxx>
+#include "DAVSession.hxx"
+#include "NeonTypes.hxx"
+#include "NeonLockStore.hxx"
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+namespace ucbhelper { class ProxyDecider; }
+
+namespace webdav_ucp
+{
+
+// A DAVSession implementation using the neon/expat library
+class NeonSession : public DAVSession
+{
+private:
+ osl::Mutex m_aMutex;
+ OUString m_aScheme;
+ OUString m_aHostName;
+ OUString m_aProxyName;
+ sal_Int32 m_nPort;
+ sal_Int32 m_nProxyPort;
+ css::uno::Sequence< css::beans::NamedValue > m_aFlags;
+ HttpSession * m_pHttpSession;
+ bool m_bNeedNewSession = false; // Something happened that could invalidate m_pHttpSession
+ void * m_pRequestData;
+ const ucbhelper::InternetProxyDecider & m_rProxyDecider;
+
+ // @@@ This should really be per-request data. But Neon currently
+ // (0.23.5) has no interface for passing per-request user data.
+ // Theoretically, a NeonSession instance could handle multiple requests
+ // at a time --currently it doesn't. Thus this is not an issue at the
+ // moment.
+ DAVRequestEnvironment m_aEnv;
+
+ static bool m_bGlobalsInited;
+ static NeonLockStore m_aNeonLockStore;
+
+protected:
+ virtual ~NeonSession() override;
+
+public:
+ /// @throws std::exception
+ NeonSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
+ const OUString& inUri,
+ const css::uno::Sequence< css::beans::NamedValue >& rFlags,
+ const ucbhelper::InternetProxyDecider & rProxyDecider );
+
+ // DAVSession methods
+ virtual bool CanUse( const OUString & inPath,
+ const css::uno::Sequence< css::beans::NamedValue >& rFlags ) override;
+
+ virtual bool UsesProxy() override;
+
+ const DAVRequestEnvironment & getRequestEnvironment() const
+ { return m_aEnv; }
+
+ virtual void
+ OPTIONS( const OUString & inPath,
+ DAVOptions& rOptions, // contains the name+values
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // allprop & named
+ virtual void
+ PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // propnames
+ virtual void
+ PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo >& ioResInfo,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream > & ioOutputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ GET0( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream > & ioOutputStream,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ PUT( const OUString & inPath,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ css::uno::Reference< css::io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ COPY( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite ) override;
+
+ virtual void
+ MOVE( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite ) override;
+
+ virtual void DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // set new lock.
+ virtual void LOCK( const OUString & inURL,
+ css::ucb::Lock & inLock,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void UNLOCK( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // helpers
+ virtual void abort() override;
+
+ const OUString & getHostName() const { return m_aHostName; }
+
+ ::uno::Reference< ::uno::XComponentContext > const & getComponentContext() const
+ { return m_xFactory->getComponentContext(); }
+
+ const void * getRequestData() const { return m_pRequestData; }
+
+ bool isDomainMatch( const OUString& certHostName );
+
+ int CertificationNotify(const ne_ssl_certificate *cert);
+
+ int NeonAuth(const char* inAuthProtocol, const char* inRealm,
+ int attempt, char* inoutUserName, char * inoutPassWord);
+
+ void PreSendRequest(ne_request* req, ne_buffer* headers);
+
+private:
+ friend class NeonLockStore;
+
+ /// @throws css::uno::RuntimeException
+ void Init();
+
+ /// @throws css::uno::RuntimeException
+ void Init( const DAVRequestEnvironment & rEnv );
+
+ // ret: true => retry request.
+ /// @throws std::exception
+ void HandleError( int nError,
+ const OUString & inPath,
+ const DAVRequestEnvironment & rEnv );
+
+ ucbhelper::InternetProxyServer getProxySettings() const;
+
+ bool removeExpiredLocktoken( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv );
+
+ // refresh lock, called by NeonLockStore::refreshLocks
+ bool LOCK( NeonLock * pLock,
+ sal_Int32 & rlastChanceToSendRefreshRequest );
+
+ // unlock, called by NeonLockStore::~NeonLockStore
+ bool UNLOCK( NeonLock * pLock );
+
+ // low level GET implementation, used by public GET implementations
+ static int GET( ne_session * sess,
+ const char * uri,
+ ne_block_reader reader,
+ bool getheaders,
+ void * userdata );
+
+ // low level GET implementation, used by public GET implementations
+ // used as a HEAD substitute when head is not available
+ static int GET0( ne_session * sess,
+ const char * uri,
+ bool getheaders,
+ void * userdata );
+
+ // Buffer-based PUT implementation. Neon only has file descriptor-
+ // based API.
+ static int PUT( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ size_t size );
+
+ // Buffer-based POST implementation. Neon only has file descriptor-
+ // based API.
+ int POST( ne_session * sess,
+ const char * uri,
+ const char * buffer,
+ ne_block_reader reader,
+ void * userdata,
+ const OUString & rContentType,
+ const OUString & rReferer );
+
+ // Helper: XInputStream -> Sequence< sal_Int8 >
+ static bool getDataFromInputStream(
+ const css::uno::Reference< css::io::XInputStream > & xStream,
+ css::uno::Sequence< sal_Int8 > & rData,
+ bool bAppendTrailingZeroByte );
+
+ OUString makeAbsoluteURL( OUString const & rURL ) const;
+};
+
+osl::Mutex& getGlobalNeonMutex();
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONSESSION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonTypes.hxx b/ucb/source/ucp/webdav-neon/NeonTypes.hxx
new file mode 100644
index 000000000..3e2a7c575
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonTypes.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONTYPES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONTYPES_HXX
+
+#include <config_lgpl.h>
+#include <ne_session.h>
+#include <ne_utils.h>
+#include <ne_basic.h>
+#include <ne_props.h>
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+#include <ne_locks.h>
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+typedef ne_session HttpSession;
+typedef ne_status HttpStatus;
+
+typedef ne_propname NeonPropName;
+typedef ne_prop_result_set NeonPropFindResultSet;
+
+typedef struct ne_lock NeonLock;
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONTYPES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonUri.cxx b/ucb/source/ucp/webdav-neon/NeonUri.cxx
new file mode 100644
index 000000000..c1c263421
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonUri.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <sal/config.h>
+
+#include <rtl/uri.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <ne_alloc.h>
+#include "NeonUri.hxx"
+#include "DAVException.hxx"
+
+#include "../inc/urihelper.hxx"
+
+using namespace webdav_ucp;
+
+// FIXME: not sure whether initializing a ne_uri statically is supposed to work
+// the string fields of ne_uri are char*, not const char*
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
+
+namespace {
+
+const ne_uri g_sUriDefaultsHTTP = { const_cast<char *>("http"),
+ nullptr,
+ nullptr,
+ DEFAULT_HTTP_PORT,
+ nullptr,
+ nullptr,
+ nullptr };
+const ne_uri g_sUriDefaultsHTTPS = { const_cast<char *>("https"),
+ nullptr,
+ nullptr,
+ DEFAULT_HTTPS_PORT,
+ nullptr,
+ nullptr,
+ nullptr };
+const ne_uri g_sUriDefaultsFTP = { const_cast<char *>("ftp"),
+ nullptr,
+ nullptr,
+ DEFAULT_FTP_PORT,
+ nullptr,
+ nullptr,
+ nullptr };
+} // namespace
+
+NeonUri::NeonUri( const ne_uri * inUri )
+{
+ if ( inUri == nullptr )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ char * uri = ne_uri_unparse( inUri );
+
+ if ( uri == nullptr )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ init( OString( uri ), inUri );
+ ne_free( uri );
+
+ calculateURI();
+}
+
+NeonUri::NeonUri( const OUString & inUri )
+{
+ if ( inUri.isEmpty() )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ // #i77023#
+ OUString aEscapedUri( ucb_impl::urihelper::encodeURI( inUri ) );
+
+ OString theInputUri(
+ aEscapedUri.getStr(), aEscapedUri.getLength(), RTL_TEXTENCODING_UTF8 );
+
+ ne_uri theUri;
+ if ( ne_uri_parse( theInputUri.getStr(), &theUri ) != 0 )
+ {
+ ne_uri_free( &theUri );
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ init( theInputUri, &theUri );
+ ne_uri_free( &theUri );
+
+ calculateURI();
+}
+
+void NeonUri::init( const OString & rUri, const ne_uri * pUri )
+{
+ // Complete URI.
+ const ne_uri * pUriDefs
+ = rUri.matchIgnoreAsciiCase( "ftp:" ) ?
+ &g_sUriDefaultsFTP :
+ rUri.matchIgnoreAsciiCase( "https:" ) ?
+ &g_sUriDefaultsHTTPS :
+ &g_sUriDefaultsHTTP;
+
+ mScheme = OStringToOUString(
+ pUri->scheme ? pUri->scheme : pUriDefs->scheme,
+ RTL_TEXTENCODING_UTF8 );
+ mUserInfo = OStringToOUString(
+ pUri->userinfo ? pUri->userinfo : pUriDefs->userinfo,
+ RTL_TEXTENCODING_UTF8 );
+ mHostName = OStringToOUString(
+ pUri->host ? pUri->host : pUriDefs->host,
+ RTL_TEXTENCODING_UTF8 );
+ mPort = pUri->port > 0 ? pUri->port : pUriDefs->port;
+ mPath = OStringToOUString(
+ pUri->path ? pUri->path : pUriDefs->path,
+ RTL_TEXTENCODING_UTF8 );
+
+ if ( pUri->query )
+ {
+ mPath += "?" + OStringToOUString( pUri->query, RTL_TEXTENCODING_UTF8 );
+ }
+
+ if ( pUri->fragment )
+ {
+ mPath += "#" + OStringToOUString( pUri->fragment, RTL_TEXTENCODING_UTF8 );
+ }
+}
+
+void NeonUri::calculateURI ()
+{
+ OUStringBuffer aBuf( 256 );
+ aBuf.append( mScheme );
+ aBuf.append( "://" );
+ if ( !mUserInfo.isEmpty() )
+ {
+ //TODO! differentiate between empty and missing userinfo
+ aBuf.append( mUserInfo );
+ aBuf.append( "@" );
+ }
+ // Is host a numeric IPv6 address?
+ if ( ( mHostName.indexOf( ':' ) != -1 ) &&
+ ( mHostName[ 0 ] != '[' ) )
+ {
+ aBuf.append( "[" );
+ aBuf.append( mHostName );
+ aBuf.append( "]" );
+ }
+ else
+ {
+ aBuf.append( mHostName );
+ }
+
+ // append port, but only, if not default port.
+ bool bAppendPort = true;
+ switch ( mPort )
+ {
+ case DEFAULT_HTTP_PORT:
+ bAppendPort = mScheme != "http";
+ break;
+
+ case DEFAULT_HTTPS_PORT:
+ bAppendPort = mScheme != "https";
+ break;
+
+ case DEFAULT_FTP_PORT:
+ bAppendPort = mScheme != "ftp";
+ break;
+ }
+ if ( bAppendPort )
+ {
+ aBuf.append( ":" );
+ aBuf.append( OUString::number( mPort ) );
+ }
+ aBuf.append( mPath );
+
+ mURI = aBuf.makeStringAndClear();
+}
+
+OUString NeonUri::GetPathBaseName () const
+{
+ sal_Int32 nPos = mPath.lastIndexOf ('/');
+ sal_Int32 nTrail = 0;
+ if (nPos == mPath.getLength () - 1)
+ {
+ // Trailing slash found. Skip.
+ nTrail = 1;
+ nPos = mPath.lastIndexOf ('/', nPos);
+ }
+ if (nPos != -1)
+ {
+ OUString aTemp(
+ mPath.copy (nPos + 1, mPath.getLength () - nPos - 1 - nTrail) );
+
+ // query, fragment present?
+ nPos = aTemp.indexOf( '?' );
+ if ( nPos == -1 )
+ nPos = aTemp.indexOf( '#' );
+
+ if ( nPos != -1 )
+ aTemp = aTemp.copy( 0, nPos );
+
+ return aTemp;
+ }
+ else
+ return "/";
+}
+
+bool NeonUri::operator== ( const NeonUri & rOther ) const
+{
+ return ( mURI == rOther.mURI );
+}
+
+OUString NeonUri::GetPathBaseNameUnescaped () const
+{
+ return unescape( GetPathBaseName() );
+}
+
+void NeonUri::AppendPath (const OUString& rPath)
+{
+ if (mPath.lastIndexOf ('/') != mPath.getLength () - 1)
+ mPath += "/";
+
+ mPath += rPath;
+ calculateURI ();
+};
+
+// static
+OUString NeonUri::escapeSegment( const OUString& segment )
+{
+ return rtl::Uri::encode( segment,
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+}
+
+// static
+OUString NeonUri::unescape( const OUString& segment )
+{
+ return rtl::Uri::decode( segment,
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 );
+}
+
+// static
+OUString NeonUri::makeConnectionEndPointString(
+ const OUString & rHostName, int nPort )
+{
+ OUStringBuffer aBuf;
+
+ // Is host a numeric IPv6 address?
+ if ( ( rHostName.indexOf( ':' ) != -1 ) &&
+ ( rHostName[ 0 ] != '[' ) )
+ {
+ aBuf.append( "[" );
+ aBuf.append( rHostName );
+ aBuf.append( "]" );
+ }
+ else
+ {
+ aBuf.append( rHostName );
+ }
+
+ if ( ( nPort != DEFAULT_HTTP_PORT ) && ( nPort != DEFAULT_HTTPS_PORT ) )
+ {
+ aBuf.append( ":" );
+ aBuf.append( OUString::number( nPort ) );
+ }
+ return aBuf.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/NeonUri.hxx b/ucb/source/ucp/webdav-neon/NeonUri.hxx
new file mode 100644
index 000000000..fae505e50
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/NeonUri.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONURI_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONURI_HXX
+
+#include <config_lgpl.h>
+#include <ne_uri.h>
+#include <rtl/ustring.hxx>
+#include "DAVException.hxx"
+
+namespace webdav_ucp
+{
+
+#define DEFAULT_HTTP_PORT 80
+#define DEFAULT_HTTPS_PORT 443
+#define DEFAULT_FTP_PORT 21
+
+// A URI implementation for use with the neon/expat library
+class NeonUri
+{
+ private:
+ OUString mURI;
+ OUString mScheme;
+ OUString mUserInfo;
+ OUString mHostName;
+ sal_Int32 mPort;
+ OUString mPath;
+
+ void init( const OString & rUri, const ne_uri * pUri );
+ void calculateURI ();
+
+ public:
+ /// @throws DAVException
+ explicit NeonUri( const OUString & inUri );
+ /// @throws DAVException
+ explicit NeonUri( const ne_uri * inUri );
+
+ bool operator== ( const NeonUri & rOther ) const;
+ bool operator!= ( const NeonUri & rOther ) const
+ { return !operator==( rOther ); }
+
+ const OUString & GetURI() const
+ { return mURI; };
+ const OUString & GetScheme() const
+ { return mScheme; };
+ const OUString & GetUserInfo() const
+ { return mUserInfo; };
+ const OUString & GetHost() const
+ { return mHostName; };
+ sal_Int32 GetPort() const
+ { return mPort; };
+ const OUString & GetPath() const
+ { return mPath; };
+
+ OUString GetPathBaseName() const;
+
+ OUString GetPathBaseNameUnescaped() const;
+
+ void SetScheme (const OUString& scheme)
+ { mScheme = scheme; calculateURI (); };
+
+ void AppendPath (const OUString& rPath);
+
+ static OUString escapeSegment( const OUString& segment );
+ static OUString unescape( const OUString& string );
+
+ // "host:port", omit ":port" for port 80 and 443
+ static OUString makeConnectionEndPointString(
+ const OUString & rHostName,
+ int nPort );
+};
+
+} // namespace webdav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_NEONURI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/PropertyMap.hxx b/ucb/source/ucp/webdav-neon/PropertyMap.hxx
new file mode 100644
index 000000000..159651085
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/PropertyMap.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_PROPERTYMAP_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_PROPERTYMAP_HXX
+
+#include <config_lgpl.h>
+#include <com/sun/star/beans/Property.hpp>
+#include <unordered_set>
+
+namespace webdav_ucp {
+
+
+struct equalPropertyName
+{
+ bool operator()( const css::beans::Property & p1,
+ const css::beans::Property & p2 ) const
+ {
+ return p1.Name == p2.Name;
+ }
+};
+
+struct hashPropertyName
+{
+ size_t operator()( const css::beans::Property & p ) const
+ {
+ return p.Name.hashCode();
+ }
+};
+
+typedef std::unordered_set
+<
+ css::beans::Property,
+ hashPropertyName,
+ equalPropertyName
+>
+PropertyMap;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/PropfindCache.cxx b/ucb/source/ucp/webdav-neon/PropfindCache.cxx
new file mode 100644
index 000000000..a54003661
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/PropfindCache.cxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <osl/time.h>
+#include "PropfindCache.hxx"
+
+namespace webdav_ucp
+{
+
+ // PropertyNames implementation
+
+ PropertyNames::PropertyNames() :
+ m_nStaleTime( 0 ),
+ m_sURL(),
+ m_aPropertiesNames()
+ {
+ }
+
+ PropertyNames::PropertyNames( const OUString& rURL ) :
+ m_nStaleTime( 0 ),
+ m_sURL( rURL ),
+ m_aPropertiesNames()
+ {
+ }
+
+ //PropertyNamesCache implementation
+
+ PropertyNamesCache::PropertyNamesCache()
+ {
+ }
+
+ PropertyNamesCache::~PropertyNamesCache()
+ {
+ }
+
+ bool PropertyNamesCache::getCachedPropertyNames( const OUString& rURL, PropertyNames& rCacheElement )
+ {
+ // search the URL in the static map
+ osl::MutexGuard aGuard( m_aMutex );
+ PropNameCache::const_iterator it = m_aTheCache.find( rURL );
+ if ( it == m_aTheCache.end() )
+ return false;
+ else
+ {
+ // check if the element is stale, before restoring
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ if ( (*it).second.getStaleTime() < t1.Seconds )
+ {
+ // if stale, remove from cache, do not restore
+ m_aTheCache.erase( it );
+ return false;
+ // return false instead
+ }
+ rCacheElement = (*it).second;
+ return true;
+ }
+ }
+
+ void PropertyNamesCache::removeCachedPropertyNames( const OUString& rURL )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ PropNameCache::const_iterator it = m_aTheCache.find( rURL );
+ if ( it != m_aTheCache.end() )
+ {
+ m_aTheCache.erase( it );
+ }
+ }
+
+ void PropertyNamesCache::addCachePropertyNames( PropertyNames& rCacheElement )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aURL( rCacheElement.getURL() );
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ rCacheElement.setStaleTime( t1.Seconds + 10 );
+
+ m_aTheCache[ aURL ] = rCacheElement;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/ucb/source/ucp/webdav-neon/PropfindCache.hxx b/ucb/source/ucp/webdav-neon/PropfindCache.hxx
new file mode 100644
index 000000000..bcbdff93e
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/PropfindCache.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_PROPFINDCACHE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_PROPFINDCACHE_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <osl/mutex.hxx>
+#include <map>
+#include <vector>
+
+#include "DAVResource.hxx"
+
+namespace webdav_ucp
+{
+ // A property names cache mechanism, URL driven.
+ // It is used to cache the property names received
+ // from the WebDAV server, to minimize the need of
+ // net transactions (e.g. PROPFIND).
+ // The cache lifetime should be short
+ // just to remove the annoying slowness when
+ // typing text or moving cursor around when the
+ // net link is slow.
+
+ // Define the properties cache element
+ class PropertyNames final
+ {
+ /// target time when this element becomes stale
+ sal_uInt32 m_nStaleTime;
+ OUString m_sURL;
+ // the property name list received from WebDAV server
+ std::vector< DAVResourceInfo > m_aPropertiesNames;
+
+ public:
+ PropertyNames();
+ explicit PropertyNames( const OUString& rURL );
+
+ sal_uInt32 getStaleTime() const { return m_nStaleTime; };
+ void setStaleTime( const sal_uInt32 nStaleTime ) { m_nStaleTime = nStaleTime; };
+
+ OUString& getURL() { return m_sURL; };
+
+ const std::vector< DAVResourceInfo >& getPropertiesNames() const { return m_aPropertiesNames; };
+ void setPropertiesNames( const std::vector< DAVResourceInfo >& aPropertiesNames ) { m_aPropertiesNames = aPropertiesNames; };
+ };
+
+ // Define the PropertyNames cache
+ // TODO: the OUString key element in std::map needs to be changed with a URI representation
+ // with a specific compare (std::less) implementation, this last one implementing
+ // as suggested in <https://tools.ietf.org/html/rfc3986#section-6>.
+ // To find by URI and not by string equality.
+ typedef std::map< OUString, PropertyNames,
+ std::less< OUString > >PropNameCache;
+
+ class PropertyNamesCache final
+ {
+ PropNameCache m_aTheCache;
+ osl::Mutex m_aMutex;
+
+ public:
+ PropertyNamesCache();
+ ~PropertyNamesCache();
+
+ bool getCachedPropertyNames( const OUString& URL, PropertyNames& rCacheElement );
+ void removeCachedPropertyNames( const OUString& URL );
+ void addCachePropertyNames( PropertyNames& rCacheElement );
+ };
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.cxx b/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.cxx
new file mode 100644
index 000000000..6a06f0dcb
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.cxx
@@ -0,0 +1,509 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <config_lgpl.h>
+#include <string.h>
+#include <ne_xml.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include "UCBDeadPropertyValue.hxx"
+#include <memory>
+
+using namespace webdav_ucp;
+using namespace com::sun::star;
+
+namespace {
+
+struct UCBDeadPropertyValueParseContext
+{
+ std::unique_ptr<OUString> pType;
+ std::unique_ptr<OUString> pValue;
+
+ UCBDeadPropertyValueParseContext() {}
+};
+
+}
+
+static const char aTypeString[] = "string";
+static const char aTypeLong[] = "long";
+static const char aTypeShort[] = "short";
+static const char aTypeBoolean[] = "boolean";
+static const char aTypeChar[] = "char";
+static const char aTypeByte[] = "byte";
+static const char aTypeHyper[] = "hyper";
+static const char aTypeFloat[] = "float";
+static const char aTypeDouble[] = "double";
+
+static const char aXMLPre[] = "<ucbprop><type>";
+static const char aXMLMid[] = "</type><value>";
+static const char aXMLEnd[] = "</value></ucbprop>";
+
+
+#define STATE_TOP (1)
+
+#define STATE_UCBPROP (STATE_TOP)
+#define STATE_TYPE (STATE_TOP + 1)
+#define STATE_VALUE (STATE_TOP + 2)
+
+
+extern "C" {
+
+static int UCBDeadPropertyValue_startelement_callback(
+ void *,
+ int parent,
+ const char * /*nspace*/,
+ const char *name,
+ const char ** )
+{
+ if ( name != nullptr )
+ {
+ switch ( parent )
+ {
+ case NE_XML_STATEROOT:
+ if ( strcmp( name, "ucbprop" ) == 0 )
+ return STATE_UCBPROP;
+ break;
+
+ case STATE_UCBPROP:
+ if ( strcmp( name, "type" ) == 0 )
+ return STATE_TYPE;
+ else if ( strcmp( name, "value" ) == 0 )
+ return STATE_VALUE;
+ break;
+ }
+ }
+ return NE_XML_DECLINE;
+}
+
+
+static int UCBDeadPropertyValue_chardata_callback(
+ void *userdata,
+ int state,
+ const char *buf,
+ size_t len )
+{
+ UCBDeadPropertyValueParseContext * pCtx
+ = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
+
+ switch ( state )
+ {
+ case STATE_TYPE:
+ assert( !pCtx->pType &&
+ "UCBDeadPropertyValue_endelement_callback - "
+ "Type already set!" );
+ pCtx->pType.reset( new OUString( buf, len, RTL_TEXTENCODING_ASCII_US ) );
+ break;
+
+ case STATE_VALUE:
+ assert( !pCtx->pValue &&
+ "UCBDeadPropertyValue_endelement_callback - "
+ "Value already set!" );
+ pCtx->pValue.reset( new OUString( buf, len, RTL_TEXTENCODING_ASCII_US ) );
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+
+static int UCBDeadPropertyValue_endelement_callback(
+ void *userdata,
+ int state,
+ const char *,
+ const char * )
+{
+ UCBDeadPropertyValueParseContext * pCtx
+ = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
+
+ switch ( state )
+ {
+ case STATE_TYPE:
+ if ( !pCtx->pType )
+ return 1; // abort
+ break;
+
+ case STATE_VALUE:
+ if ( !pCtx->pValue )
+ return 1; // abort
+ break;
+
+ case STATE_UCBPROP:
+ if ( !pCtx->pType || ! pCtx->pValue )
+ return 1; // abort
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+}
+
+static OUString encodeValue( const OUString & rValue )
+{
+ // Note: I do not use the usual &amp; + &lt; + &gt; encoding, because
+ // I want to prevent any XML parser from trying to 'understand'
+ // the value. This caused problems:
+
+ // Example:
+ // - Unencoded property value: x<z
+ // PROPPATCH:
+ // - Encoded property value: x&lt;z
+ // - UCBDeadPropertyValue::toXML result:
+ // <ucbprop><type>string</type><value>x&lt;z</value></ucbprop>
+ // PROPFIND:
+ // - parser replaces &lt; by > ==> error (not well formed)
+
+ OUStringBuffer aResult;
+ const sal_Unicode * pValue = rValue.getStr();
+
+ sal_Int32 nCount = rValue.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const sal_Unicode c = pValue[ n ];
+
+ if ( '%' == c )
+ aResult.append( "%per;" );
+ else if ( '<' == c )
+ aResult.append( "%lt;" );
+ else if ( '>' == c )
+ aResult.append( "%gt;" );
+ else
+ aResult.append( c );
+ }
+ return aResult.makeStringAndClear();
+}
+
+
+static OUString decodeValue( const OUString & rValue )
+{
+ OUStringBuffer aResult;
+ const sal_Unicode * pValue = rValue.getStr();
+
+ sal_Int32 nPos = 0;
+ sal_Int32 nEnd = rValue.getLength();
+
+ while ( nPos < nEnd )
+ {
+ sal_Unicode c = pValue[ nPos ];
+
+ if ( '%' == c )
+ {
+ nPos++;
+
+ if ( nPos == nEnd )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+
+ c = pValue[ nPos ];
+
+ if ( 'p' == c )
+ {
+ // %per;
+
+ if ( nPos > nEnd - 4 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 'e' == pValue[ nPos + 1 ] )
+ &&
+ ( 'r' == pValue[ nPos + 2 ] )
+ &&
+ ( ';' == pValue[ nPos + 3 ] ) )
+ {
+ aResult.append( '%' );
+ nPos += 3;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+ }
+ else if ( 'l' == c )
+ {
+ // %lt;
+
+ if ( nPos > nEnd - 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 't' == pValue[ nPos + 1 ] )
+ &&
+ ( ';' == pValue[ nPos + 2 ] ) )
+ {
+ aResult.append( '<' );
+ nPos += 2;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+ }
+ else if ( 'g' == c )
+ {
+ // %gt;
+
+ if ( nPos > nEnd - 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 't' == pValue[ nPos + 1 ] )
+ &&
+ ( ';' == pValue[ nPos + 2 ] ) )
+ {
+ aResult.append( '>' );
+ nPos += 2;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "decodeValue() - syntax error!" );
+ return OUString();
+ }
+ }
+ else
+ aResult.append( c );
+
+ nPos++;
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+
+// static
+bool UCBDeadPropertyValue::supportsType( const uno::Type & rType )
+{
+ return rType == cppu::UnoType<OUString>::get()
+ || rType == cppu::UnoType<sal_Int32>::get()
+ || rType == cppu::UnoType<sal_Int16>::get()
+ || rType == cppu::UnoType<bool>::get()
+ || rType == cppu::UnoType<cppu::UnoCharType>::get()
+ || rType == cppu::UnoType<sal_Int8>::get()
+ || rType == cppu::UnoType<sal_Int64>::get()
+ || rType == cppu::UnoType<float>::get()
+ || rType == cppu::UnoType<double>::get();
+}
+
+
+// static
+bool UCBDeadPropertyValue::createFromXML( const OString & rInData,
+ uno::Any & rOutData )
+{
+ bool success = false;
+
+ ne_xml_parser * parser = ne_xml_create();
+ if ( parser )
+ {
+ UCBDeadPropertyValueParseContext aCtx;
+ ne_xml_push_handler( parser,
+ UCBDeadPropertyValue_startelement_callback,
+ UCBDeadPropertyValue_chardata_callback,
+ UCBDeadPropertyValue_endelement_callback,
+ &aCtx );
+
+ ne_xml_parse( parser, rInData.getStr(), rInData.getLength() );
+
+ success = !ne_xml_failed( parser );
+
+ ne_xml_destroy( parser );
+
+ if ( success )
+ {
+ if ( aCtx.pType && aCtx.pValue )
+ {
+ // Decode aCtx.pValue! It may contain XML reserved chars.
+ OUString aStringValue = decodeValue( *aCtx.pValue );
+ if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeString ) )
+ {
+ rOutData <<= aStringValue;
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeLong ) )
+ {
+ rOutData <<= aStringValue.toInt32();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeShort ) )
+ {
+ rOutData <<= sal_Int16( aStringValue.toInt32() );
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeBoolean ) )
+ {
+ if ( aStringValue.equalsIgnoreAsciiCase("true") )
+ rOutData <<= true;
+ else
+ rOutData <<= false;
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeChar ) )
+ {
+ rOutData <<= aStringValue.toChar();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeByte ) )
+ {
+ rOutData <<= sal_Int8( aStringValue.toChar() );
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeHyper ) )
+ {
+ rOutData <<= aStringValue.toInt64();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeFloat ) )
+ {
+ rOutData <<= aStringValue.toFloat();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeDouble ) )
+ {
+ rOutData <<= aStringValue.toDouble();
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "createFromXML() - "
+ "Unsupported property type!" );
+ success = false;
+ }
+ }
+ else
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+
+// static
+bool UCBDeadPropertyValue::toXML( const uno::Any & rInData,
+ OUString & rOutData )
+{
+ // <ucbprop><type>the_type</type><value>the_value</value></ucbprop>
+
+ // Check property type. Extract type and value as string.
+
+ const uno::Type& rType = rInData.getValueType();
+ OUString aStringValue;
+ OUString aStringType;
+
+ if ( rType == cppu::UnoType<OUString>::get() )
+ {
+ // string
+ rInData >>= aStringValue;
+ aStringType = aTypeString;
+ }
+ else if ( rType == cppu::UnoType<sal_Int32>::get() )
+ {
+ // long
+ sal_Int32 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeLong;
+ }
+ else if ( rType == cppu::UnoType<sal_Int16>::get() )
+ {
+ // short
+ sal_Int32 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeShort;
+ }
+ else if ( rType == cppu::UnoType<bool>::get() )
+ {
+ // boolean
+ bool bValue = false;
+ rInData >>= bValue;
+ aStringValue = OUString::boolean( bValue );
+ aStringType = aTypeBoolean;
+ }
+ else if ( rType == cppu::UnoType<cppu::UnoCharType>::get() )
+ {
+ // char
+ sal_Unicode cValue = 0;
+ rInData >>= cValue;
+ aStringValue = OUString( cValue );
+ aStringType = aTypeChar;
+ }
+ else if ( rType == cppu::UnoType<sal_Int8>::get() )
+ {
+ // byte
+ sal_Int8 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString( sal_Unicode( nValue ) );
+ aStringType = aTypeByte;
+ }
+ else if ( rType == cppu::UnoType<sal_Int64>::get() )
+ {
+ // hyper
+ sal_Int64 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeHyper;
+ }
+ else if ( rType == cppu::UnoType<float>::get() )
+ {
+ // float
+ float nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeFloat;
+ }
+ else if ( rType == cppu::UnoType<double>::get() )
+ {
+ // double
+ double nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeDouble;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "toXML() - unsupported property type!" );
+ return false;
+ }
+
+ // Encode value! It must not contain XML reserved chars!
+ aStringValue = encodeValue( aStringValue );
+
+ rOutData = aXMLPre;
+ rOutData += aStringType;
+ rOutData += aXMLMid;
+ rOutData += aStringValue;
+ rOutData += aXMLEnd;
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.hxx b/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.hxx
new file mode 100644
index 000000000..b93c142d5
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/UCBDeadPropertyValue.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_UCBDEADPROPERTYVALUE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_UCBDEADPROPERTYVALUE_HXX
+
+#include <rtl/string.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace webdav_ucp
+{
+
+class UCBDeadPropertyValue
+{
+public:
+ static bool supportsType( const css::uno::Type & rType );
+
+ static bool createFromXML( const OString & rInData,
+ css::uno::Any & rOutData );
+ static bool toXML( const css::uno::Any & rInData,
+ OUString & rOutData );
+};
+
+}
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_UCBDEADPROPERTYVALUE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/ucpdav1.component b/ucb/source/ucp/webdav-neon/ucpdav1.component
new file mode 100644
index 000000000..5bebe2535
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/ucpdav1.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--**********************************************************************
+*
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* Copyright 2000, 2010 Oracle and/or its affiliates.
+*
+* OpenOffice.org - a multi-platform office productivity suite
+*
+* This file is part of OpenOffice.org.
+*
+* OpenOffice.org is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License version 3
+* only, as published by the Free Software Foundation.
+*
+* OpenOffice.org is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License version 3 for more details
+* (a copy is included in the LICENSE file that accompanied this code).
+*
+* You should have received a copy of the GNU Lesser General Public License
+* version 3 along with OpenOffice.org. If not, see
+* <http://www.openoffice.org/license.html>
+* for a copy of the LGPLv3 License.
+*
+**********************************************************************-->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="ucpdav1" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.WebDAVContentProvider">
+ <service name="com.sun.star.ucb.WebDAVContentProvider"/>
+ </implementation>
+</component>
diff --git a/ucb/source/ucp/webdav-neon/webdavcontent.cxx b/ucb/source/ucp/webdav-neon/webdavcontent.cxx
new file mode 100644
index 000000000..931195e35
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavcontent.cxx
@@ -0,0 +1,4217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <memory>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <officecfg/Inet.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/simpleinteractionrequest.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertySetInfoChange.hpp>
+#include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandEnvironment.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument3.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+#include <ucbhelper/macros.hxx>
+
+#include "webdavcontent.hxx"
+#include "webdavprovider.hxx"
+#include "webdavresultset.hxx"
+#include "ContentProperties.hxx"
+#include "NeonUri.hxx"
+#include "UCBDeadPropertyValue.hxx"
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+namespace
+{
+ // implement a GET to substitute HEAD, when HEAD not available
+ void lcl_sendPartialGETRequest( bool &bError,
+ DAVException &aLastException,
+ const std::vector< OUString >& rProps,
+ std::vector< OUString > &aHeaderNames,
+ const std::unique_ptr< DAVResourceAccess > &xResAccess,
+ std::unique_ptr< ContentProperties > &xProps,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+ {
+ DAVResource aResource;
+ DAVRequestHeaders aPartialGet;
+ aPartialGet.emplace_back( OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
+ OUString( "bytes=0-0" ) );
+
+ bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
+ [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
+
+ if ( bIsRequestSize )
+ {
+ // we need to know if the server accepts range requests for a resource
+ // and the range unit it uses
+ aHeaderNames.emplace_back( "Accept-Ranges" ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
+ aHeaderNames.emplace_back( "Content-Range" ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
+ }
+ try
+ {
+ xResAccess->GET0( aPartialGet, aHeaderNames, aResource, xEnv );
+ bError = false;
+
+ if ( bIsRequestSize )
+ {
+ // the ContentProperties maps "Content-Length" to the UCB "Size" property
+ // This would have an unrealistic value of 1 byte because we did only a partial GET
+ // Solution: if "Content-Range" is present, map it with UCB "Size" property
+ OUString aAcceptRanges, aContentRange, aContentLength;
+ std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
+ for ( const auto& rResponseProp : aResponseProps )
+ {
+ if ( rResponseProp.Name == "Accept-Ranges" )
+ rResponseProp.Value >>= aAcceptRanges;
+ else if ( rResponseProp.Name == "Content-Range" )
+ rResponseProp.Value >>= aContentRange;
+ else if ( rResponseProp.Name == "Content-Length" )
+ rResponseProp.Value >>= aContentLength;
+ }
+
+ sal_Int64 nSize = 1;
+ if ( aContentLength.getLength() )
+ {
+ nSize = aContentLength.toInt64();
+ }
+
+ // according to <> http://tools.ietf.org/html/rfc2616#section-3.12
+ // <https://tools.ietf.org/html/rfc7233#section-2>
+ // needs some explanation for this
+ // probably some changes?
+ // the only range unit defined is "bytes" and implementations
+ // MAY ignore ranges specified using other units.
+ if ( nSize == 1 &&
+ aContentRange.getLength() &&
+ aAcceptRanges == "bytes" )
+ {
+ // Parse the Content-Range to get the size
+ // vid. http://tools.ietf.org/html/rfc2616#section-14.16
+ // Content-Range: <range unit> <bytes range>/<size>
+ sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
+ if ( nSlash != -1 )
+ {
+ OUString aSize = aContentRange.copy( nSlash + 1 );
+ // "*" means that the instance-length is unknown at the time when the response was generated
+ if ( aSize != "*" )
+ {
+ auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
+ [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
+ if (it != aResponseProps.end())
+ {
+ it->Value <<= aSize;
+ }
+ }
+ }
+ }
+ }
+
+ if (xProps)
+ xProps->addProperties(
+ rProps,
+ ContentProperties( aResource ) );
+ else
+ xProps.reset ( new ContentProperties( aResource ) );
+ }
+ catch ( DAVException const & ex )
+ {
+ aLastException = ex;
+ }
+ }
+}
+
+
+// Static value, to manage a simple OPTIONS cache
+// Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
+// Cached DAVOptions have a lifetime that depends on the errors received or not received
+// and on the value of received options.
+static DAVOptionsCache aStaticDAVOptionsCache;
+
+
+// Content Implementation.
+
+
+// ctr for content on an existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( false ),
+ m_bCollection( false ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ initOptsCacheLifeTime();
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext,
+ rSessionFactory,
+ Identifier->getContentIdentifier() ) );
+
+ NeonUri aURI( Identifier->getContentIdentifier() );
+ m_aEscapedTitle = aURI.GetPathBaseName();
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+}
+
+
+// ctr for content on a non-existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ bool isCollection )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_eResourceTypeForLocks( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( true ),
+ m_bCollection( isCollection ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ initOptsCacheLifeTime();
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+
+ // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
+}
+
+
+// virtual
+Content::~Content()
+{
+}
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL Content::acquire()
+ throw( )
+{
+ ContentImplHelper::acquire();
+}
+
+
+// virtual
+void SAL_CALL Content::release()
+ throw( )
+{
+ ContentImplHelper::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
+{
+ // Note: isFolder may require network activities! So call it only
+ // if it is really necessary!!!
+ uno::Any aRet = cppu::queryInterface(
+ rType,
+ static_cast< ucb::XContentCreator * >( this ) );
+ if ( aRet.hasValue() )
+ {
+ try
+ {
+ uno::Reference< task::XInteractionHandler > xIH(
+ task::PasswordContainerInteractionHandler::create( m_xContext ) );
+
+ // Supply a command env to isFolder() that contains an interaction
+ // handler that uses the password container service to obtain
+ // credentials without displaying a password gui.
+
+ uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
+ ucb::CommandEnvironment::create(
+ m_xContext,
+ xIH,
+ uno::Reference< ucb::XProgressHandler >() ) );
+
+ return isFolder( xCmdEnv ) ? aRet : uno::Any();
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ return uno::Any();
+ }
+ }
+ return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( Content );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ {
+ static cppu::OTypeCollection s_aFolderTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ),
+ CPPU_TYPE_REF( ucb::XContentCreator ) );
+
+ return s_aFolderTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aDocumentTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+
+ return s_aDocumentTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL Content::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.WebDAVContent";
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+{
+ return { WEBDAV_CONTENT_SERVICE_NAME };
+}
+
+
+// XContent methods.
+
+
+// virtual
+OUString SAL_CALL Content::getContentType()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ return WEBDAV_COLLECTION_TYPE;
+
+ return WEBDAV_CONTENT_TYPE;
+}
+
+
+// XCommandProcessor methods.
+
+
+// virtual
+uno::Any SAL_CALL Content::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ SAL_INFO( "ucb.ucp.webdav", "Content::execute: start: command: " <<
+ aCommand.Name << ", env: " <<
+ (Environment.is() ? "present" : "missing") );
+
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+
+ // getPropertyValues
+
+
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties, Environment );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+
+ // setPropertyValues
+
+
+ uno::Sequence< beans::PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !aProperties.hasElements() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "No properties!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties, Environment );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+
+ // getPropertySetInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getPropertySetInfo( Environment,
+ false /* don't cache data */ );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+
+ // getCommandInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getCommandInfo( Environment, false );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+
+ // open
+
+
+ ucb::OpenCommandArgument3 aOpenCommand;
+ ucb::OpenCommandArgument2 aTmp;
+ if ( !( aCommand.Argument >>= aTmp ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ // compat mode, extract Arg2 info into newer structure
+ aOpenCommand.Mode = aTmp.Mode;
+ aOpenCommand.Priority = aTmp.Priority;
+ aOpenCommand.Sink = aTmp.Sink;
+ aOpenCommand.Properties = aTmp.Properties;
+ aOpenCommand.SortingInfo = aTmp.SortingInfo;
+ }
+
+ aRet = open( aOpenCommand, Environment );
+
+ }
+ else if ( aCommand.Name == "insert" )
+ {
+
+ // insert
+
+
+ ucb::InsertCommandArgument arg;
+ if ( !( aCommand.Argument >>= arg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ insert( arg.Data, arg.ReplaceExisting, Environment );
+ }
+ else if ( aCommand.Name == "delete" )
+ {
+
+ // delete
+
+
+ bool bDeletePhysical = false;
+ aCommand.Argument >>= bDeletePhysical;
+
+// KSO: Ignore parameter and destroy the content, if you don't support
+// putting objects into trashcan. ( Since we do not have a trash can
+// service yet (src603), you actually have no other choice. )
+// if ( bDeletePhysical )
+// {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->DESTROY( Environment );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+// }
+
+ // Propagate destruction.
+ destroy( bDeletePhysical );
+
+ // Remove own and all children's Additional Core Properties.
+ removeAdditionalPropertySet();
+ }
+ else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
+ {
+
+ // transfer
+ // ( Not available at documents )
+
+
+ ucb::TransferInfo transferArgs;
+ if ( !( aCommand.Argument >>= transferArgs ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ transfer( transferArgs, Environment );
+ }
+ else if ( aCommand.Name == "post" )
+ {
+
+ // post
+
+
+ ucb::PostCommandArgument2 aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ post( aArg, Environment );
+ }
+ else if ( aCommand.Name == "lock" )
+ {
+
+ // lock
+
+ 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" )
+ {
+
+ // unlock
+ // do not check for a DAV resource
+ // the lock store will be checked before sending
+ unlock( Environment );
+ }
+ else if ( aCommand.Name == "createNewContent" && isFolder( Environment ) )
+ {
+
+ // createNewContent
+
+
+ ucb::ContentInfo aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= createNewContent( aArg );
+ }
+ else if ( aCommand.Name == "addProperty" )
+ {
+ ucb::PropertyCommandArgument aPropArg;
+ if ( !( aCommand.Argument >>= aPropArg ))
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in addProperty
+ try
+ {
+ addProperty( aPropArg, Environment );
+ }
+ catch ( const beans::PropertyExistException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch ( const beans::IllegalTypeException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch ( const lang::IllegalArgumentException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ }
+ else if ( aCommand.Name == "removeProperty" )
+ {
+ OUString sPropName;
+ if ( !( aCommand.Argument >>= sPropName ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in removeProperty
+ try
+ {
+ removeProperty( sPropName, Environment );
+ }
+ catch( const beans::UnknownPropertyException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch( const beans::NotRemoveableException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ }
+ else
+ {
+
+ // Unsupported command
+
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::UnsupportedCommandException(
+ aCommand.Name,
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ SAL_INFO( "ucb.ucp.webdav", "Content::execute: end: command: " << aCommand.Name );
+
+ return aRet;
+}
+
+
+// virtual
+void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
+{
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ xResAccess->abort();
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // abort failed!
+ }
+}
+
+
+// XPropertyContainer methods.
+
+
+void Content::addProperty( const ucb::PropertyCommandArgument& aCmdArg,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+// if ( m_bTransient )
+// @@@ ???
+
+ if ( aCmdArg.Property.Name.isEmpty() )
+ throw lang::IllegalArgumentException(
+ "\"addProperty\" with empty Property.Name",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+
+ // Check property type.
+ if ( !UCBDeadPropertyValue::supportsType( aCmdArg.Property.Type ) )
+ {
+ throw beans::IllegalTypeException(
+ "\"addProperty\" unsupported Property.Type",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+
+ if ( aCmdArg.DefaultValue.hasValue()
+ && aCmdArg.DefaultValue.getValueType() != aCmdArg.Property.Type )
+ {
+ throw beans::IllegalTypeException(
+ "\"addProperty\" DefaultValue does not match Property.Type",
+ static_cast< ::cppu::OWeakObject * >( this ) );
+ }
+
+
+ // Make sure a property with the requested name does not already
+ // exist in dynamic and static(!) properties.
+
+
+ // Take into account special properties with custom namespace
+ // using <prop:the_propname xmlns:prop="the_namespace">
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty(
+ aCmdArg.Property.Name, aSpecialName );
+
+ // Note: This requires network access!
+ if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
+ ->hasPropertyByName(
+ bIsSpecial ? aSpecialName : aCmdArg.Property.Name ) )
+ {
+ // Property does already exist.
+ throw beans::PropertyExistException();
+ }
+
+
+ // Add a new dynamic property.
+
+
+ ProppatchValue aValue(
+ PROPSET, aCmdArg.Property.Name, aCmdArg.DefaultValue );
+
+ std::vector< ProppatchValue > aProppatchValues;
+ aProppatchValues.push_back( aValue );
+
+ try
+ {
+ // Set property value at server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_INSERTED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Store property locally.
+ ContentImplHelper::addProperty(
+ bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
+ aCmdArg.Property.Attributes, aCmdArg.DefaultValue );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ ResourceType eType = getResourceType( xEnv );
+ switch ( eType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw lang::IllegalArgumentException();
+
+ case FTP:
+ case NON_DAV:
+ // Store property locally.
+ ContentImplHelper::addProperty(
+ bIsSpecial ? aSpecialName : aCmdArg.Property.Name,
+ aCmdArg.Property.Attributes, aCmdArg.DefaultValue );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ }
+}
+
+void Content::removeProperty( const OUString& Name,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+
+ // Try to remove property from server.
+
+
+ try
+ {
+ std::vector< ProppatchValue > aProppatchValues;
+ ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
+ aProppatchValues.push_back( aValue );
+
+ // Remove property value from server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_REMOVED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ ResourceType eType = getResourceType( xEnv );
+ switch ( eType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw beans::UnknownPropertyException(Name);
+
+ case FTP:
+ case NON_DAV:
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::removeProperty - "
+ "Unable to determine resource type!" );
+// throw beans::UnknownPropertyException();
+ }
+ }
+ }
+}
+
+// virtual
+void SAL_CALL Content::addProperty( const OUString& Name,
+ sal_Int16 Attributes,
+ const uno::Any& DefaultValue )
+{
+ beans::Property aProperty;
+ aProperty.Name = Name;
+ aProperty.Type = DefaultValue.getValueType();
+ aProperty.Attributes = Attributes;
+ aProperty.Handle = -1;
+
+ addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
+ uno::Reference< ucb::XCommandEnvironment >());
+}
+
+// virtual
+void SAL_CALL Content::removeProperty( const OUString& Name )
+{
+ removeProperty( Name,
+ uno::Reference< ucb::XCommandEnvironment >() );
+}
+
+
+// XContentCreator methods.
+
+
+// virtual
+uno::Sequence< ucb::ContentInfo > SAL_CALL
+Content::queryCreatableContentsInfo()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< ucb::ContentInfo > aSeq( 2 );
+
+ // document.
+ aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes
+ = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
+ | ucb::ContentInfoAttribute::KIND_DOCUMENT;
+
+ beans::Property aProp;
+ m_pProvider->getProperty(
+ "Title", aProp );
+
+ uno::Sequence< beans::Property > aDocProps( 1 );
+ aDocProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 0 ].Properties = aDocProps;
+
+ // folder.
+ aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
+ aSeq.getArray()[ 1 ].Attributes
+ = ucb::ContentInfoAttribute::KIND_FOLDER;
+
+ uno::Sequence< beans::Property > aFolderProps( 1 );
+ aFolderProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 1 ].Properties = aFolderProps;
+ return aSeq;
+}
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+Content::createNewContent( const ucb::ContentInfo& Info )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( Info.Type.isEmpty() )
+ return uno::Reference< ucb::XContent >();
+
+ if ( ( Info.Type != WEBDAV_COLLECTION_TYPE ) && ( Info.Type != WEBDAV_CONTENT_TYPE ) )
+ return uno::Reference< ucb::XContent >();
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ assert( !aURL.isEmpty() && "WebdavContent::createNewContent - empty identifier!" );
+
+ if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
+ aURL += "/";
+
+ bool isCollection;
+ if ( Info.Type == WEBDAV_COLLECTION_TYPE )
+ {
+ aURL += "New_Collection";
+ isCollection = true;
+ }
+ else
+ {
+ aURL += "New_Content";
+ isCollection = false;
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId(
+ new ::ucbhelper::ContentIdentifier( aURL ) );
+
+ // create the local content
+ try
+ {
+ return new ::webdav_ucp::Content( m_xContext,
+ m_pProvider,
+ xId,
+ m_xResAccess->getSessionFactory(),
+ isCollection );
+ }
+ catch ( ucb::ContentCreationException & )
+ {
+ return uno::Reference< ucb::XContent >();
+ }
+}
+
+
+// virtual
+OUString Content::getParentURL()
+{
+ // <scheme>:// -> ""
+ // <scheme>://foo -> ""
+ // <scheme>://foo/ -> ""
+ // <scheme>://foo/bar -> <scheme>://foo/
+ // <scheme>://foo/bar/ -> <scheme>://foo/
+ // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ sal_Int32 nPos = aURL.lastIndexOf( '/' );
+ if ( nPos == ( aURL.getLength() - 1 ) )
+ {
+ // Trailing slash found. Skip.
+ nPos = aURL.lastIndexOf( '/', nPos );
+ }
+
+ sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
+ if ( nPos1 != -1 )
+ nPos1 = aURL.lastIndexOf( '/', nPos1 );
+
+ if ( nPos1 == -1 )
+ return OUString();
+
+ return aURL.copy( 0, nPos + 1 );
+}
+
+
+// Non-interface methods.
+
+
+// static
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ const ContentProperties& rData,
+ const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider,
+ const OUString& rContentId )
+{
+ // Note: Empty sequence means "get values of all supported properties".
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ if ( rProperties.hasElements() )
+ {
+ uno::Reference< beans::XPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ for ( const beans::Property& rProp : rProperties )
+ {
+ // Process standard UCB, DAV and HTTP properties.
+ const uno::Any & rValue = rData.getValue( rProp.Name );
+ if ( rValue.hasValue() )
+ {
+ xRow->appendObject( rProp, rValue );
+ }
+ else
+ {
+ // Process local Additional Properties.
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet =
+ rProvider->getAdditionalPropertySet( rContentId,
+ false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( !xAdditionalPropSet.is() ||
+ !xRow->appendPropertySetValue(
+ xAdditionalPropSet, rProp ) )
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Append all standard UCB, DAV and HTTP properties.
+ const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
+
+ ContentProvider * pProvider
+ = static_cast< ContentProvider * >( rProvider.get() );
+ beans::Property aProp;
+
+ for ( const auto& rProp : *xProps )
+ {
+ pProvider->getProperty( rProp.first, aProp );
+ xRow->appendObject( aProp, rProp.second.value() );
+ }
+
+ // Append all local Additional Properties.
+ uno::Reference< beans::XPropertySet > xSet =
+ rProvider->getAdditionalPropertySet( rContentId, false );
+ xRow->appendPropertySet( xSet );
+ }
+
+ return uno::Reference< sdbc::XRow >( xRow.get() );
+}
+
+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 caught 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,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< ContentProperties > xProps;
+ std::unique_ptr< ContentProperties > xCachedProps;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ OUString aUnescapedTitle;
+ bool bHasAll = false;
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ::ucbhelper::ContentProviderImplHelper > xProvider;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aUnescapedTitle = NeonUri::unescape( m_aEscapedTitle );
+ xIdentifier.set( m_xIdentifier );
+ xProvider.set( m_xProvider.get() );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+
+ // First, ask cache...
+ if (m_xCachedProps)
+ {
+ xCachedProps.reset(new ContentProperties(*m_xCachedProps));
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset(new ContentProperties(*xCachedProps));
+ }
+ }
+
+ if ( !m_bTransient && !bHasAll )
+ {
+
+ // Obtain values from server...
+
+
+ // First, identify whether resource is DAV or not
+ bool bNetworkAccessAllowed = true;
+ ResourceType eType = getResourceType(
+ xEnv, xResAccess, &bNetworkAccessAllowed );
+
+ if ( eType == DAV )
+ {
+ // cache lookup... getResourceType may fill the props cache via
+ // PROPFIND!
+ if (m_xCachedProps)
+ {
+ xCachedProps.reset(new ContentProperties(*m_xCachedProps));
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access
+ // needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset(new ContentProperties(*xCachedProps));
+ }
+
+ if ( !bHasAll )
+ {
+ // Only DAV resources support PROPFIND
+ std::vector< OUString > aPropNames;
+
+ uno::Sequence< beans::Property > aProperties(
+ rProperties.getLength() );
+
+ if ( !m_aFailedPropNames.empty() )
+ {
+ sal_Int32 nProps = rProperties.getLength();
+ std::copy(rProperties.begin(), rProperties.end(), aProperties.begin());
+
+ aProperties.realloc( nProps );
+ }
+ else
+ {
+ aProperties = rProperties;
+ }
+
+ if ( aProperties.hasElements() )
+ ContentProperties::UCBNamesToDAVNames(
+ aProperties, aPropNames );
+
+ if ( !aPropNames.empty() )
+ {
+ std::vector< DAVResource > resources;
+ try
+ {
+ xResAccess->PROPFIND(
+ DAVZERO, aPropNames, resources, xEnv );
+
+ if ( 1 == resources.size() )
+ {
+#if defined SAL_LOG_INFO
+ {//debug
+ // print received resources
+ for ( const auto& rProp : resources[0].properties )
+ {
+ OUString aPropValue;
+ bool bValue;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if( rProp.Value >>= aPropValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" << aPropValue );
+ else if( rProp.Value >>= bValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" <<
+ ( bValue ? "true" : "false" ) );
+ else if( rProp.Value >>= aSupportedLocks )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << rProp.Name << ":" );
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ SAL_INFO( "ucb.ucp.webdav"," scope: "
+ << ( aSupportedLocks[ n ].Scope == css::ucb::LockScope_SHARED ? "shared" : "exclusive" )
+ << ", type: "
+ << ( aSupportedLocks[ n ].Type != css::ucb::LockType_WRITE ? "" : "write" ) );
+ }
+ }
+ }
+ }
+#endif
+ if (xProps)
+ xProps->addProperties(
+ aPropNames,
+ ContentProperties( resources[ 0 ] ));
+ else
+ xProps.reset(
+ new ContentProperties( resources[ 0 ] ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ bNetworkAccessAllowed = bNetworkAccessAllowed
+ && shouldAccessNetworkAfterException( e );
+
+ if ( !bNetworkAccessAllowed )
+ {
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+ }
+ }
+ }
+ }
+
+ if ( bNetworkAccessAllowed )
+ {
+ // All properties obtained already?
+ std::vector< OUString > aMissingProps;
+ if ( !( xProps
+ && xProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ && !m_bDidGetOrHead )
+ {
+ // Possibly the missing props can be obtained using a HEAD
+ // request.
+
+ std::vector< OUString > aHeaderNames;
+ ContentProperties::UCBNamesToHTTPNames(
+ rProperties,
+ aHeaderNames );
+
+ if( eType != DAV )
+ {
+ // in case of not DAV PROFIND (previously in program flow) failed
+ // so we need to add the only prop that's common
+ // to DAV and NON_DAV: MediaType, that maps to Content-Type
+ aHeaderNames.emplace_back("Content-Type" );
+ }
+
+ if (!aHeaderNames.empty()) try
+ {
+ DAVResource resource;
+ GetPropsUsingHeadRequest(resource, xResAccess, aHeaderNames, xEnv);
+ m_bDidGetOrHead = true;
+
+ 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 )
+ {
+ // 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 )
+ {
+ // 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;
+ }
+ }
+
+ 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
+ }
+ }
+ }
+ }
+ }
+
+ // might trigger HTTP redirect.
+ // Therefore, title must be updated here.
+ NeonUri aUri( xResAccess->getURL() );
+ aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
+
+ if ( eType == UNKNOWN )
+ {
+ xProps.reset( new ContentProperties( aUnescapedTitle ) );
+ }
+
+ // For DAV resources we only know the Title, for non-DAV
+ // resources we additionally know that it is a document.
+
+ if ( eType == DAV )
+ {
+ if (!xProps)
+ xProps.reset(new ContentProperties(aUnescapedTitle));
+ else
+ xProps->addProperty("Title", uno::makeAny(aUnescapedTitle), true);
+ }
+ else
+ {
+ if (!xProps)
+ xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
+ else
+ xProps->addProperty(
+ "Title",
+ uno::makeAny( aUnescapedTitle ),
+ true );
+
+ xProps->addProperty(
+ "IsFolder",
+ uno::makeAny( false ),
+ true );
+ xProps->addProperty(
+ "IsDocument",
+ uno::makeAny( true ),
+ true );
+ }
+ }
+ else
+ {
+ // No server access for just created (not yet committed) objects.
+ // Only a minimal set of properties supported at this stage.
+ if (m_bTransient)
+ xProps.reset( new ContentProperties( aUnescapedTitle,
+ m_bCollection ) );
+ }
+
+ // Add a default for the properties requested but not found.
+ // Determine still missing properties, add a default.
+ // Some client function doesn't expect a void uno::Any,
+ // but instead wants some sort of default.
+ std::vector< OUString > aMissingProps;
+ if ( !xProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ {
+ //
+ for ( const auto& rProp : aMissingProps )
+ {
+ // For the time being only a couple of properties need to be added
+ if ( rProp == "DateModified" || rProp == "DateCreated" )
+ {
+ util::DateTime aDate;
+ xProps->addProperty(
+ rProp,
+ 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 ( rProp == "IsFolder" )
+ {
+ xProps->addProperty(
+ rProp,
+ uno::makeAny( false ),
+ true );
+ }
+ else if ( rProp == "IsDocument" )
+ {
+ xProps->addProperty(
+ rProp,
+ uno::makeAny( true ),
+ true );
+ }
+ }
+ }
+
+ for ( const auto& rProperty : rProperties )
+ {
+ const OUString rName = rProperty.Name;
+ if ( rName == "BaseURI" )
+ {
+ // Add BaseURI property, if requested.
+ xProps->addProperty(
+ "BaseURI",
+ uno::makeAny( getBaseURI( xResAccess ) ),
+ true );
+ }
+ else if ( rName == "CreatableContentsInfo" )
+ {
+ // Add CreatableContentsInfo property, if requested.
+ bool bFolder = false;
+ xProps->getValue(
+ "IsFolder" )
+ >>= bFolder;
+ xProps->addProperty(
+ "CreatableContentsInfo",
+ uno::makeAny( bFolder
+ ? queryCreatableContentsInfo()
+ : uno::Sequence< ucb::ContentInfo >() ),
+ true );
+ }
+ }
+
+ uno::Reference< sdbc::XRow > xResultRow
+ = getPropertyValues( m_xContext,
+ rProperties,
+ *xProps,
+ xProvider,
+ xIdentifier->getContentIdentifier() );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if (!m_xCachedProps)
+ m_xCachedProps.reset(new CachableContentProperties(*xProps));
+ else
+ m_xCachedProps->addProperties(*xProps);
+
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ m_aEscapedTitle = NeonUri::escapeSegment( aUnescapedTitle );
+ }
+
+ return xResultRow;
+}
+
+
+uno::Sequence< uno::Any > Content::setPropertyValues(
+ const uno::Sequence< beans::PropertyValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ContentProvider > xProvider;
+ bool bTransient;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xProvider.set( m_pProvider );
+ xIdentifier.set( m_xIdentifier );
+ bTransient = m_bTransient;
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ uno::Sequence< uno::Any > aRet( rValues.getLength() );
+ uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
+ sal_Int32 nChanged = 0;
+
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ // aEvent.PropertyName =
+ aEvent.PropertyHandle = -1;
+ // aEvent.OldValue =
+ // aEvent.NewValue =
+
+ std::vector< ProppatchValue > aProppatchValues;
+
+ uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ bool bExchange = false;
+ OUString aNewTitle;
+ OUString aOldTitle;
+ sal_Int32 nTitlePos = -1;
+
+ uno::Reference< beans::XPropertySetInfo > xInfo;
+
+ const beans::PropertyValue* pValues = rValues.getConstArray();
+ sal_Int32 nCount = rValues.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::PropertyValue& rValue = pValues[ n ];
+ const OUString & rName = rValue.Name;
+
+ beans::Property aTmpProp;
+ xProvider->getProperty( rName, aTmpProp );
+
+ if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+
+ // Mandatory props.
+
+
+ if ( rName == "ContentType" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsDocument" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsFolder" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "Title" )
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty titles!
+ if ( !aNewValue.isEmpty() )
+ {
+ try
+ {
+ NeonUri aURI( xIdentifier->getContentIdentifier() );
+ aOldTitle = aURI.GetPathBaseNameUnescaped();
+
+ if ( aNewValue != aOldTitle )
+ {
+ // modified title -> modified URL -> exchange !
+ if ( !bTransient )
+ bExchange = true;
+
+ // new value will be set later...
+ aNewTitle = aNewValue;
+
+ // remember position within sequence of values (for
+ // error handling).
+ nTitlePos = n;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ aRet[ n ] <<= lang::IllegalArgumentException(
+ "Invalid content identifier!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= lang::IllegalArgumentException(
+ "Empty title not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= beans::IllegalTypeException(
+ "Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else
+ {
+
+ // Optional props.
+
+
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty(
+ rName, aSpecialName );
+
+ if ( !xInfo.is() )
+ xInfo = getPropertySetInfo( xEnv,
+ false /* don't cache data */ );
+
+ if ( !xInfo->hasPropertyByName(
+ bIsSpecial ? aSpecialName : rName ) )
+ {
+ // Check, whether property exists. Skip otherwise.
+ // PROPPATCH::set would add the property automatically, which
+ // is not allowed for "setPropertyValues" command!
+ aRet[ n ] <<= beans::UnknownPropertyException(
+ "Property is unknown!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+ if ( rName == "Size" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateCreated" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateModified" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "MediaType" )
+ {
+ // Read-only property!
+ // (but could be writable, if 'getcontenttype' would be)
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ if ( rName == "CreatableContentsInfo" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ if ( getResourceType( xEnv, xResAccess ) == DAV )
+ {
+ // Property value will be set on server.
+ ProppatchValue aValue( PROPSET, rName, rValue.Value );
+ aProppatchValues.push_back( aValue );
+ }
+ else
+ {
+ // Property value will be stored in local property store.
+ if ( !bTriedToGetAdditionalPropSet &&
+ !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet
+ = getAdditionalPropertySet( false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ try
+ {
+ uno::Any aOldValue
+ = xAdditionalPropSet->getPropertyValue( rName );
+ if ( aOldValue != rValue.Value )
+ {
+ xAdditionalPropSet->setPropertyValue(
+ rName, rValue.Value );
+
+ aEvent.PropertyName = rName;
+ aEvent.OldValue = aOldValue;
+ aEvent.NewValue = rValue.Value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( beans::UnknownPropertyException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( lang::WrappedTargetException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( beans::PropertyVetoException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( lang::IllegalArgumentException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= uno::Exception(
+ "No property set for storing the value!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ }
+ } // for
+
+ if ( !bTransient && !aProppatchValues.empty() )
+ {
+ try
+ {
+ // clean cached value of PROPFIND property names
+ // PROPPATCH can change them
+ removeCachedPropertyNames( xResAccess->getURL() );
+ // Set property values at server.
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+
+ for ( const auto& rProppatchValue : aProppatchValues )
+ {
+ aEvent.PropertyName = rProppatchValue.name;
+ aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
+ aEvent.NewValue = rProppatchValue.value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::setPropertyValues - PROPPATCH failed!" );
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+ }
+
+ if ( bExchange )
+ {
+ // Assemble new content identifier...
+
+ OUString aNewURL = getParentURL();
+ if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
+ aNewURL += "/";
+
+ aNewURL += NeonUri::escapeSegment( aNewTitle );
+
+ uno::Reference< ucb::XContentIdentifier > xNewId
+ = new ::ucbhelper::ContentIdentifier( aNewURL );
+
+ NeonUri sourceURI( xIdentifier->getContentIdentifier() );
+ NeonUri targetURI( xNewId->getContentIdentifier() );
+
+ try
+ {
+ targetURI.SetScheme( sourceURI.GetScheme() );
+
+ // clean cached value of PROPFIND property names
+ removeCachedPropertyNames( sourceURI.GetURI() );
+ removeCachedPropertyNames( targetURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ xResAccess->MOVE(
+ sourceURI.GetPath(), targetURI.GetURI(), false, xEnv );
+
+ // @@@ Should check for resources that could not be moved
+ // (due to source access or target overwrite) and send
+ // this information through the interaction handler.
+
+ // @@@ Existing content should be checked to see if it needs
+ // to be deleted at the source
+
+ // @@@ Existing content should be checked to see if it has
+ // been overwritten at the target
+
+ if ( exchangeIdentity( xNewId ) )
+ {
+ xResAccess->setURL( aNewURL );
+
+// DAV resources store all additional props on server!
+// // Adapt Additional Core Properties.
+// renameAdditionalPropertySet( xOldId->getContentIdentifier(),
+// xNewId->getContentIdentifier(),
+// sal_True );
+ }
+ else
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRet[ nTitlePos ] <<= uno::Exception(
+ "Exchange failed!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRet[ nTitlePos ] = MapDAVException( e, true );
+ }
+ }
+
+ if ( !aNewTitle.isEmpty() )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aEvent.PropertyName = "Title";
+ aEvent.OldValue <<= aOldTitle;
+ aEvent.NewValue <<= aNewTitle;
+
+ m_aEscapedTitle = NeonUri::escapeSegment( aNewTitle );
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+
+ if ( nChanged > 0 )
+ {
+ aChanges.realloc( nChanged );
+ notifyPropertiesChange( aChanges );
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ return aRet;
+}
+
+
+uno::Any Content::open(
+ const ucb::OpenCommandArgument3 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Any aRet;
+
+ bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
+ ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
+ if ( bOpenFolder )
+ {
+ if ( isFolder( xEnv ) )
+ {
+ // Open collection.
+
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet( m_xContext, this, rArg, xEnv );
+ aRet <<= xSet;
+ }
+ else
+ {
+ // Error: Not a folder!
+
+ OUStringBuffer aMsg;
+ if ( getResourceType( xEnv ) == FTP )
+ {
+ aMsg.append( "FTP over HTTP proxy: resource cannot "
+ "be opened as folder! Wrong Open Mode!" );
+ }
+ else
+ {
+ aMsg.append( "Non-folder resource cannot be "
+ "opened as folder! Wrong Open Mode!" );
+ }
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ lang::IllegalArgumentException(
+ aMsg.makeStringAndClear(),
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+
+ if ( rArg.Sink.is() )
+ {
+ // Open document.
+
+ if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
+ {
+ // Currently(?) unsupported.
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedOpenModeException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ sal_Int16( rArg.Mode ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
+ if ( xOut.is() )
+ {
+ // PUSH: write data
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ xResAccess->setFlags( rArg.OpeningFlags );
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->GET( xOut, aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if (!m_xCachedProps)
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties( ContentProperties( aResource ) );
+
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xDataSink.is() )
+ {
+ // PULL: wait for client read
+ OUString aTargetURL = m_xIdentifier->getContentIdentifier();
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ xResAccess->setFlags( rArg.OpeningFlags );
+
+ // fill inputstream sync; return if all data present
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ aTargetURL = xResAccess->getURL();
+ removeCachedPropertyNames( aTargetURL );
+ // check if the resource was present on the server
+ // first update it, if necessary
+ // if the open is called directly, without the default open sequence,
+ // e.g. the one used when opening a file looking for properties
+ // first this call will have no effect, since OPTIONS would have already been called
+ // as a consequence of getPropertyValues()
+ DAVOptions aDAVOptions;
+ getResourceOptions( xEnv, aDAVOptions, xResAccess );
+
+ 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() );
+ }
+ uno::Reference< io::XInputStream > xIn
+ = xResAccess->GET( aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if (!m_xCachedProps)
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties(
+ aResource.properties );
+
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ xDataSink->setInputStream( xIn );
+ }
+ catch ( DAVException const & e )
+ {
+ //TODO cache the http error if not yet cached
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ // Note: aOpenCommand.Sink may contain an XStream
+ // implementation. Support for this type of
+ // sink is optional...
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+ }
+
+ return aRet;
+}
+
+void Content::post(
+ const ucb::PostCommandArgument2 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xSink.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ uno::Reference< io::XInputStream > xResult
+ = xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ xSink->setInputStream( xResult );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
+ if ( xResult.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xResult,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+}
+
+
+void Content::queryChildren( ContentRefList& rChildren )
+{
+ // Obtain a list with a snapshot of all currently instantiated contents
+ // from provider and extract the contents which are direct children
+ // of this content.
+
+ ::ucbhelper::ContentRefList aAllContents;
+ m_xProvider->queryExistingContents( aAllContents );
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
+
+ if ( nURLPos != ( aURL.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aURL += "/";
+ }
+
+ sal_Int32 nLen = aURL.getLength();
+
+ for ( const auto& rChild : aAllContents )
+ {
+ ::ucbhelper::ContentImplHelperRef xChild = rChild;
+ OUString aChildURL
+ = xChild->getIdentifier()->getContentIdentifier();
+
+ // Is aURL a prefix of aChildURL?
+ if ( ( aChildURL.getLength() > nLen ) &&
+ ( aChildURL.startsWith( aURL ) ) )
+ {
+ sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
+
+ if ( ( nPos == -1 ) ||
+ ( nPos == ( aChildURL.getLength() - 1 ) ) )
+ {
+ // No further slashes / only a final slash. It's a child!
+ rChildren.emplace_back(
+ static_cast< ::webdav_ucp::Content * >(
+ xChild.get() ) );
+ }
+ }
+ }
+}
+
+
+void Content::insert(
+ const uno::Reference< io::XInputStream > & xInputStream,
+ bool bReplaceExisting,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ bool bTransient, bCollection;
+ OUString aEscapedTitle;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ bTransient = m_bTransient;
+ bCollection = m_bCollection;
+ aEscapedTitle = m_aEscapedTitle;
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ // Check, if all required properties are present.
+
+ if ( aEscapedTitle.isEmpty() )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
+
+ uno::Sequence<OUString> aProps { "Title" };
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::MissingPropertiesException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ aProps ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !bReplaceExisting )
+ {
+ /* [RFC 2616] - HTTP
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server.
+ */
+
+ /* [RFC 2518] - WebDAV
+
+ MKCOL creates a new collection resource at the location specified by
+ the Request-URI. If the resource identified by the Request-URI is
+ non-null then the MKCOL MUST fail.
+ */
+
+ // ==> Complain on PUT, continue on MKCOL.
+ if ( !bTransient || !bCollection )
+ {
+ ucb::UnsupportedNameClashException aEx(
+ "Unable to write without overwrite!",
+ static_cast< cppu::OWeakObject * >( this ),
+ ucb::NameClash::ERROR );
+
+ uno::Reference< task::XInteractionHandler > xIH;
+
+ if ( Environment.is() )
+ xIH = Environment->getInteractionHandler();
+
+ if ( !xIH.is() )
+ {
+ // No IH; throw.
+ throw aEx;
+ }
+
+ uno::Any aExAsAny( uno::makeAny( aEx ) );
+
+ rtl::Reference< ucbhelper::SimpleInteractionRequest > xRequest
+ = new ucbhelper::SimpleInteractionRequest(
+ aExAsAny,
+ ContinuationFlags::Approve | ContinuationFlags::Disapprove );
+ xIH->handle( xRequest.get() );
+
+ const ContinuationFlags nResp = xRequest->getResponse();
+
+ switch ( nResp )
+ {
+ case ContinuationFlags::NONE:
+ // Not handled; throw.
+ throw aEx;
+// break;
+
+ case ContinuationFlags::Approve:
+ // Continue -> Overwrite.
+ bReplaceExisting = true;
+ break;
+
+ case ContinuationFlags::Disapprove:
+ // Abort.
+ throw ucb::CommandFailedException(
+ OUString(),
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav", "Content::insert - "
+ "Unknown interaction selection!" );
+ throw ucb::CommandFailedException(
+ "Unknown interaction selection!",
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+ }
+
+ }
+ }
+
+ if ( bTransient )
+ {
+ // Assemble new content identifier...
+ OUString aURL = getParentURL();
+ if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += aEscapedTitle;
+
+ try
+ {
+ xResAccess->setURL( aURL );
+
+ if ( bCollection )
+ {
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->MKCOL( Environment );
+ }
+ else
+ {
+ // remove options from cache, PUT may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->PUT( xInputStream, Environment );
+ // clean cached value of PROPFIND properties names
+ }
+ // 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 )
+ {
+ if ( bCollection )
+ {
+ if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // [RFC 2518] - WebDAV
+ // 405 (Method Not Allowed) - MKCOL can only be
+ // executed on a deleted/non-existent resource.
+
+ if ( bReplaceExisting )
+ {
+ // Destroy old resource.
+ try
+ {
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->DESTROY( Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ // Insert (recursion!).
+ insert( xInputStream, bReplaceExisting, Environment );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+
+ // Success!
+ return;
+ }
+ else
+ {
+ OUString aTitle;
+ try
+ {
+ NeonUri aURI( aURL );
+ aTitle = aURI.GetPathBaseNameUnescaped();
+ }
+ catch ( DAVException const & )
+ {
+ }
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTitle ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( except, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xIdentifier = new ::ucbhelper::ContentIdentifier( aURL );
+ }
+
+ inserted();
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_bTransient = false;
+ }
+ }
+ else
+ {
+ if ( !xInputStream.is() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::MissingInputStreamException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ // save the URL since it may change due to redirection
+ OUString aTargetUrl = xResAccess->getURL();
+ try
+ {
+ removeCachedPropertyNames( xResAccess->getURL() );
+ // remove options from cache, PUT may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( aTargetUrl );
+ xResAccess->PUT( xInputStream, Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+}
+
+
+void Content::transfer(
+ const ucb::TransferInfo & rArgs,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ uno::Reference< ucb::XContentProvider > xProvider;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xIdentifier.set( m_xIdentifier );
+ xProvider.set( m_xProvider.get() );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ NeonUri sourceURI( rArgs.SourceURL );
+ NeonUri targetURI( xIdentifier->getContentIdentifier() );
+
+ OUString aTargetURI;
+ try
+ {
+ aTargetURI = targetURI.GetPathBaseNameUnescaped();
+
+ // Check source's and target's URL scheme
+
+ OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if ( aScheme == DAV_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == DAVS_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if ( aScheme == WEBDAV_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == WEBDAVS_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else
+ {
+ if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::InteractiveBadTransferURLException(
+ "Unsupported URL scheme!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ aScheme = targetURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if ( aScheme == DAV_URL_SCHEME )
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == DAVS_URL_SCHEME )
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if ( aScheme == WEBDAV_URL_SCHEME )
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == WEBDAVS_URL_SCHEME )
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+
+ // @@@ This implementation of 'transfer' only works
+ // if the source and target are located at same host.
+ // (Neon does not support cross-server copy/move)
+
+ // Check for same host
+
+ if ( !sourceURI.GetHost().isEmpty() &&
+ ( sourceURI.GetHost() != targetURI.GetHost() ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::InteractiveBadTransferURLException(
+ "Different hosts!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ OUString aTitle = rArgs.NewTitle;
+
+ if ( aTitle.isEmpty() )
+ aTitle = sourceURI.GetPathBaseNameUnescaped();
+
+ if ( aTitle == "/" )
+ {
+ // kso: ???
+ aTitle.clear();
+ }
+
+ targetURI.AppendPath( aTitle );
+
+ OUString aTargetURL = xIdentifier->getContentIdentifier();
+ if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
+ != aTargetURL.getLength() )
+ aTargetURL += "/";
+
+ aTargetURL += aTitle;
+
+ uno::Reference< ucb::XContentIdentifier > xTargetId
+ = new ::ucbhelper::ContentIdentifier( aTargetURL );
+
+ DAVResourceAccess aSourceAccess( m_xContext,
+ xResAccess->getSessionFactory(),
+ sourceURI.GetURI() );
+
+ if ( rArgs.MoveData )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xSource
+ = static_cast< Content * >(
+ xProvider->queryContent( xId ).get() );
+
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the move the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ aSourceAccess.MOVE( sourceURI.GetPath(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+ if ( xSource.is() )
+ {
+ // Propagate destruction to listeners.
+ xSource->destroy( true );
+ }
+
+// DAV resources store all additional props on server!
+// // Rename own and all children's Additional Core Properties.
+// renameAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// sal_True );
+ }
+ else
+ {
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the copy the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
+ aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
+ aSourceAccess.COPY( sourceURI.GetPath(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+// DAV resources store all additional props on server!
+// // Copy own and all children's Additional Core Properties.
+// copyAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// sal_True );
+ }
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xTarget
+ = static_cast< Content * >(
+ xProvider->queryContent( xTargetId ).get() );
+
+ // Announce transferred content in its new folder.
+ xTarget->inserted();
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+ catch ( DAVException const & e )
+ {
+ // [RFC 2518] - WebDAV
+ // 412 (Precondition Failed) - The server was unable to maintain
+ // the liveness of the properties listed in the propertybehavior
+ // XML element or the Overwrite header is "F" and the state of
+ // the destination resource is non-null.
+
+ if ( e.getStatus() == SC_PRECONDITION_FAILED )
+ {
+ switch ( rArgs.NameClash )
+ {
+ case ucb::NameClash::ERROR:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTargetURI ) ),
+ Environment );
+ [[fallthrough]]; // Unreachable
+ }
+
+ case ucb::NameClash::OVERWRITE:
+ break;
+
+ case ucb::NameClash::KEEP: // deprecated
+ case ucb::NameClash::RENAME:
+ case ucb::NameClash::ASK:
+ default:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedNameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArgs.NameClash ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+}
+
+
+void Content::destroy( bool bDeletePhysical )
+{
+ // @@@ take care about bDeletePhysical -> trashcan support
+ uno::Reference< ucb::XContent > xThis = this;
+
+ deleted();
+
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Process instantiated children...
+
+ ::webdav_ucp::Content::ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( auto& rChild : aChildren )
+ {
+ rChild->destroy( bDeletePhysical );
+ }
+}
+
+// returns the resource type, to be checked for locks
+Content::ResourceType Content::resourceTypeForLocks(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess)
+{
+ 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
+ if (m_xCachedProps)
+ {
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
+ >>= aSupportedLocks ) //get the cached value for supportedlock
+ {
+ for ( const auto& rSupportedLock : std::as_const(aSupportedLocks) )
+ {
+ if ( rSupportedLock.Scope
+ == ucb::LockScope_EXCLUSIVE &&
+ rSupportedLock.Type
+ == ucb::LockType_WRITE )
+ eResourceTypeForLocks = DAV;
+ }
+ }
+ }
+ }
+
+ const OUString & rURL = m_xIdentifier->getContentIdentifier();
+
+ if ( eResourceTypeForLocks == UNKNOWN )
+ {
+ // resource type for lock/unlock operations still unknown, need to ask the server
+
+ const OUString aScheme(
+ rURL.copy( 0, rURL.indexOf( ':' ) ).toAsciiLowerCase() );
+
+ if ( aScheme == FTP_URL_SCHEME )
+ {
+ eResourceTypeForLocks = FTP;
+ }
+ else
+ {
+ DAVOptions aDAVOptions;
+ getResourceOptions( Environment, aDAVOptions, rResAccess );
+ if( aDAVOptions.isClass1() ||
+ aDAVOptions.isClass2() ||
+ aDAVOptions.isClass3() )
+ {
+ // 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() )
+ {
+ // ok, possible lock, check for it
+ 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 );
+ rResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );
+
+ bool wasSupportedlockFound = false;
+
+ // 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
+
+ for ( const auto& rProp : resources[0].properties )
+ {
+ if ( rProp.Name == DAVProperties::SUPPORTEDLOCK )
+ {
+ wasSupportedlockFound = true;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( rProp.Value >>= aSupportedLocks )
+ {
+ bool isSupported = std::any_of(aSupportedLocks.begin(), aSupportedLocks.end(),
+ [](const ucb::LockEntry& rLock) {
+ // 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
+ return rLock.Scope == ucb::LockScope_EXCLUSIVE
+ && rLock.Type == ucb::LockType_WRITE;
+ });
+ if (isSupported)
+ {
+ // requested locking mode is supported
+ eResourceTypeForLocks = DAV;
+ SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
+ }
+ break;
+ }
+ }
+ }
+ }
+ 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
+ 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
+ // 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 ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = NON_DAV;
+ break;
+ default:
+ //fallthrough
+ SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ eResourceTypeForLocks = UNKNOWN;
+ }
+ }
+ }
+ }
+ else
+ eResourceTypeForLocks = NON_DAV;
+
+ }
+ }
+ 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;
+}
+
+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 )
+{
+// prepare aURL to be used in exception, see below
+ OUString aURL;
+ if ( m_bTransient )
+ {
+ aURL = getParentURL();
+ if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += m_aEscapedTitle;
+ }
+ else
+ {
+ aURL = m_xIdentifier->getContentIdentifier();
+ }
+
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ uno::Any aOwnerAny;
+ aOwnerAny
+ <<= OUString("LibreOffice - http://www.libreoffice.org/");
+
+ ucb::Lock aLock(
+ ucb::LockScope_EXCLUSIVE,
+ ucb::LockType_WRITE,
+ ucb::LockDepth_ZERO,
+ aOwnerAny,
+ 180, // lock timeout in secs
+ //-1, // infinite lock
+ uno::Sequence< OUString >() );
+
+ // OPTIONS may change as a consequence of the lock operation
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->LOCK( aLock, Environment );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ // check if the exception thrown is 'already locked'
+ // 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() )
+ {
+ case DAVException::DAV_LOCKED:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "lock() resource already locked - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">");
+ throw
+ ucb::InteractiveLockingLockedException(
+ "Locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false );
+ }
+ break;
+ case DAVException::DAV_HTTP_AUTH:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "lock() DAVException Authentication error - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">" );
+ // DAVException::DAV_HTTP_AUTH exception can mean:
+ // - interaction handler for credential management not present (happens, depending
+ // on the LO framework processing)
+ // - the remote site is a WebDAV with special configuration: read/only for read operations
+ // and read/write for write operations, the user is not allowed to lock/write and
+ // she cancelled the credentials request.
+ // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
+ // management that takes part in cancelCommandExecution() below
+ // Unfortunately there is no InteractiveNetwork*Exception available to signal this
+ // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
+ throw
+ ucb::InteractiveNetworkWriteException(
+ "Authentication error while trying to lock! Write only WebDAV perhaps?",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ }
+ break;
+ case DAVException::DAV_HTTP_ERROR:
+ //grab the error code
+ switch( e.getStatus() )
+ {
+ // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
+ // WebDAV connector which apparently fail on resource first creation
+ // rfc4918 section-7.3 (see link below)
+ case SC_NOT_FOUND: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
+ // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
+ // in SharePoint when locking the resource on first creation fails due to this:
+ // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
+ // (retrieved on 2015-08-14)
+ case SC_PRECONDITION_FAILED: // <http://tools.ietf.org/html/rfc7232#section-4.2>
+ // 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>
+ SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << 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
+ // the first PUT, but if LOCK is not supported
+ // (simple web or DAV with lock disabled) we end with one of these http
+ // errors.
+ // These same errors may be reported when the LOCK on an unmapped
+ // (i.e. non existent) resource is not implemented.
+ // Detailed specification 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 successful
+ return;
+ break;
+ default:
+ //fallthrough
+ ;
+ }
+
+ SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ cancelCommandExecution( e, Environment );
+ // Unreachable
+ }
+}
+
+
+void Content::unlock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+
+ // check if the target URL is a Class1 DAV
+ DAVOptions aDAVOptions;
+ getResourceOptions( Environment, aDAVOptions, xResAccess );
+
+ // at least class one is needed
+ if( aDAVOptions.isClass1() )
+ {
+ // remove options from cache, unlock may change it
+ // it will be refreshed when needed
+ aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
+ // clean cached value of PROPFIND properties names
+ removeCachedPropertyNames( xResAccess->getURL() );
+ xResAccess->UNLOCK( Environment );
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ switch( e.getError() )
+ {
+ 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() )
+ {
+ // 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>
+ SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ return;
+ break;
+ default:
+ //fallthrough
+ ;
+ }
+ break;
+ default:
+ //fallthrough
+ ;
+ }
+ SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
+ << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+ cancelCommandExecution( e, Environment );
+ // Unreachable
+ }
+}
+
+
+bool Content::exchangeIdentity(
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ if ( !xNewId.is() )
+ return false;
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Already persistent?
+ if ( m_bTransient )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
+ return false;
+ }
+
+ // Exchange own identitity.
+
+ // Fail, if a content with given id already exists.
+// if ( !hasData( xNewId ) )
+ {
+ OUString aOldURL = m_xIdentifier->getContentIdentifier();
+
+ aGuard.clear();
+ if ( exchange( xNewId ) )
+ {
+ // Process instantiated children...
+
+ ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( const auto& rChild : aChildren )
+ {
+ ContentRef xChild = rChild;
+
+ // Create new content identifier for the child...
+ uno::Reference< ucb::XContentIdentifier >
+ xOldChildId = xChild->getIdentifier();
+ OUString aOldChildURL
+ = xOldChildId->getContentIdentifier();
+ OUString aNewChildURL
+ = aOldChildURL.replaceAt(
+ 0,
+ aOldURL.getLength(),
+ xNewId->getContentIdentifier() );
+ uno::Reference< ucb::XContentIdentifier > xNewChildId
+ = new ::ucbhelper::ContentIdentifier( aNewChildURL );
+
+ if ( !xChild->exchangeIdentity( xNewChildId ) )
+ return false;
+ }
+ return true;
+ }
+ }
+
+ SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - "
+ "Panic! Cannot exchange identity!" );
+ return false;
+}
+
+
+bool Content::isFolder(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_bTransient )
+ return m_bCollection;
+ }
+
+ uno::Sequence< beans::Property > aProperties( 1 );
+ aProperties[ 0 ].Name = "IsFolder";
+ aProperties[ 0 ].Handle = -1;
+ uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
+ if ( xRow.is() )
+ {
+ try
+ {
+ return xRow->getBoolean( 1 );
+ }
+ catch ( sdbc::SQLException const & )
+ {
+ }
+ }
+
+ return false;
+}
+
+
+uno::Any Content::MapDAVException( const DAVException & e, bool bWrite )
+{
+ // Map DAVException...
+ uno::Any aException;
+
+ OUString aURL;
+ if ( m_bTransient )
+ {
+ aURL = getParentURL();
+ if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += m_aEscapedTitle;
+ }
+ else
+ {
+ aURL = m_xIdentifier->getContentIdentifier();
+ }
+
+ switch ( e.getStatus() )
+ {
+ case SC_NOT_FOUND:
+ {
+ uno::Sequence< uno::Any > aArgs( 1 );
+ aArgs[ 0 ] <<= beans::PropertyValue(
+ "Uri", -1,
+ uno::makeAny(aURL),
+ beans::PropertyState_DIRECT_VALUE);
+
+ aException <<=
+ ucb::InteractiveAugmentedIOException(
+ "Not found!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ ucb::IOErrorCode_NOT_EXISTING,
+ aArgs );
+ return aException;
+ }
+ default:
+ break;
+ }
+
+ switch ( e.getError() )
+ {
+ case DAVException::DAV_HTTP_ERROR:
+ {
+ if ( bWrite )
+ aException <<=
+ ucb::InteractiveNetworkWriteException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ else
+ aException <<=
+ ucb::InteractiveNetworkReadException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+ }
+
+ case DAVException::DAV_HTTP_LOOKUP:
+ aException <<=
+ ucb::InteractiveNetworkResolveNameException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTH:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTHPROXY:
+// break;
+
+ case DAVException::DAV_HTTP_TIMEOUT:
+ case DAVException::DAV_HTTP_CONNECT:
+ aException <<=
+ ucb::InteractiveNetworkConnectException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_REDIRECT:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_SESSION_CREATE:
+// break;
+
+ case DAVException::DAV_INVALID_ARG:
+ aException <<=
+ lang::IllegalArgumentException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ break;
+
+ case DAVException::DAV_LOCKED:
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false ); // not SelfOwned
+ break;
+
+ case DAVException::DAV_LOCKED_SELF:
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked (self!)",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ true ); // SelfOwned
+ break;
+
+ case DAVException::DAV_NOT_LOCKED:
+ aException <<=
+ ucb::InteractiveLockingNotLockedException(
+ "Not locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ case DAVException::DAV_LOCK_EXPIRED:
+ aException <<=
+ ucb::InteractiveLockingLockExpiredException(
+ "Lock expired!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ default:
+ aException <<=
+ ucb::InteractiveNetworkGeneralException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR );
+ break;
+ }
+
+ return aException;
+}
+
+
+// static
+bool Content::shouldAccessNetworkAfterException( const DAVException & e )
+{
+ return !(( e.getStatus() == SC_NOT_FOUND ) ||
+ ( e.getStatus() == SC_GONE ) ||
+ ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_LOOKUP ) ||
+ ( e.getError() == DAVException::DAV_HTTP_CONNECT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTH ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTHPROXY ));
+}
+
+
+void Content::cancelCommandExecution(
+ const DAVException & e,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv,
+ bool bWrite /* = sal_False */ )
+{
+ ucbhelper::cancelCommandExecution( MapDAVException( e, bWrite ), xEnv );
+ // Unreachable
+}
+
+
+OUString
+Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // First, try to obtain value of response header "Content-Location".
+ if (m_xCachedProps)
+ {
+ OUString aLocation;
+ m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
+ if ( !aLocation.isEmpty() )
+ {
+ try
+ {
+ // Do not use m_xIdentifier->getContentIdentifier() because it
+ // for example does not reflect redirects applied to requests
+ // done using the original URI but m_xResAccess' URI does.
+ return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
+ aLocation );
+ }
+ catch ( rtl::MalformedUriException const & )
+ {
+ }
+ }
+ }
+
+ return 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,
+ bool * networkAccessAllowed)
+{
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType != UNKNOWN) {
+ return m_eResourceType;
+ }
+ }
+
+ ResourceType eResourceType = UNKNOWN;
+ DAVOptions aDAVOptions;
+
+ const OUString & rURL = rResAccess->getURL();
+ const OUString aScheme(
+ rURL.copy( 0, rURL.indexOf( ':' ) ).toAsciiLowerCase() );
+
+ if ( aScheme == FTP_URL_SCHEME )
+ {
+ eResourceType = FTP;
+ }
+ else
+ {
+ getResourceOptions( xEnv, aDAVOptions, rResAccess, networkAccessAllowed );
+
+ // at least class one is needed
+ if( aDAVOptions.isClass1() )
+ {
+ try
+ {
+ // Try to fetch some frequently used property value, e.g. those
+ // used when loading documents... along with identifying whether
+ // this is a DAV resource.
+ std::vector< DAVResource > resources;
+ std::vector< OUString > aPropNames;
+ uno::Sequence< beans::Property > aProperties( 5 );
+ aProperties[ 0 ].Name = "IsFolder";
+ aProperties[ 1 ].Name = "IsDocument";
+ aProperties[ 2 ].Name = "IsReadOnly";
+ aProperties[ 3 ].Name = "MediaType";
+ aProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
+
+ ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
+
+ rResAccess->PROPFIND( DAVZERO, aPropNames, resources, xEnv );
+
+ if ( resources.size() == 1 )
+ {
+#if defined SAL_LOG_INFO
+ {//debug
+ // print received resources
+ for ( const auto& rProp : resources[0].properties )
+ {
+ OUString aPropValue;
+ bool bValue;
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if(rProp.Value >>= aPropValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" << aPropValue );
+ else if( rProp.Value >>= bValue )
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" <<
+ ( bValue ? "true" : "false" ) );
+ else if( rProp.Value >>= aSupportedLocks )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << rProp.Name << ":" );
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n <<"]: scope: "
+ << ( aSupportedLocks[ n ].Scope == css::ucb::LockScope_SHARED ? "shared" : "exclusive" )
+ << ", type: "
+ << ( aSupportedLocks[ n ].Type != css::ucb::LockType_WRITE ? "" : "write" ) );
+ }
+ }
+ }
+ }
+#endif
+ osl::MutexGuard g(m_aMutex);
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
+ m_xCachedProps->containsAllNames(
+ aProperties, m_aFailedPropNames );
+ }
+ eResourceType = DAV;
+ }
+ catch ( DAVException const & e )
+ {
+ rResAccess->resetUri();
+
+ SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
+
+ if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
+ // resource is NON_DAV
+ eResourceType = NON_DAV;
+ }
+ else if (networkAccessAllowed != nullptr)
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ if ( e.getStatus() == SC_NOT_FOUND )
+ {
+ // arrives here if OPTIONS is still cached for a resource previously available
+ // operate on the OPTIONS cache:
+ // if OPTIONS was not found, do nothing
+ // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
+ // not found and adjust lifetime accordingly
+ DAVOptions aDAVOptionsInner;
+ if( aStaticDAVOptionsCache.getDAVOptions( rURL, aDAVOptionsInner ) )
+ {
+ // TODO? get redirected url
+ aDAVOptionsInner.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptionsInner.setHttpResponseStatusText( e.getData() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsInner,
+ m_nOptsCacheLifeNotFound );
+ }
+ }
+ // if the two net events below happen, something
+ // is going on to the connection so break the command flow
+ if ( ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_CONNECT ) )
+ {
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+ }
+ }
+ else
+ {
+ rResAccess->resetUri();
+
+ // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
+ if ( aDAVOptions.getHttpResponseStatusCode() == USC_CONNECTION_TIMED_OUT )
+ {
+ // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
+ try
+ {
+ // extract host name and connection port
+ NeonUri theUri( rURL );
+ const OUString& aHostName = theUri.GetHost();
+ sal_Int32 nPort = theUri.GetPort();
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ NeonUri::makeConnectionEndPointString( aHostName,
+ nPort ) );
+ }
+ catch ( DAVException& exp )
+ {
+ cancelCommandExecution( exp, xEnv );
+ }
+ }
+
+ if ( aDAVOptions.getHttpResponseStatusCode() != SC_NOT_FOUND &&
+ aDAVOptions.getHttpResponseStatusCode() != SC_GONE ) // the cached OPTIONS can have SC_GONE
+ {
+ eResourceType = NON_DAV;
+ }
+ else
+ {
+ //resource doesn't exist
+ if ( networkAccessAllowed != nullptr )
+ *networkAccessAllowed = false; }
+ }
+ }
+
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType == UNKNOWN) {
+ m_eResourceType = eResourceType;
+ } else {
+ SAL_WARN_IF(
+ eResourceType != m_eResourceType, "ucb.ucp.webdav",
+ "different resource types for <" << rURL << ">: "
+ << +eResourceType << " vs. " << +m_eResourceType);
+ }
+ SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <"<<rURL<<">: " << m_eResourceType );
+ return m_eResourceType;
+}
+
+
+Content::ResourceType Content::getResourceType(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ }
+ Content::ResourceType const ret = getResourceType( xEnv, xResAccess );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(new DAVResourceAccess(*xResAccess));
+ }
+ return ret;
+}
+
+
+void Content::initOptsCacheLifeTime()
+{
+ // see description in
+ // officecfg/registry/schema/org/openoffice/Inet.xcs
+ // for use of these field values.
+ sal_uInt32 nAtime;
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeImplWeb::get( m_xContext );
+ m_nOptsCacheLifeImplWeb = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeDAV::get( m_xContext );
+ m_nOptsCacheLifeDAV = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get( m_xContext );
+ m_nOptsCacheLifeDAVLocked = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 3600 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeNotImpl::get( m_xContext );
+ m_nOptsCacheLifeNotImpl = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 43200 ) ) );
+
+ nAtime = officecfg::Inet::Settings::OptsCacheLifeNotFound::get( m_xContext );
+ m_nOptsCacheLifeNotFound = std::max( sal_uInt32( 0 ),
+ std::min( nAtime, sal_uInt32( 30 ) ) );
+}
+
+
+void Content::getResourceOptions(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ DAVOptions& rDAVOptions,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed )
+{
+ OUString aRedirURL;
+ OUString aTargetURL = rResAccess->getURL();
+ DAVOptions aDAVOptions;
+ // first check if in cache, if not, then send method to server
+ if ( !aStaticDAVOptionsCache.getDAVOptions( aTargetURL, aDAVOptions ) )
+ {
+ try
+ {
+ rResAccess->OPTIONS( aDAVOptions, xEnv );
+ // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
+ sal_uInt32 nLifeTime = ( aDAVOptions.isClass1() ||
+ aDAVOptions.isClass2() ||
+ aDAVOptions.isClass3() ) ?
+ m_nOptsCacheLifeDAV : // a WebDAV site
+ m_nOptsCacheLifeImplWeb; // a site implementing OPTIONS but
+ // it's not DAV
+ // if resource is locked, will use a
+ // different lifetime
+ if( aDAVOptions.isLocked() )
+ nLifeTime = m_nOptsCacheLifeDAVLocked;
+
+ // check if redirected
+ aRedirURL = rResAccess->getURL();
+ if( aRedirURL == aTargetURL)
+ { // no redirection
+ aRedirURL.clear();
+ }
+ // cache this URL's option
+ aDAVOptions.setURL( aTargetURL );
+ aDAVOptions.setRedirectedURL( aRedirURL );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ nLifeTime );
+ }
+ catch ( DAVException const & e )
+ {
+ // first, remove from cache, will be added if needed, depending on the error received
+ aStaticDAVOptionsCache.removeDAVOptions( aTargetURL );
+ rResAccess->resetUri();
+
+ aDAVOptions.setURL( aTargetURL );
+ aDAVOptions.setRedirectedURL( aRedirURL );
+ switch( e.getError() )
+ {
+ case DAVException::DAV_HTTP_TIMEOUT:
+ case DAVException::DAV_HTTP_CONNECT:
+ {
+ // something bad happened to the connection
+ // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
+ // probably a new bit stating 'timed out' should be added to opts var?
+ // in any case abort the command
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // cache the internal unofficial status code
+
+ aDAVOptions.setHttpResponseStatusCode( USC_CONNECTION_TIMED_OUT );
+ // used only internally, so the text doesn't really matter...
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_LOOKUP:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ aDAVOptions.setHttpResponseStatusCode( USC_LOOKUP_FAILED );
+ // used only internally, so the text doesn't really matter...
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_AUTH:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // - the remote site is a WebDAV with special configuration: read/only for read operations
+ // and read/write for write operations, the user is not allowed to lock/write and
+ // she cancelled the credentials request.
+ // this is not actually an error, it means only that for current user this is a standard web,
+ // though possibly DAV enabled
+ aDAVOptions.setHttpResponseStatusCode( USC_AUTH_FAILED );
+ // used only internally, so the text doesn't really matter...
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_AUTHPROXY:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ aDAVOptions.setHttpResponseStatusCode( USC_AUTHPROXY_FAILED );
+ // used only internally, so the text doesn't really matter...
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ break;
+ case DAVException::DAV_HTTP_ERROR:
+ {
+ switch( e.getStatus() )
+ {
+ case SC_FORBIDDEN:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ case SC_BAD_REQUEST:
+ case SC_INTERNAL_SERVER_ERROR:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
+ aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptions.setHttpResponseStatusText( e.getData() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotFound );
+ }
+ break;
+ case SC_NOT_IMPLEMENTED:
+ case SC_METHOD_NOT_ALLOWED:
+ {
+ // OPTIONS method must be implemented in DAV
+ // resource is NON_DAV, or not advertising it
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ case SC_NOT_FOUND:
+ {
+ // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
+ // 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, aDAVOptions ) )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier->getContentIdentifier() << "> resource exists" );
+ nLifeTime = m_nOptsCacheLifeNotImpl;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
+ if ( networkAccessAllowed != nullptr )
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+ }
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ nLifeTime );
+ }
+ break;
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
+ << ", '" << e.getData() << "'" );
+ aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ aDAVOptions.setHttpResponseStatusText( e.getData() );
+ // cache it, so OPTIONS won't be called again, this URL does not support it
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ }
+ }
+ break;
+ // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
+ // number of redirections, consider the resource type as UNKNOWN
+ // possibly a normal web site, not DAV
+ case DAVException::DAV_HTTP_REDIRECT:
+ default:
+ {
+ SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: "
+ << e.getError() << ", HTTP error: "<< e.getStatus() );
+ aStaticDAVOptionsCache.addDAVOptions( aDAVOptions,
+ m_nOptsCacheLifeNotImpl );
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ // check current response status code, perhaps we need to set networkAccessAllowed
+ sal_uInt16 CachedResponseStatusCode = aDAVOptions.getHttpResponseStatusCode();
+ if ( networkAccessAllowed != nullptr &&
+ ( ( CachedResponseStatusCode == SC_NOT_FOUND ) ||
+ ( CachedResponseStatusCode == SC_GONE ) ||
+ ( CachedResponseStatusCode == USC_CONNECTION_TIMED_OUT ) ||
+ ( CachedResponseStatusCode == USC_LOOKUP_FAILED ) ||
+ ( CachedResponseStatusCode == USC_AUTH_FAILED ) ||
+ ( CachedResponseStatusCode == USC_AUTHPROXY_FAILED )
+ )
+ )
+ {
+ *networkAccessAllowed = false;
+ }
+ }
+ rDAVOptions = aDAVOptions;
+}
+
+
+//static
+bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ DAVOptions& rDAVOptions )
+{
+ std::vector< OUString > aHeaderNames;
+ DAVResource aResource;
+
+ try
+ {
+ // 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 );
+ rDAVOptions.setHttpResponseStatusCode( 0 );
+ rDAVOptions.setHttpResponseStatusText( OUString() );
+ return true;
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getError() == DAVException::DAV_HTTP_ERROR )
+ {
+ if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
+ e.getStatus() == SC_METHOD_NOT_ALLOWED ||
+ e.getStatus() == SC_NOT_FOUND )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "HEAD probably 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.emplace_back(
+ OUString( "Range" ),
+ OUString( "bytes=0-0" ));
+
+ rResAccess->GET0( aPartialGet,
+ aHeaderNames,
+ aResource,
+ xEnv );
+ return true;
+ }
+ catch ( DAVException const & ex )
+ {
+ if ( ex.getError() == DAVException::DAV_HTTP_ERROR )
+ {
+ rDAVOptions.setHttpResponseStatusCode( ex.getStatus() );
+ rDAVOptions.setHttpResponseStatusText( ex.getData() );
+ }
+ }
+ }
+ else
+ {
+ rDAVOptions.setHttpResponseStatusCode( e.getStatus() );
+ rDAVOptions.setHttpResponseStatusText( e.getData() );
+ }
+ }
+ return false;
+ }
+ catch ( ... )
+ {
+ }
+ // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
+ rDAVOptions.setHttpResponseStatusCode( SC_NOT_IMPLEMENTED );
+ rDAVOptions.setHttpResponseStatusText( OUString() );
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavcontent.hxx b/ucb/source/ucp/webdav-neon/webdavcontent.hxx
new file mode 100644
index 000000000..4a976a26b
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavcontent.hxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVCONTENT_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVCONTENT_HXX
+
+#include <config_lgpl.h>
+#include <memory>
+#include <list>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include <ucbhelper/contenthelper.hxx>
+#include "DAVResourceAccess.hxx"
+#include "PropertyMap.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+ struct PropertyValue;
+}
+
+namespace com::sun::star::io {
+ class XInputStream;
+}
+
+namespace com::sun::star::sdbc {
+ class XRow;
+}
+
+namespace com::sun::star::ucb {
+ struct OpenCommandArgument3;
+ struct PostCommandArgument2;
+ struct PropertyCommandArgument;
+ struct TransferInfo;
+}
+
+namespace webdav_ucp
+{
+
+
+// UNO service name for the content.
+#define WEBDAV_CONTENT_SERVICE_NAME "com.sun.star.ucb.WebDAVContent"
+
+
+class ContentProvider;
+class ContentProperties;
+class CachableContentProperties;
+
+class Content : public ::ucbhelper::ContentImplHelper,
+ public css::ucb::XContentCreator
+{
+ enum ResourceType
+ {
+ 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;
+ std::unique_ptr< CachableContentProperties >
+ m_xCachedProps; // locally cached props
+ OUString m_aEscapedTitle;
+ // resource type for general DAV methods
+ ResourceType m_eResourceType;
+ // resource type for general LOCK method only
+ ResourceType m_eResourceTypeForLocks;
+ ContentProvider* m_pProvider; // No need for a ref, base class holds object
+ bool m_bTransient;
+ bool m_bCollection;
+ bool m_bDidGetOrHead;
+ std::vector< OUString > m_aFailedPropNames;
+ // Options Cache lifetime
+ // for web site implementing OPTIONS, but not dav
+ sal_uInt32 m_nOptsCacheLifeImplWeb;
+ // for WebDAV site where OPTIONS is mandatory
+ sal_uInt32 m_nOptsCacheLifeDAV;
+ // same as above, but when the resource is locked by us
+ sal_uInt32 m_nOptsCacheLifeDAVLocked;
+// For web site not implementing OPTIONS
+ // during this time we assume the site doesn't turn to WebDAV
+ // but remains a simple Web
+ sal_uInt32 m_nOptsCacheLifeNotImpl;
+ // When resource is not found
+ // may be the resource is unavailable only briefly?
+ // so better have this small
+ sal_uInt32 m_nOptsCacheLifeNotFound;
+
+ void initOptsCacheLifeTime();
+
+private:
+ virtual css::uno::Sequence< css::beans::Property >
+ getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual css::uno::Sequence< css::ucb::CommandInfo >
+ getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual OUString getParentURL() override;
+
+ /// @throws css::uno::Exception
+ bool isFolder( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ css::uno::Sequence< css::uno::Any >
+ setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ typedef rtl::Reference< Content > ContentRef;
+ typedef std::vector< ContentRef > ContentRefList;
+ void queryChildren( ContentRefList& rChildren);
+
+ bool
+ exchangeIdentity( const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+
+ OUString
+ getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess );
+
+ /// @throws css::uno::Exception
+ ResourceType
+ getResourceType( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ ResourceType
+ getResourceType( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed = nullptr);
+
+ // Command "open"
+ /// @throws css::uno::Exception
+ css::uno::Any open(
+ const css::ucb::OpenCommandArgument3 & rArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // Command "post"
+ /// @throws css::uno::Exception
+ void post( const css::ucb::PostCommandArgument2 & rArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // Command "insert"
+ /// @throws css::uno::Exception
+ void insert( const css::uno::Reference< css::io::XInputStream > & xInputStream,
+ bool bReplaceExisting,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "transfer"
+ /// @throws css::uno::Exception
+ void transfer( const css::ucb::TransferInfo & rArgs,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "delete"
+ /// @throws css::uno::Exception
+ void destroy( bool bDeletePhysical );
+
+ // Command "lock"
+ /// @throws css::uno::Exception
+ void lock( const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "unlock"
+ /// @throws css::uno::Exception
+ void unlock( const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ css::uno::Any MapDAVException( const DAVException & e,
+ bool bWrite );
+ /// @throws css::uno::Exception
+ void cancelCommandExecution(
+ const DAVException & e,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv,
+ bool bWrite = false );
+
+ static bool shouldAccessNetworkAfterException( const DAVException & e );
+
+ ResourceType resourceTypeForLocks(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnvironment,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess );
+
+ ResourceType resourceTypeForLocks(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& rEnvironment );
+
+ /// @throws css::beans::PropertyExistException
+ /// @throws css::beans::IllegalTypeException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ /// @throws css::beans::UnknownPropertyException
+ /// @throws css::beans::NotRemoveableException
+ /// @throws css::uno::RuntimeException
+ void removeProperty( const OUString& Name,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+public:
+ /// @throws css::ucb::ContentCreationException
+ /// @throws css::uno::RuntimeException
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory );
+ /// @throws css::ucb::ContentCreationException
+ /// @throws css::uno::RuntimeException
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ bool isCollection );
+ virtual ~Content() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ throw() override;
+ virtual void SAL_CALL release()
+ throw() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XContent
+ virtual OUString SAL_CALL
+ getContentType() override;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL
+ execute( const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override;
+ virtual void SAL_CALL
+ abort( sal_Int32 CommandId ) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL
+ addProperty( const OUString& Name,
+ sal_Int16 Attributes,
+ const css::uno::Any& DefaultValue ) override;
+
+ virtual void SAL_CALL
+ removeProperty( const OUString& Name ) override;
+
+
+ // Additional interfaces
+
+
+ // XContentCreator
+ virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL
+ queryCreatableContentsInfo() override;
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createNewContent( const css::ucb::ContentInfo& Info ) override;
+
+
+ // Non-interface methods.
+
+
+ DAVResourceAccess & getResourceAccess() { return *m_xResAccess; }
+
+ // Called from resultset data supplier.
+ static css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const ContentProperties& rData,
+ const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider,
+ const OUString& rContentId );
+
+ // Use OPTIONS method to retrieve the type of the Web resource
+ /// @throws css::uno::Exception
+ void getResourceOptions( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ DAVOptions& rDAVOptions,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed = nullptr);
+
+ static bool isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ DAVOptions& rDAVOptions );
+
+ static void removeCachedPropertyNames( const OUString & rURL );
+
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx b/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx
new file mode 100644
index 000000000..d8ff4ff6f
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavcontentcaps.cxx
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <memory>
+#include <set>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/ucb/Link.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/ucb/LockEntry.hpp>
+#include "webdavcontent.hxx"
+#include "webdavprovider.hxx"
+#include "ContentProperties.hxx"
+#include "PropfindCache.hxx"
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+
+// ContentProvider implementation.
+
+
+void ContentProvider::getProperty(
+ const OUString & rPropName, beans::Property & rProp )
+{
+ if ( !m_pProps )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_pProps )
+ {
+ m_pProps.reset( new PropertyMap );
+
+
+ // Fill map of known properties...
+
+
+ // Mandatory UCB properties.
+ m_pProps->insert(
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+
+ // Optional UCB properties.
+
+ m_pProps->insert(
+ beans::Property(
+ "DateCreated",
+ -1,
+ cppu::UnoType<util::DateTime>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "DateModified",
+ -1,
+ cppu::UnoType<util::DateTime>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "Size",
+ -1,
+ cppu::UnoType<sal_Int64>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "BaseURI",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<
+ uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ // Standard DAV properties.
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::CREATIONDATE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::DISPLAYNAME,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTLANGUAGE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTLENGTH,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTTYPE ,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETETAG,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETLASTMODIFIED,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::LOCKDISCOVERY,
+ -1,
+ cppu::UnoType<
+ uno::Sequence< ucb::Lock >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::RESOURCETYPE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::SOURCE,
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::Link >>::get(),
+ beans::PropertyAttribute::BOUND ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::SUPPORTEDLOCK,
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::LockEntry >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::EXECUTABLE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+ }
+ }
+
+
+ // Lookup property.
+
+
+ beans::Property aProp;
+ aProp.Name = rPropName;
+ const PropertyMap::const_iterator it = m_pProps->find( aProp );
+ if ( it != m_pProps->end() )
+ {
+ rProp = *it;
+ }
+ else
+ {
+ // All unknown props are treated as:
+ rProp = beans::Property(
+ rPropName,
+ - 1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+ }
+}
+
+
+static PropertyNamesCache aStaticPropertyNamesCache;
+
+// static
+void Content::removeCachedPropertyNames( const OUString & rURL )
+{
+ aStaticPropertyNamesCache.removeCachedPropertyNames( rURL );
+}
+
+// Content implementation.
+
+
+// virtual
+uno::Sequence< beans::Property > Content::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ bool bTransient;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ std::unique_ptr< ContentProperties > xCachedProps;
+ rtl::Reference< ContentProvider > xProvider;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ bTransient = m_bTransient;
+ xResAccess.reset(new DAVResourceAccess(*m_xResAccess));
+ if (m_xCachedProps)
+ xCachedProps.reset(new ContentProperties(*m_xCachedProps));
+ xProvider.set( m_pProvider );
+ }
+
+ std::set< OUString > aPropSet;
+
+ // No server access for just created (not yet committed) objects.
+ // Only a minimal set of properties supported at this stage.
+ if ( !bTransient )
+ {
+ // Obtain all properties supported for this resource from server.
+ DAVOptions aDAVOptions;
+ getResourceOptions( xEnv, aDAVOptions, xResAccess );
+ // only Class 1 is needed for PROPFIND
+ if ( aDAVOptions.isClass1() )
+ {
+ try
+ {
+ std::vector< DAVResourceInfo > props;
+ OUString aTheURL( xResAccess->getURL() );
+ PropertyNames aPropsNames( aTheURL );
+
+ if( !aStaticPropertyNamesCache.getCachedPropertyNames( aTheURL, aPropsNames ) )
+ {
+
+ xResAccess->PROPFIND( DAVZERO, props, xEnv );
+ aPropsNames.setPropertiesNames( props );
+
+ aStaticPropertyNamesCache.addCachePropertyNames( aPropsNames );
+ }
+ else
+ {
+ props = aPropsNames.getPropertiesNames();
+ }
+
+ // Note: vector should contain exactly one resource info, because
+ // we used a depth of DAVZERO for PROPFIND.
+ if (props.size() == 1)
+ {
+ aPropSet.insert( (*props.begin()).properties.begin(),
+ (*props.begin()).properties.end() );
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ }
+ }
+
+ // Add DAV properties, map DAV properties to UCB properties.
+ bool bHasCreationDate = false; // creationdate <-> DateCreated
+ bool bHasGetLastModified = false; // getlastmodified <-> DateModified
+ bool bHasGetContentType = false; // getcontenttype <-> MediaType
+ bool bHasGetContentLength = false; // getcontentlength <-> Size
+
+ bool bHasContentType = false;
+ bool bHasIsDocument = false;
+ bool bHasIsFolder = false;
+ bool bHasTitle = false;
+ bool bHasBaseURI = false;
+ bool bHasDateCreated = false;
+ bool bHasDateModified = false;
+ bool bHasMediaType = false;
+ bool bHasSize = false;
+ bool bHasCreatableInfos = false;
+
+ {
+ for ( const auto& rProp : aPropSet )
+ {
+ if ( !bHasCreationDate &&
+ ( rProp == DAVProperties::CREATIONDATE ) )
+ {
+ bHasCreationDate = true;
+ }
+ else if ( !bHasGetLastModified &&
+ ( rProp == DAVProperties::GETLASTMODIFIED ) )
+ {
+ bHasGetLastModified = true;
+ }
+ else if ( !bHasGetContentType &&
+ ( rProp == DAVProperties::GETCONTENTTYPE ) )
+ {
+ bHasGetContentType = true;
+ }
+ else if ( !bHasGetContentLength &&
+ ( rProp == DAVProperties::GETCONTENTLENGTH ) )
+ {
+ bHasGetContentLength = true;
+ }
+ else if ( !bHasContentType && rProp == "ContentType" )
+ {
+ bHasContentType = true;
+ }
+ else if ( !bHasIsDocument && rProp == "IsDocument" )
+ {
+ bHasIsDocument = true;
+ }
+ else if ( !bHasIsFolder && rProp == "IsFolder" )
+ {
+ bHasIsFolder = true;
+ }
+ else if ( !bHasTitle && rProp == "Title" )
+ {
+ bHasTitle = true;
+ }
+ else if ( !bHasBaseURI && rProp == "BaseURI" )
+ {
+ bHasBaseURI = true;
+ }
+ else if ( !bHasDateCreated && rProp == "DateCreated" )
+ {
+ bHasDateCreated = true;
+ }
+ else if ( !bHasDateModified && rProp == "DateModified" )
+ {
+ bHasDateModified = true;
+ }
+ else if ( !bHasMediaType && rProp == "MediaType" )
+ {
+ bHasMediaType = true;
+ }
+ else if ( !bHasSize && rProp == "Size" )
+ {
+ bHasSize = true;
+ }
+ else if ( !bHasCreatableInfos && rProp == "CreatableContentsInfo" )
+ {
+ bHasCreatableInfos = true;
+ }
+ }
+ }
+
+ // Add mandatory properties.
+ if ( !bHasContentType )
+ aPropSet.insert(
+ OUString( "ContentType" ) );
+
+ if ( !bHasIsDocument )
+ aPropSet.insert(
+ OUString( "IsDocument" ) );
+
+ if ( !bHasIsFolder )
+ aPropSet.insert(
+ OUString( "IsFolder" ) );
+
+ if ( !bHasTitle )
+ {
+ // Always present since it can be calculated from content's URI.
+ aPropSet.insert(
+ OUString( "Title" ) );
+ }
+
+ // Add optional properties.
+
+ if ( !bHasBaseURI )
+ {
+ // Always present since it can be calculated from content's URI.
+ aPropSet.insert(
+ OUString( "BaseURI" ) );
+ }
+
+ if ( !bHasDateCreated && bHasCreationDate )
+ aPropSet.insert(
+ OUString( "DateCreated" ) );
+
+ if ( !bHasDateModified && bHasGetLastModified )
+ aPropSet.insert(
+ OUString( "DateModified" ) );
+
+ if ( !bHasMediaType && bHasGetContentType )
+ aPropSet.insert(
+ OUString( "MediaType" ) );
+
+ if ( !bHasSize && bHasGetContentLength )
+ aPropSet.insert(
+ OUString( "Size" ) );
+
+ if ( !bHasCreatableInfos )
+ aPropSet.insert(
+ OUString(
+ "CreatableContentsInfo" ) );
+
+ // Add cached properties, if present and still missing.
+ if (xCachedProps)
+ {
+ const std::unique_ptr< PropertyValueMap > & xProps
+ = xCachedProps->getProperties();
+
+ for ( const auto& rEntry : *xProps )
+ aPropSet.insert( rEntry.first );
+ }
+
+ // std::set -> uno::Sequence
+ sal_Int32 nCount = aPropSet.size();
+ uno::Sequence< beans::Property > aProperties( nCount );
+
+ sal_Int32 n = 0;
+ beans::Property aProp;
+
+ for ( const auto& rProp : aPropSet )
+ {
+ xProvider->getProperty( rProp, aProp );
+ aProperties[ n++ ] = aProp;
+ }
+
+ return aProperties;
+}
+
+
+// virtual
+uno::Sequence< ucb::CommandInfo > Content::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< ucb::CommandInfo > aCmdInfo( 10 );
+
+
+ // Mandatory commands
+
+
+ aCmdInfo[ 0 ] =
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get() );
+ aCmdInfo[ 1 ] =
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get() );
+ aCmdInfo[ 2 ] =
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get() );
+ aCmdInfo[ 3 ] =
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() );
+
+
+ // Optional standard commands
+
+
+ aCmdInfo[ 4 ] =
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get() );
+ aCmdInfo[ 5 ] =
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<ucb::InsertCommandArgument>::get() );
+ aCmdInfo[ 6 ] =
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get() );
+
+
+ // New commands
+
+
+ aCmdInfo[ 7 ] =
+ ucb::CommandInfo(
+ "post",
+ -1,
+ cppu::UnoType<ucb::PostCommandArgument2>::get() );
+ aCmdInfo[ 8 ] =
+ ucb::CommandInfo(
+ "addProperty",
+ -1,
+ cppu::UnoType<ucb::PropertyCommandArgument>::get() );
+ aCmdInfo[ 9 ] =
+ ucb::CommandInfo(
+ "removeProperty",
+ -1,
+ cppu::UnoType<OUString>::get() );
+
+ bool bFolder = false;
+
+ try
+ {
+ bFolder = isFolder( xEnv );
+ }
+ catch ( uno::Exception const & )
+ {
+ return aCmdInfo;
+ }
+
+ 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 );
+ if ( nMoreCmds )
+ aCmdInfo.realloc( nPos + nMoreCmds );
+ else
+ return aCmdInfo;
+
+ if ( bFolder )
+ {
+
+ // Optional standard commands
+
+
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get() );
+ nPos++;
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get() );
+ nPos++;
+ }
+ else
+ {
+ // no document-only commands at the moment.
+ }
+
+ if ( bSupportsLocking )
+ {
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "lock",
+ -1,
+ cppu::UnoType<void>::get() );
+ nPos++;
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "unlock",
+ -1,
+ cppu::UnoType<void>::get() );
+ nPos++;
+ }
+ return aCmdInfo;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavdatasupplier.cxx b/ucb/source/ucp/webdav-neon/webdavdatasupplier.cxx
new file mode 100644
index 000000000..7675f6c5d
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavdatasupplier.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <sal/log.hxx>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/ResultSetException.hpp>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include <memory>
+#include <vector>
+#include "webdavdatasupplier.hxx"
+#include "webdavcontent.hxx"
+#include "ContentProperties.hxx"
+#include "NeonUri.hxx"
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+namespace webdav_ucp
+{
+
+
+// struct ResultListEntry.
+
+namespace {
+
+struct ResultListEntry
+{
+ OUString aId;
+ uno::Reference< ucb::XContentIdentifier > xId;
+ uno::Reference< ucb::XContent > xContent;
+ uno::Reference< sdbc::XRow > xRow;
+ std::shared_ptr<ContentProperties> const pData;
+
+ explicit ResultListEntry(std::shared_ptr<ContentProperties> const& pEntry)
+ : pData(pEntry)
+ {}
+};
+
+}
+
+// ResultList.
+
+
+typedef std::vector<std::unique_ptr<ResultListEntry>> ResultList;
+
+
+// struct DataSupplier_Impl.
+
+
+struct DataSupplier_Impl
+{
+ osl::Mutex m_aMutex;
+ ResultList m_Results;
+ rtl::Reference< Content > m_xContent;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ sal_Int32 m_nOpenMode;
+ bool m_bCountFinal;
+ bool m_bThrowException;
+
+ DataSupplier_Impl(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode )
+ : m_xContent( rContent ), m_xContext( rxContext ), m_nOpenMode( nOpenMode ),
+ m_bCountFinal( false ), m_bThrowException( false ) {}
+};
+
+}
+
+
+// DataSupplier Implementation.
+
+
+DataSupplier::DataSupplier(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode )
+: m_pImpl( new DataSupplier_Impl( rxContext, rContent, nOpenMode ) )
+{
+}
+
+
+// virtual
+DataSupplier::~DataSupplier()
+{
+}
+
+
+// virtual
+OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ OUString aId = m_pImpl->m_Results[ nIndex ]->aId;
+ if ( !aId.isEmpty() )
+ {
+ // Already cached.
+ return aId;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ OUString aId = m_pImpl->m_xContent->getResourceAccess().getURL();
+
+ const ContentProperties& props(*(m_pImpl->m_Results[ nIndex ]->pData));
+
+ if ( ( aId.lastIndexOf( '/' ) + 1 ) != aId.getLength() )
+ aId += "/";
+
+ aId += props.getEscapedTitle();
+
+ if ( props.isTrailingSlash() )
+ aId += "/";
+
+ m_pImpl->m_Results[ nIndex ]->aId = aId;
+ return aId;
+ }
+ return OUString();
+}
+
+
+// virtual
+uno::Reference< ucb::XContentIdentifier >
+DataSupplier::queryContentIdentifier( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = m_pImpl->m_Results[ nIndex ]->xId;
+ if ( xId.is() )
+ {
+ // Already cached.
+ return xId;
+ }
+ }
+
+ OUString aId = queryContentIdentifierString( nIndex );
+ if ( !aId.isEmpty() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aId );
+ m_pImpl->m_Results[ nIndex ]->xId = xId;
+ return xId;
+ }
+ return uno::Reference< ucb::XContentIdentifier >();
+}
+
+
+// virtual
+uno::Reference< ucb::XContent >
+DataSupplier::queryContent( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_pImpl->m_Results[ nIndex ]->xContent;
+ if ( xContent.is() )
+ {
+ // Already cached.
+ return xContent;
+ }
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = queryContentIdentifier( nIndex );
+ if ( xId.is() )
+ {
+ try
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_pImpl->m_xContent->getProvider()->queryContent( xId );
+ m_pImpl->m_Results[ nIndex ]->xContent = xContent;
+ return xContent;
+
+ }
+ catch ( ucb::IllegalIdentifierException& )
+ {
+ }
+ }
+ return uno::Reference< ucb::XContent >();
+}
+
+
+// virtual
+bool DataSupplier::getResult( sal_uInt32 nIndex )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ // Result already present.
+ return true;
+ }
+
+ // Obtain values...
+ if ( getData() )
+ {
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ // Result already present.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// virtual
+sal_uInt32 DataSupplier::totalCount()
+{
+ // Obtain values...
+ getData();
+
+ return m_pImpl->m_Results.size();
+}
+
+
+// virtual
+sal_uInt32 DataSupplier::currentCount()
+{
+ return m_pImpl->m_Results.size();
+}
+
+
+// virtual
+bool DataSupplier::isCountFinal()
+{
+ return m_pImpl->m_bCountFinal;
+}
+
+
+// virtual
+uno::Reference< sdbc::XRow > DataSupplier::queryPropertyValues(
+ sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ {
+ uno::Reference< sdbc::XRow > xRow = m_pImpl->m_Results[ nIndex ]->xRow;
+ if ( xRow.is() )
+ {
+ // Already cached.
+ return xRow;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ uno::Reference< sdbc::XRow > xRow
+ = Content::getPropertyValues(
+ m_pImpl->m_xContext,
+ getResultSet()->getProperties(),
+ *(m_pImpl->m_Results[ nIndex ]->pData),
+ rtl::Reference< ::ucbhelper::ContentProviderImplHelper >(
+ m_pImpl->m_xContent->getProvider().get() ),
+ queryContentIdentifierString( nIndex ) );
+ m_pImpl->m_Results[ nIndex ]->xRow = xRow;
+ return xRow;
+ }
+
+ return uno::Reference< sdbc::XRow >();
+}
+
+
+// virtual
+void DataSupplier::releasePropertyValues( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if (nIndex < m_pImpl->m_Results.size())
+ m_pImpl->m_Results[ nIndex ]->xRow.clear();
+}
+
+
+// virtual
+void DataSupplier::close()
+{
+}
+
+
+// virtual
+void DataSupplier::validate()
+{
+ if ( m_pImpl->m_bThrowException )
+ throw ucb::ResultSetException();
+}
+
+
+bool DataSupplier::getData()
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( !m_pImpl->m_bCountFinal )
+ {
+ std::vector< OUString > propertyNames;
+ ContentProperties::UCBNamesToDAVNames(
+ getResultSet()->getProperties(), propertyNames );
+
+ // Append "resourcetype", if not already present. It's value is
+ // needed to get a valid ContentProperties::pIsFolder value, which
+ // is needed for OpenMode handling.
+
+ bool isNoResourceType = std::none_of(propertyNames.begin(), propertyNames.end(),
+ [](const OUString& rName) { return rName == DAVProperties::RESOURCETYPE; });
+
+ if ( isNoResourceType )
+ propertyNames.push_back( DAVProperties::RESOURCETYPE );
+
+ std::vector< DAVResource > resources;
+ try
+ {
+ // propfind depth 1, get property values for parent AND for each
+ // child
+ m_pImpl->m_xContent->getResourceAccess()
+ .PROPFIND( DAVONE,
+ propertyNames,
+ resources,
+ getResultSet()->getEnvironment() );
+#if defined SAL_LOG_INFO
+ {
+ //print the resource for every URI returned
+ for ( const auto& rResource : resources )
+ {
+ NeonUri aCurrURI( rResource.uri );
+ OUString aCurrPath = aCurrURI.GetPath();
+ aCurrPath = NeonUri::unescape( aCurrPath );
+ SAL_INFO( "ucb.ucp.webdav", "getData() - resource URL: <" << rResource.uri << ">, unescaped to: <" << aCurrPath << "> )" );
+ for ( const auto& rProp : rResource.properties )
+ {
+ SAL_INFO( "ucb.ucp.webdav", "PROPFIND - property name: " << rProp.Name );
+ }
+ }
+ }
+#endif
+ }
+ catch ( DAVException & )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Running PROPFIND: DAVException" );
+ m_pImpl->m_bThrowException = true;
+ }
+
+ if ( !m_pImpl->m_bThrowException )
+ {
+ try
+ {
+ NeonUri aURI(
+ m_pImpl->m_xContent->getResourceAccess().getURL() );
+ OUString aPath = aURI.GetPath();
+
+ if ( aPath.endsWith("/") )
+ aPath = aPath.copy( 0, aPath.getLength() - 1 );
+
+ aPath = NeonUri::unescape( aPath );
+ bool bFoundParent = false;
+
+ for (DAVResource & rRes : resources)
+ {
+ // Filter parent, which is contained somewhere(!) in
+ // the vector.
+ if ( !bFoundParent )
+ {
+ try
+ {
+ NeonUri aCurrURI( rRes.uri );
+ OUString aCurrPath = aCurrURI.GetPath();
+ if ( aCurrPath.endsWith("/") )
+ aCurrPath
+ = aCurrPath.copy(
+ 0,
+ aCurrPath.getLength() - 1 );
+
+ aCurrPath = NeonUri::unescape( aCurrPath );
+ if ( aPath == aCurrPath )
+ {
+ bFoundParent = true;
+ continue;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // do nothing, ignore error. continue.
+ }
+ }
+
+ std::shared_ptr<ContentProperties> const
+ pContentProperties = std::make_shared<ContentProperties>(rRes);
+
+ // Check resource against open mode.
+ switch ( m_pImpl->m_nOpenMode )
+ {
+ case ucb::OpenMode::FOLDERS:
+ {
+ bool bFolder = false;
+
+ const uno::Any & rValue
+ = pContentProperties->getValue(
+ "IsFolder" );
+ rValue >>= bFolder;
+
+ if ( !bFolder )
+ continue;
+
+ break;
+ }
+
+ case ucb::OpenMode::DOCUMENTS:
+ {
+ bool bDocument = false;
+
+ const uno::Any & rValue
+ = pContentProperties->getValue(
+ "IsDocument" );
+ rValue >>= bDocument;
+
+ if ( !bDocument )
+ continue;
+
+ break;
+ }
+
+ case ucb::OpenMode::ALL:
+ default:
+ break;
+ }
+
+ m_pImpl->m_Results.push_back(
+ std::make_unique<ResultListEntry>(pContentProperties));
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ }
+
+ m_pImpl->m_bCountFinal = true;
+
+ // Callback possible, because listeners may be informed!
+ aGuard.clear();
+ getResultSet()->rowCountFinal();
+ }
+ return !m_pImpl->m_bThrowException;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavdatasupplier.hxx b/ucb/source/ucp/webdav-neon/webdavdatasupplier.hxx
new file mode 100644
index 000000000..bd5b73340
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavdatasupplier.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVDATASUPPLIER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVDATASUPPLIER_HXX
+
+#include <config_lgpl.h>
+#include <memory>
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+
+namespace webdav_ucp {
+
+struct DataSupplier_Impl;
+class Content;
+
+class DataSupplier : public ucbhelper::ResultSetDataSupplier
+{
+ std::unique_ptr<DataSupplier_Impl> m_pImpl;
+
+private:
+ bool getData();
+
+public:
+ DataSupplier( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode);
+
+ virtual ~DataSupplier() override;
+
+ virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier >
+ queryContentIdentifier( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContent >
+ queryContent( sal_uInt32 nIndex ) override;
+
+ virtual bool getResult( sal_uInt32 nIndex ) override;
+
+ virtual sal_uInt32 totalCount() override;
+ virtual sal_uInt32 currentCount() override;
+ virtual bool isCountFinal() override;
+
+ virtual css::uno::Reference< css::sdbc::XRow >
+ queryPropertyValues( sal_uInt32 nIndex ) override;
+ virtual void releasePropertyValues( sal_uInt32 nIndex ) override;
+
+ virtual void close() override;
+
+ virtual void validate() override;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavprovider.cxx b/ucb/source/ucp/webdav-neon/webdavprovider.cxx
new file mode 100644
index 000000000..2b0bd5bfe
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavprovider.cxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <sal/config.h>
+
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/getcomponentcontext.hxx>
+#include <ucbhelper/macros.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include "webdavprovider.hxx"
+#include "webdavcontent.hxx"
+
+#include <osl/mutex.hxx>
+#include <tools/urlobj.hxx>
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+
+// ContentProvider Implementation.
+
+
+ContentProvider::ContentProvider(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+: ::ucbhelper::ContentProviderImplHelper( rxContext ),
+ m_xDAVSessionFactory( new DAVSessionFactory )
+{
+}
+
+
+// virtual
+ContentProvider::~ContentProvider()
+{
+}
+
+
+// XInterface methods.
+
+void SAL_CALL ContentProvider::acquire()
+ throw()
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ContentProvider::release()
+ throw()
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XTypeProvider* >(this),
+ static_cast< lang::XServiceInfo* >(this),
+ static_cast< ucb::XContentProvider* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_IMPL_3( ContentProvider,
+ lang::XTypeProvider,
+ lang::XServiceInfo,
+ ucb::XContentProvider );
+
+
+// XServiceInfo methods.
+
+XSERVICEINFO_COMMOM_IMPL( ContentProvider,
+ "com.sun.star.comp.WebDAVContentProvider" )
+/// @throws css::uno::Exception
+static css::uno::Reference< css::uno::XInterface >
+ContentProvider_CreateInstance( const css::uno::Reference< css::lang::XMultiServiceFactory> & rSMgr )
+{
+ css::lang::XServiceInfo* pX = new ContentProvider( ucbhelper::getComponentContext(rSMgr) );
+ return css::uno::Reference< css::uno::XInterface >::query( pX );
+}
+
+css::uno::Sequence< OUString >
+ContentProvider::getSupportedServiceNames_Static()
+{
+ css::uno::Sequence< OUString > aSNS { WEBDAV_CONTENT_PROVIDER_SERVICE_NAME };
+ return aSNS;
+}
+
+// Service factory implementation.
+
+
+ONE_INSTANCE_SERVICE_FACTORY_IMPL( ContentProvider );
+
+
+// XContentProvider methods.
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+ContentProvider::queryContent(
+ const uno::Reference<
+ ucb::XContentIdentifier >& Identifier )
+{
+ // Check URL scheme...
+ INetURLObject aURL( Identifier->getContentIdentifier() );
+
+ if ( aURL.isSchemeEqualTo( INetProtocol::NotValid ) )
+ throw ucb::IllegalIdentifierException();
+
+ if ( !aURL.isAnyKnownWebDAVScheme() )
+ throw ucb::IllegalIdentifierException();
+
+ uno::Reference< ucb::XContentIdentifier > xCanonicId;
+
+ if (aURL.isSchemeEqualTo( INetProtocol::VndSunStarWebdav ) ||
+ aURL.isSchemeEqualTo(DAV_URL_SCHEME) ||
+ aURL.isSchemeEqualTo( WEBDAV_URL_SCHEME ) )
+ {
+ aURL.changeScheme( INetProtocol::Http );
+ xCanonicId = new ::ucbhelper::ContentIdentifier( aURL.getExternalURL() );
+ }
+ else if ( aURL.isSchemeEqualTo( VNDSUNSTARWEBDAVS_URL_SCHEME ) ||
+ aURL.isSchemeEqualTo( DAVS_URL_SCHEME ) ||
+ aURL.isSchemeEqualTo( WEBDAVS_URL_SCHEME ))
+ {
+ aURL.changeScheme( INetProtocol::Https );
+ xCanonicId = new ::ucbhelper::ContentIdentifier( aURL.getExternalURL() );
+ }
+ else
+ {
+ xCanonicId = Identifier;
+ }
+
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xCanonicId ).get();
+ if ( xContent.is() )
+ return xContent;
+
+ // Create a new content.
+
+ try
+ {
+ xContent = new ::webdav_ucp::Content(
+ m_xContext, this, xCanonicId, m_xDAVSessionFactory );
+ registerNewContent( xContent );
+ }
+ catch ( ucb::ContentCreationException const & )
+ {
+ throw ucb::IllegalIdentifierException();
+ }
+
+ if ( !xContent->getIdentifier().is() )
+ throw ucb::IllegalIdentifierException();
+
+ return xContent;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavprovider.hxx b/ucb/source/ucp/webdav-neon/webdavprovider.hxx
new file mode 100644
index 000000000..80556ab23
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavprovider.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVPROVIDER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVPROVIDER_HXX
+
+#include <memory>
+#include <config_lgpl.h>
+#include <rtl/ref.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include "DAVSessionFactory.hxx"
+#include <ucbhelper/providerhelper.hxx>
+#include "PropertyMap.hxx"
+
+namespace webdav_ucp {
+
+
+// UNO service name for the provider. This name will be used by the UCB to
+// create instances of the provider.
+#define WEBDAV_CONTENT_PROVIDER_SERVICE_NAME "com.sun.star.ucb.WebDAVContentProvider"
+
+// URL scheme. This is the scheme the provider will be able to create
+// contents for. The UCB will select the provider ( i.e. in order to create
+// contents ) according to this scheme.
+#define VNDSUNSTARWEBDAV_URL_SCHEME "vnd.sun.star.webdav"
+#define VNDSUNSTARWEBDAVS_URL_SCHEME u"vnd.sun.star.webdavs"
+#define HTTP_URL_SCHEME "http"
+#define HTTPS_URL_SCHEME "https"
+#define DAV_URL_SCHEME u"dav"
+#define DAVS_URL_SCHEME u"davs"
+#define WEBDAV_URL_SCHEME u"webdav"
+#define WEBDAVS_URL_SCHEME u"webdavs"
+
+#define FTP_URL_SCHEME "ftp"
+
+#define HTTP_CONTENT_TYPE "application/" HTTP_URL_SCHEME "-content"
+
+#define WEBDAV_CONTENT_TYPE HTTP_CONTENT_TYPE
+#define WEBDAV_COLLECTION_TYPE "application/" VNDSUNSTARWEBDAV_URL_SCHEME "-collection"
+
+
+class ContentProvider : public ::ucbhelper::ContentProviderImplHelper
+{
+ rtl::Reference< DAVSessionFactory > m_xDAVSessionFactory;
+ std::unique_ptr<PropertyMap> m_pProps;
+
+public:
+ explicit ContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ContentProvider() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ throw() override;
+ virtual void SAL_CALL release()
+ throw() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ static OUString getImplementationName_Static();
+ static css::uno::Sequence< OUString > getSupportedServiceNames_Static();
+
+ static css::uno::Reference< css::lang::XSingleServiceFactory >
+ createServiceFactory( const css::uno::Reference<
+ css::lang::XMultiServiceFactory >& rxServiceMgr );
+
+ // XContentProvider
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override;
+
+
+ // Non-interface methods.
+
+ void getProperty( const OUString & rPropName,
+ css::beans::Property & rProp );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavresultset.cxx b/ucb/source/ucp/webdav-neon/webdavresultset.cxx
new file mode 100644
index 000000000..be4fadc9b
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavresultset.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - This implementation is not a dynamic result set!!! It only implements
+ the necessary interfaces, but never recognizes/notifies changes!!!
+
+ *************************************************************************/
+#include "webdavresultset.hxx"
+
+using namespace com::sun::star;
+using namespace webdav_ucp;
+
+
+// DynamicResultSet Implementation.
+
+
+DynamicResultSet::DynamicResultSet(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const ucb::OpenCommandArgument2& rCommand,
+ const uno::Reference< ucb::XCommandEnvironment >& rxEnv )
+: ResultSetImplHelper( rxContext, rCommand ),
+ m_xContent( rxContent ),
+ m_xEnv( rxEnv )
+{
+}
+
+
+// Non-interface methods.
+
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet( m_xContext,
+ m_aCommand.Properties,
+ new DataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ),
+ m_xEnv );
+}
+
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet( m_xContext,
+ m_aCommand.Properties,
+ new DataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ),
+ m_xEnv );
+ m_xResultSet2 = m_xResultSet1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavresultset.hxx b/ucb/source/ucp/webdav-neon/webdavresultset.hxx
new file mode 100644
index 000000000..470b16f25
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavresultset.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVRESULTSET_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_NEON_WEBDAVRESULTSET_HXX
+
+#include <config_lgpl.h>
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#include "webdavcontent.hxx"
+#include "webdavdatasupplier.hxx"
+
+namespace webdav_ucp {
+
+class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+{
+ rtl::Reference< Content > m_xContent;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv;
+
+private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+public:
+ DynamicResultSet( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const css::ucb::OpenCommandArgument2& rCommand,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& rxEnv );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav-neon/webdavservices.cxx b/ucb/source/ucp/webdav-neon/webdavservices.cxx
new file mode 100644
index 000000000..b29955535
--- /dev/null
+++ b/ucb/source/ucp/webdav-neon/webdavservices.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include "webdavprovider.hxx"
+
+using namespace com::sun::star;
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * ucpdav1_component_getFactory(
+ const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
+{
+ void * pRet = nullptr;
+
+ uno::Reference< lang::XMultiServiceFactory > xSMgr(
+ static_cast< lang::XMultiServiceFactory * >(
+ pServiceManager ) );
+ uno::Reference< lang::XSingleServiceFactory > xFactory;
+
+
+ // WebDAV Content Provider.
+
+
+ if ( ::webdav_ucp::ContentProvider::getImplementationName_Static().
+ equalsAscii( pImplName ) )
+ {
+ xFactory = ::webdav_ucp::ContentProvider::createServiceFactory( xSMgr );
+ }
+
+
+ if ( xFactory.is() )
+ {
+ xFactory->acquire();
+ pRet = xFactory.get();
+ }
+
+ return pRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/AprEnv.cxx b/ucb/source/ucp/webdav/AprEnv.cxx
new file mode 100644
index 000000000..a1e61e5f9
--- /dev/null
+++ b/ucb/source/ucp/webdav/AprEnv.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "AprEnv.hxx"
+
+namespace apr_environment
+{
+
+AprEnv::AprEnv()
+ : mpAprPool( nullptr )
+{
+ apr_initialize();
+
+ apr_pool_create(&mpAprPool, nullptr);
+
+ mpSerfLockStore = new http_dav_ucp::SerfLockStore();
+}
+
+AprEnv::~AprEnv()
+{
+ delete mpSerfLockStore;
+
+ apr_pool_destroy(mpAprPool);
+
+ apr_terminate();
+}
+
+/* static */
+AprEnv* AprEnv::getAprEnv()
+{
+ static AprEnv rAprEnv;
+
+ return &rAprEnv;
+}
+
+apr_pool_t* AprEnv::getAprPool()
+{
+ return mpAprPool;
+}
+
+http_dav_ucp::SerfLockStore* AprEnv::getSerfLockStore()
+{
+ return mpSerfLockStore;
+}
+
+} // namespace apr_environment
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/AprEnv.hxx b/ucb/source/ucp/webdav/AprEnv.hxx
new file mode 100644
index 000000000..4590c64aa
--- /dev/null
+++ b/ucb/source/ucp/webdav/AprEnv.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_APRENV_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_APRENV_HXX
+
+#include <apr_pools.h>
+#include "SerfLockStore.hxx"
+
+namespace apr_environment
+{
+
+// singleton class providing environment for APR libraries
+class AprEnv
+{
+ public:
+ ~AprEnv();
+
+ static AprEnv* getAprEnv();
+
+ apr_pool_t* getAprPool();
+
+ http_dav_ucp::SerfLockStore* getSerfLockStore();
+
+ private:
+ apr_pool_t* mpAprPool;
+ // SerfLockStore is a static object and has to be destroyed
+ // before AprEnv, so store it here.
+ http_dav_ucp::SerfLockStore* mpSerfLockStore;
+
+ AprEnv();
+
+ AprEnv(const AprEnv&) = delete;
+ AprEnv& operator=(const AprEnv&) = delete;
+};
+
+} // namespace apr_environment
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_APRENV_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/ContentProperties.cxx b/ucb/source/ucp/webdav/ContentProperties.cxx
new file mode 100644
index 000000000..85406e680
--- /dev/null
+++ b/ucb/source/ucp/webdav/ContentProperties.cxx
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/util/DateTime.hpp>
+#include "SerfUri.hxx"
+#include "DAVResource.hxx"
+#include "DAVProperties.hxx"
+#include "DateTimeHelper.hxx"
+#include "webdavprovider.hxx"
+#include "ContentProperties.hxx"
+
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+/*
+=============================================================================
+
+ Property Mapping
+
+=============================================================================
+HTTP (entity header) WebDAV (property) UCB (property)
+=============================================================================
+
+Allow
+Content-Encoding
+Content-Language getcontentlanguage
+Content-Length getcontentlength Size
+Content-Location
+Content-MD5
+Content-Range
+Content-Type getcontenttype MediaType
+Expires
+Last-Modified getlastmodified DateModified
+ creationdate DateCreated
+ resourcetype IsFolder,IsDocument,ContentType
+ displayname
+ETag (actually getetag
+a response header )
+ lockdiscovery
+ supportedlock
+ source
+ Title (always taken from URI)
+
+=============================================================================
+
+Important: HTTP headers will not be mapped to DAV properties; only to UCB
+ properties. (Content-Length,Content-Type,Last-Modified)
+*/
+
+
+// ContentProperties Implementation.
+
+
+// static member!
+uno::Any ContentProperties::m_aEmptyAny;
+
+ContentProperties::ContentProperties( const DAVResource& rResource )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ SAL_WARN_IF( !rResource.uri.getLength(), "ucb.ucp.webdav",
+ "ContentProperties ctor - Empty resource URI!" );
+
+ // Title
+ try
+ {
+ SerfUri aURI( rResource.uri );
+ m_aEscapedTitle = aURI.GetPathBaseName();
+
+ (*m_xProps)[ OUString( "Title" ) ]
+ = PropertyValue(
+ uno::makeAny( aURI.GetPathBaseNameUnescaped() ), true );
+ }
+ catch ( DAVException const & )
+ {
+ (*m_xProps)[ OUString( "Title" ) ]
+ = PropertyValue(
+ uno::makeAny(
+ OUString( "*** unknown ***" ) ),
+ true );
+ }
+
+ for ( const auto& rProp : rResource.properties )
+ {
+ addProperty( rProp );
+ }
+
+ if ( rResource.uri.endsWith("/") )
+ m_bTrailingSlash = true;
+}
+
+
+ContentProperties::ContentProperties(
+ const OUString & rTitle, bool bFolder )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ (*m_xProps)[ OUString( "Title" ) ]
+ = PropertyValue( uno::makeAny( rTitle ), true );
+ (*m_xProps)[ OUString( "IsFolder" ) ]
+ = PropertyValue( uno::makeAny( bFolder ), true );
+ (*m_xProps)[ OUString( "IsDocument" ) ]
+ = PropertyValue( uno::makeAny( bool( !bFolder ) ), true );
+}
+
+
+ContentProperties::ContentProperties( const OUString & rTitle )
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+ (*m_xProps)[ OUString( "Title" ) ]
+ = PropertyValue( uno::makeAny( rTitle ), true );
+}
+
+
+ContentProperties::ContentProperties()
+: m_xProps( new PropertyValueMap ),
+ m_bTrailingSlash( false )
+{
+}
+
+
+ContentProperties::ContentProperties( const ContentProperties & rOther )
+: m_aEscapedTitle( rOther.m_aEscapedTitle ),
+ m_xProps( rOther.m_xProps.get()
+ ? new PropertyValueMap( *rOther.m_xProps )
+ : new PropertyValueMap ),
+ m_bTrailingSlash( rOther.m_bTrailingSlash )
+{
+}
+
+
+bool ContentProperties::contains( const OUString & rName ) const
+{
+ if ( get( rName ) )
+ return true;
+ else
+ return false;
+}
+
+
+const uno::Any & ContentProperties::getValue(
+ const OUString & rName ) const
+{
+ const PropertyValue * pProp = get( rName );
+ if ( pProp )
+ return pProp->value();
+ else
+ return m_aEmptyAny;
+}
+
+
+const PropertyValue * ContentProperties::get(
+ const OUString & rName ) const
+{
+ PropertyValueMap::const_iterator it = m_xProps->find( rName );
+ const PropertyValueMap::const_iterator end = m_xProps->end();
+
+ if ( it == end )
+ {
+ it = std::find_if(m_xProps->cbegin(), end,
+ [&rName](const PropertyValueMap::value_type& rEntry) {
+ return rEntry.first.equalsIgnoreAsciiCase( rName );
+ });
+ if ( it != end )
+ return &(*it).second;
+
+ return nullptr;
+ }
+ else
+ return &(*it).second;
+}
+
+
+// static
+void ContentProperties::UCBNamesToDAVNames(
+ const uno::Sequence< beans::Property > & rProps,
+ std::vector< OUString > & propertyNames,
+ bool bIncludeUnmatched /* = true */ )
+{
+
+ // Assemble list of DAV properties to obtain from server.
+ // Append DAV properties needed to obtain requested UCB props.
+
+
+ // DAV UCB
+ // creationdate <- DateCreated
+ // getlastmodified <- DateModified
+ // getcontenttype <- MediaType
+ // getcontentlength <- Size
+ // resourcetype <- IsFolder, IsDocument, ContentType
+ // (taken from URI) <- Title
+
+ bool bCreationDate = false;
+ bool bLastModified = false;
+ bool bContentType = false;
+ bool bContentLength = false;
+ bool bResourceType = false;
+
+ sal_Int32 nCount = rProps.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::Property & rProp = rProps[ n ];
+
+ if ( rProp.Name == "Title" )
+ {
+ // Title is always obtained from resource's URI.
+ continue;
+ }
+ else if ( rProp.Name == "DateCreated" ||
+ ( rProp.Name == DAVProperties::CREATIONDATE ) )
+ {
+ if ( !bCreationDate )
+ {
+ propertyNames.push_back( DAVProperties::CREATIONDATE );
+ bCreationDate = true;
+ }
+ }
+ else if ( rProp.Name == "DateModified" ||
+ ( rProp.Name == DAVProperties::GETLASTMODIFIED ) )
+ {
+ if ( !bLastModified )
+ {
+ propertyNames.push_back(
+ DAVProperties::GETLASTMODIFIED );
+ bLastModified = true;
+ }
+ }
+ else if ( rProp.Name == "MediaType" ||
+ ( rProp.Name == DAVProperties::GETCONTENTTYPE ) )
+ {
+ if ( !bContentType )
+ {
+ propertyNames.push_back(
+ DAVProperties::GETCONTENTTYPE );
+ bContentType = true;
+ }
+ }
+ else if ( rProp.Name == "Size" ||
+ ( rProp.Name == DAVProperties::GETCONTENTLENGTH ) )
+ {
+ if ( !bContentLength )
+ {
+ propertyNames.push_back(
+ DAVProperties::GETCONTENTLENGTH );
+ bContentLength = true;
+ }
+ }
+ else if ( rProp.Name == "ContentType" ||
+ rProp.Name == "IsDocument" ||
+ rProp.Name == "IsFolder" ||
+ ( rProp.Name == DAVProperties::RESOURCETYPE ) )
+ {
+ if ( !bResourceType )
+ {
+ propertyNames.push_back( DAVProperties::RESOURCETYPE );
+ bResourceType = true;
+ }
+ }
+ else
+ {
+ if ( bIncludeUnmatched )
+ propertyNames.push_back( rProp.Name );
+ }
+ }
+}
+
+
+// static
+void ContentProperties::UCBNamesToHTTPNames(
+ const uno::Sequence< beans::Property > & rProps,
+ std::vector< OUString > & propertyNames,
+ bool bIncludeUnmatched /* = true */ )
+{
+
+ // Assemble list of HTTP header names to obtain from server.
+ // Append HTTP headers needed to obtain requested UCB props.
+
+
+ // HTTP UCB
+ // Last-Modified <- DateModified
+ // Content-Type <- MediaType
+ // Content-Length <- Size
+
+ sal_Int32 nCount = rProps.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::Property & rProp = rProps[ n ];
+
+ if ( rProp.Name == "DateModified" )
+ {
+ propertyNames.push_back( OUString( "Last-Modified" ) );
+ }
+ else if ( rProp.Name == "MediaType" )
+ {
+ propertyNames.push_back( OUString( "Content-Type" ) );
+ }
+ else if ( rProp.Name == "Size" )
+ {
+ propertyNames.push_back( OUString( "Content-Length" ) );
+ }
+ else
+ {
+ if ( bIncludeUnmatched )
+ propertyNames.push_back( rProp.Name );
+ }
+ }
+}
+
+
+bool ContentProperties::containsAllNames(
+ const uno::Sequence< beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const
+{
+ rNamesNotContained.clear();
+
+ sal_Int32 nCount = rProps.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const OUString & rName = rProps[ n ].Name;
+ if ( !contains( rName ) )
+ {
+ // Not found.
+ rNamesNotContained.push_back( rName );
+ }
+ }
+
+ return ( rNamesNotContained.size() == 0 );
+}
+
+
+void ContentProperties::addProperties(
+ const std::vector< OUString > & rProps,
+ const ContentProperties & rContentProps )
+{
+ for ( const OUString & rName : rProps )
+ {
+ if ( !contains( rName ) ) // ignore duplicates
+ {
+ const PropertyValue * pProp = rContentProps.get( rName );
+ if ( pProp )
+ {
+ // Add it.
+ addProperty( rName, pProp->value(), pProp->isCaseSensitive() );
+ }
+ else
+ {
+ addProperty( rName, uno::Any(), false );
+ }
+ }
+ }
+}
+
+
+void ContentProperties::addProperties( const ContentProperties & rProps )
+{
+ for ( const auto& rProp : *rProps.m_xProps )
+ {
+ addProperty(
+ rProp.first, rProp.second.value(), rProp.second.isCaseSensitive() );
+ }
+}
+
+
+void ContentProperties::addProperties(
+ const std::vector< DAVPropertyValue > & rProps )
+{
+ for ( const auto& rProp : rProps )
+ {
+ addProperty( rProp );
+ }
+}
+
+
+void ContentProperties::addProperty( const DAVPropertyValue & rProp )
+{
+ addProperty( rProp.Name, rProp.Value, rProp.IsCaseSensitive );
+}
+
+
+void ContentProperties::addProperty( const OUString & rName,
+ const css::uno::Any & rValue,
+ bool bIsCaseSensitive )
+{
+ if ( rName == DAVProperties::CREATIONDATE )
+ {
+ // Map DAV:creationdate to UCP:DateCreated
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString( "DateCreated" ) ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ // else if ( rName.equals( DAVProperties::DISPLAYNAME ) )
+ // {
+ // }
+ // else if ( rName.equals( DAVProperties::GETCONTENTLANGUAGE ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::GETCONTENTLENGTH )
+ {
+ // Map DAV:getcontentlength to UCP:Size
+ OUString aValue;
+ rValue >>= aValue;
+
+ (*m_xProps)[ OUString( "Size" ) ]
+ = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
+ }
+ else if ( rName == "Content-Length" )
+ {
+ // Do NOT map Content-Length entity header to DAV:getcontentlength!
+ // Only DAV resources have this property.
+
+ // Map Content-Length entity header to UCP:Size
+ OUString aValue;
+ rValue >>= aValue;
+
+ (*m_xProps)[ OUString( "Size" ) ]
+ = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
+ }
+ else if ( rName == DAVProperties::GETCONTENTTYPE )
+ {
+ // Map DAV:getcontenttype to UCP:MediaType (1:1)
+ (*m_xProps)[ OUString( "MediaType" ) ]
+ = PropertyValue( rValue, true );
+ }
+ else if ( rName == "Content-Type" )
+ {
+ // Do NOT map Content-Type entity header to DAV:getcontenttype!
+ // Only DAV resources have this property.
+
+ // Map DAV:getcontenttype to UCP:MediaType (1:1)
+ (*m_xProps)[ OUString( "MediaType" ) ]
+ = PropertyValue( rValue, true );
+ }
+ // else if ( rName.equals( DAVProperties::GETETAG ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::GETLASTMODIFIED )
+ {
+ // Map the DAV:getlastmodified entity header to UCP:DateModified
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString( "DateModified" ) ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ else if ( rName == "Last-Modified" )
+ {
+ // Do not map Last-Modified entity header to DAV:getlastmodified!
+ // Only DAV resources have this property.
+
+ // Map the Last-Modified entity header to UCP:DateModified
+ OUString aValue;
+ rValue >>= aValue;
+ util::DateTime aDate;
+ DateTimeHelper::convert( aValue, aDate );
+
+ (*m_xProps)[ OUString( "DateModified" ) ]
+ = PropertyValue( uno::makeAny( aDate ), true );
+ }
+ // else if ( rName.equals( DAVProperties::LOCKDISCOVERY ) )
+ // {
+ // }
+ else if ( rName == DAVProperties::RESOURCETYPE )
+ {
+ OUString aValue;
+ rValue >>= aValue;
+
+ // Map DAV:resourcetype to UCP:IsFolder, UCP:IsDocument, UCP:ContentType
+ bool bFolder =
+ aValue.equalsIgnoreAsciiCase( "collection" );
+
+ (*m_xProps)[ OUString( "IsFolder" ) ]
+ = PropertyValue( uno::makeAny( bFolder ), true );
+ (*m_xProps)[ OUString( "IsDocument" ) ]
+ = PropertyValue( uno::makeAny( bool( !bFolder ) ), true );
+ (*m_xProps)[ OUString( "ContentType" ) ]
+ = PropertyValue( uno::makeAny( bFolder
+ ? OUString( WEBDAV_COLLECTION_TYPE )
+ : OUString( WEBDAV_CONTENT_TYPE ) ), true );
+ }
+ // else if ( rName.equals( DAVProperties::SUPPORTEDLOCK ) )
+ // {
+ // }
+
+ // Save property.
+ (*m_xProps)[ rName ] = PropertyValue( rValue, bIsCaseSensitive );
+}
+
+
+// CachableContentProperties Implementation.
+
+
+namespace
+{
+ bool isCachable( OUString const & rName,
+ bool isCaseSensitive )
+ {
+ const OUString aNonCachableProps [] =
+ {
+ DAVProperties::LOCKDISCOVERY,
+
+ DAVProperties::GETETAG,
+ OUString( "ETag" ),
+
+ OUString( "DateModified" ),
+ OUString( "Last-Modified" ),
+ DAVProperties::GETLASTMODIFIED,
+
+ OUString( "Size" ),
+ OUString( "Content-Length" ),
+ DAVProperties::GETCONTENTLENGTH,
+
+ OUString( "Date" )
+ };
+
+ for ( sal_uInt32 n = 0;
+ n < ( sizeof( aNonCachableProps )
+ / sizeof( aNonCachableProps[ 0 ] ) );
+ ++n )
+ {
+ if ( isCaseSensitive )
+ {
+ if ( rName.equals( aNonCachableProps[ n ] ) )
+ return false;
+ }
+ else
+ if ( rName.equalsIgnoreAsciiCase( aNonCachableProps[ n ] ) )
+ return false;
+ }
+ return true;
+ }
+
+} // namespace
+
+
+CachableContentProperties::CachableContentProperties(
+ const ContentProperties & rProps )
+{
+ addProperties( rProps );
+}
+
+
+void CachableContentProperties::addProperties(
+ const ContentProperties & rProps )
+{
+ const std::unique_ptr< PropertyValueMap > & props = rProps.getProperties();
+
+ for ( const auto& rProp : *props )
+ {
+ if ( isCachable( rProp.first, rProp.second.isCaseSensitive() ) )
+ m_aProps.addProperty( rProp.first,
+ rProp.second.value(),
+ rProp.second.isCaseSensitive() );
+ }
+}
+
+
+void CachableContentProperties::addProperties(
+ const std::vector< DAVPropertyValue > & rProps )
+{
+ for ( const auto& rProp : rProps )
+ {
+ if ( isCachable( rProp.Name, rProp.IsCaseSensitive ) )
+ m_aProps.addProperty( rProp );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/ContentProperties.hxx b/ucb/source/ucp/webdav/ContentProperties.hxx
new file mode 100644
index 000000000..a48383f8e
--- /dev/null
+++ b/ucb/source/ucp/webdav/ContentProperties.hxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_CONTENTPROPERTIES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_CONTENTPROPERTIES_HXX
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "DAVResource.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+}
+
+namespace http_dav_ucp
+{
+
+struct DAVResource;
+
+// PropertyValueMap.
+class PropertyValue
+{
+private:
+ css::uno::Any m_aValue;
+ bool m_bIsCaseSensitive;
+
+public:
+ PropertyValue()
+ : m_bIsCaseSensitive( true ) {}
+
+ explicit PropertyValue( const css::uno::Any & rValue,
+ bool bIsCaseSensitive )
+ : m_aValue( rValue),
+ m_bIsCaseSensitive( bIsCaseSensitive ) {}
+
+ bool isCaseSensitive() const { return m_bIsCaseSensitive; }
+ const css::uno::Any & value() const { return m_aValue; }
+
+};
+
+typedef std::unordered_map< OUString, PropertyValue > PropertyValueMap;
+
+class ContentProperties
+{
+public:
+ ContentProperties();
+
+ explicit ContentProperties( const DAVResource& rResource );
+
+ // Mini props for transient contents.
+ ContentProperties( const OUString & rTitle, bool bFolder );
+
+ // Micro props for non-existing contents.
+ explicit ContentProperties( const OUString & rTitle );
+
+ ContentProperties( const ContentProperties & rOther );
+
+ bool contains( const OUString & rName ) const;
+
+ const css::uno::Any& getValue( const OUString & rName ) const;
+
+ // Maps the UCB property names contained in rProps with their DAV property
+ // counterparts, if possible. All unmappable properties will be included
+ // unchanged in resulting vector unless bIncludeUnmatched is set to false.
+ // The vector filled by this method can directly be handed over to
+ // DAVResourceAccess::PROPFIND. The result from PROPFIND
+ // (vector< DAVResource >) can be used to create a ContentProperties
+ // instance which can map DAV properties back to UCB properties.
+ static void UCBNamesToDAVNames( const css::uno::Sequence< css::beans::Property > & rProps,
+ std::vector< OUString > & resources,
+ bool bIncludeUnmatched = true );
+
+ // Maps the UCB property names contained in rProps with their HTTP header
+ // counterparts, if possible. All unmappable properties will be included
+ // unchanged in resulting vector unless bIncludeUnmatched is set to false.
+ // The vector filled by this method can directly be handed over to
+ // DAVResourceAccess::HEAD. The result from HEAD (vector< DAVResource >)
+ // can be used to create a ContentProperties instance which can map header
+ // names back to UCB properties.
+ static void UCBNamesToHTTPNames( const css::uno::Sequence< css::beans::Property > & rProps,
+ std::vector< OUString > & resources,
+ bool bIncludeUnmatched = true );
+
+ // return true, if all properties contained in rProps are contained in
+ // this ContentProperties instance. Otherwise, false will be returned.
+ // rNamesNotContained contain the missing names.
+ bool containsAllNames(
+ const css::uno::Sequence< css::beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const;
+
+ // adds all properties described by rProps that are actually contained in
+ // rContentProps to this instance. In case of duplicates the value
+ // already contained in this will left unchanged.
+ void addProperties( const std::vector< OUString > & rProps,
+ const ContentProperties & rContentProps );
+
+ // overwrites probably existing entries.
+ void addProperties( const ContentProperties & rProps );
+
+ // overwrites probably existing entries.
+ void addProperties( const std::vector< DAVPropertyValue > & rProps );
+
+ // overwrites probably existing entry.
+ void addProperty( const OUString & rName,
+ const css::uno::Any & rValue,
+ bool bIsCaseSensitive );
+
+ // overwrites probably existing entry.
+ void addProperty( const DAVPropertyValue & rProp );
+
+ bool isTrailingSlash() const { return m_bTrailingSlash; }
+
+ const OUString & getEscapedTitle() const { return m_aEscapedTitle; }
+
+ // Not good to expose implementation details, but this is actually an
+ // internal class.
+ const std::unique_ptr< PropertyValueMap > & getProperties() const
+ { return m_xProps; }
+
+private:
+ OUString m_aEscapedTitle;
+ std::unique_ptr< PropertyValueMap > m_xProps;
+ bool m_bTrailingSlash;
+
+ static css::uno::Any m_aEmptyAny;
+
+ ContentProperties & operator=( const ContentProperties & ); // n.i.
+
+ const PropertyValue * get( const OUString & rName ) const;
+};
+
+class CachableContentProperties
+{
+private:
+ ContentProperties m_aProps;
+
+ CachableContentProperties & operator=( const CachableContentProperties & ); // n.i.
+ CachableContentProperties( const CachableContentProperties & ); // n.i.
+
+public:
+ explicit CachableContentProperties( const ContentProperties & rProps );
+
+ void addProperties( const ContentProperties & rProps );
+
+ void addProperties( const std::vector< DAVPropertyValue > & rProps );
+
+ bool containsAllNames(
+ const css::uno::Sequence< css::beans::Property >& rProps,
+ std::vector< OUString > & rNamesNotContained ) const
+ { return m_aProps.containsAllNames( rProps, rNamesNotContained ); }
+
+ const css::uno::Any &
+ getValue( const OUString & rName ) const
+ { return m_aProps.getValue( rName ); }
+
+ operator const ContentProperties & () const { return m_aProps; }
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_CONTENTPROPERTIES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVAuthListener.hxx b/ucb/source/ucp/webdav/DAVAuthListener.hxx
new file mode 100644
index 000000000..95f61c0a0
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVAuthListener.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVAUTHLISTENER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVAUTHLISTENER_HXX
+
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ustring.hxx>
+
+namespace http_dav_ucp
+{
+
+class DAVAuthListener : public salhelper::SimpleReferenceObject
+{
+ public:
+ virtual int authenticate(
+ const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials,
+ bool bUsePreviousCredentials = true ) = 0;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVAUTHLISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVAuthListenerImpl.hxx b/ucb/source/ucp/webdav/DAVAuthListenerImpl.hxx
new file mode 100644
index 000000000..fc3f9a845
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVAuthListenerImpl.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVAUTHLISTENERIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVAUTHLISTENERIMPL_HXX
+
+#include "DAVAuthListener.hxx"
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+
+namespace http_dav_ucp
+{
+
+
+
+
+ class DAVAuthListener_Impl : public DAVAuthListener
+ {
+ public:
+
+ DAVAuthListener_Impl(
+ const css::uno::Reference<css::ucb::XCommandEnvironment>& xEnv,
+ const OUString & inURL )
+ : m_xEnv( xEnv ), m_aURL( inURL )
+ {
+ }
+
+ virtual int authenticate( const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials,
+ bool bUsePreviousCredentials = true ) override;
+ private:
+
+ const css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv;
+ const OUString m_aURL;
+
+ OUString m_aPrevPassword;
+ OUString m_aPrevUsername;
+ };
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVException.hxx b/ucb/source/ucp/webdav/DAVException.hxx
new file mode 100644
index 000000000..3b21067e5
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVException.hxx
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVEXCEPTION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVEXCEPTION_HXX
+
+#include <rtl/ustring.hxx>
+
+namespace http_dav_ucp
+{
+
+
+// HTTP/WebDAV status codes
+
+
+const sal_uInt16 SC_NONE = 0;
+
+// 1xx (Informational - no errors)
+const sal_uInt16 SC_CONTINUE = 100;
+const sal_uInt16 SC_SWITCHING_PROTOCOLS = 101;
+// DAV extensions
+const sal_uInt16 SC_PROCESSING = 102;
+
+//2xx (Successful - no errors)
+const sal_uInt16 SC_OK = 200;
+const sal_uInt16 SC_CREATED = 201;
+const sal_uInt16 SC_ACCEPTED = 202;
+const sal_uInt16 SC_NON_AUTHORITATIVE_INFORMATION = 203;
+const sal_uInt16 SC_NO_CONTENT = 204;
+const sal_uInt16 SC_RESET_CONTENT = 205;
+const sal_uInt16 SC_PARTIAL_CONTENT = 206;
+// DAV extensions
+const sal_uInt16 SC_MULTISTATUS = 207;
+
+//3xx (Redirection)
+const sal_uInt16 SC_MULTIPLE_CHOICES = 300;
+const sal_uInt16 SC_MOVED_PERMANENTLY = 301;
+const sal_uInt16 SC_MOVED_TEMPORARILY = 302;
+const sal_uInt16 SC_SEE_OTHER = 303;
+const sal_uInt16 SC_NOT_MODIFIED = 304;
+const sal_uInt16 SC_USE_PROXY = 305;
+const sal_uInt16 SC_TEMPORARY_REDIRECT = 307;
+
+//4xx (Client error)
+const sal_uInt16 SC_BAD_REQUEST = 400;
+const sal_uInt16 SC_UNAUTHORIZED = 401;
+const sal_uInt16 SC_PAYMENT_REQUIRED = 402;
+const sal_uInt16 SC_FORBIDDEN = 403;
+const sal_uInt16 SC_NOT_FOUND = 404;
+const sal_uInt16 SC_METHOD_NOT_ALLOWED = 405;
+const sal_uInt16 SC_NOT_ACCEPTABLE = 406;
+const sal_uInt16 SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+const sal_uInt16 SC_REQUEST_TIMEOUT = 408;
+const sal_uInt16 SC_CONFLICT = 409;
+const sal_uInt16 SC_GONE = 410;
+const sal_uInt16 SC_LENGTH_REQUIRED = 411;
+const sal_uInt16 SC_PRECONDITION_FAILED = 412;
+const sal_uInt16 SC_REQUEST_ENTITY_TOO_LARGE = 413;
+const sal_uInt16 SC_REQUEST_URI_TOO_LONG = 414;
+const sal_uInt16 SC_UNSUPPORTED_MEDIA_TYPE = 415;
+const sal_uInt16 SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+const sal_uInt16 SC_EXPECTATION_FAILED = 417;
+// DAV extensions
+const sal_uInt16 SC_UNPROCESSABLE_ENTITY = 422;
+const sal_uInt16 SC_LOCKED = 423;
+const sal_uInt16 SC_FAILED_DEPENDENCY = 424;
+
+//5xx (Server error)
+const sal_uInt16 SC_INTERNAL_SERVER_ERROR = 500;
+const sal_uInt16 SC_NOT_IMPLEMENTED = 501;
+const sal_uInt16 SC_BAD_GATEWAY = 502;
+const sal_uInt16 SC_SERVICE_UNAVAILABLE = 503;
+const sal_uInt16 SC_GATEWAY_TIMEOUT = 504;
+const sal_uInt16 SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+// DAV extensions
+const sal_uInt16 SC_INSUFFICIENT_STORAGE = 507;
+
+
+class DAVException : public std::exception
+{
+ public:
+ enum ExceptionCode {
+ DAV_HTTP_ERROR = 0, // Generic error,
+ // mData = server error message,
+ // mStatusCode = HTTP status code
+ DAV_HTTP_LOOKUP, // Name lookup failed,
+ // mData = server[:port]
+ DAV_HTTP_NOAUTH, // No User authentication data provided - e.g., user aborts corresponding dialog
+ // mData = server[:port]
+ DAV_HTTP_AUTH, // User authentication failed on server,
+ // mData = server[:port]
+ DAV_HTTP_AUTHPROXY, // User authentication failed on proxy,
+ // mData = proxy server[:port]
+ DAV_HTTP_CONNECT, // Could not connect to server,
+ // mData = server[:port]
+ DAV_HTTP_TIMEOUT, // Connection timed out
+ // mData = server[:port]
+ DAV_HTTP_FAILED, // The precondition failed
+ // mData = server[:port]
+ DAV_HTTP_RETRY, // Retry request
+ // mData = server[:port]
+ DAV_HTTP_REDIRECT, // Request was redirected,
+ // mData = new URL
+ DAV_SESSION_CREATE, // session creation error,
+ // mData = server[:port]
+ DAV_INVALID_ARG, // invalid argument
+
+ DAV_LOCK_EXPIRED, // DAV lock expired
+
+ DAV_NOT_LOCKED, // not locked
+
+ DAV_LOCKED_SELF, // locked by this OOo session
+
+ DAV_LOCKED // locked by third party
+ };
+
+ private:
+ ExceptionCode mExceptionCode;
+ OUString mData;
+ sal_uInt16 mStatusCode;
+
+ public:
+ explicit DAVException( ExceptionCode inExceptionCode )
+ : mExceptionCode( inExceptionCode )
+ , mData()
+ , mStatusCode( SC_NONE )
+ {};
+ DAVException( ExceptionCode inExceptionCode,
+ const OUString & rData )
+ : mExceptionCode( inExceptionCode )
+ , mData( rData )
+ , mStatusCode( SC_NONE )
+ {};
+ DAVException( ExceptionCode inExceptionCode,
+ const OUString & rData,
+ sal_uInt16 nStatusCode )
+ : mExceptionCode( inExceptionCode )
+ , mData( rData )
+ , mStatusCode( nStatusCode )
+ {};
+
+ const ExceptionCode & getError() const { return mExceptionCode; }
+ const OUString & getData() const { return mData; }
+ sal_uInt16 getStatus() const { return mStatusCode; }
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVEXCEPTION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVProperties.cxx b/ucb/source/ucp/webdav/DAVProperties.cxx
new file mode 100644
index 000000000..a08a8488d
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVProperties.cxx
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include "DAVProperties.hxx"
+#include <rtl/ustrbuf.hxx>
+
+using namespace http_dav_ucp;
+
+const OUString DAVProperties::CREATIONDATE =
+ OUString( "DAV:creationdate" );
+const OUString DAVProperties::DISPLAYNAME =
+ OUString( "DAV:displayname" );
+const OUString DAVProperties::GETCONTENTLANGUAGE =
+ OUString( "DAV:getcontentlanguage" );
+const OUString DAVProperties::GETCONTENTLENGTH =
+ OUString( "DAV:getcontentlength" );
+const OUString DAVProperties::GETCONTENTTYPE =
+ OUString( "DAV:getcontenttype" );
+const OUString DAVProperties::GETETAG =
+ OUString( "DAV:getetag" );
+const OUString DAVProperties::GETLASTMODIFIED =
+ OUString( "DAV:getlastmodified" );
+const OUString DAVProperties::LOCKDISCOVERY =
+ OUString( "DAV:lockdiscovery" );
+const OUString DAVProperties::RESOURCETYPE =
+ OUString( "DAV:resourcetype" );
+const OUString DAVProperties::SUPPORTEDLOCK =
+ OUString( "DAV:supportedlock" );
+
+const OUString DAVProperties::EXECUTABLE =
+ OUString( "http://apache.org/dav/props/executable" );
+
+
+// static
+void DAVProperties::createSerfPropName( const OUString & rFullName,
+ SerfPropName & rName )
+{
+ if ( rFullName.startsWith( "DAV:" ) )
+ {
+ rName.nspace = "DAV:";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy( RTL_CONSTASCII_LENGTH( "DAV:" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "http://apache.org/dav/props/" ) )
+ {
+ rName.nspace = "http://apache.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://apache.org/dav/props/" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "http://ucb.openoffice.org/dav/props/" ) )
+ {
+ rName.nspace = "http://ucb.openoffice.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString(
+ rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://ucb.openoffice.org/dav/props/" ) ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ else if ( rFullName.startsWith( "<prop:" ) )
+ {
+ // Support for 3rd party namespaces/props
+
+ OString aFullName
+ = OUStringToOString( rFullName, RTL_TEXTENCODING_UTF8 );
+
+ // Format: <prop:the_propname xmlns:prop="the_namespace">
+
+ sal_Int32 nStart = RTL_CONSTASCII_LENGTH( "<prop:" );
+ sal_Int32 nLen = aFullName.indexOf( ' ' ) - nStart;
+ rName.name = strdup( aFullName.copy( nStart, nLen ).getStr() );
+
+ nStart = aFullName.indexOf( '=', nStart + nLen ) + 2; // after ="
+ nLen = aFullName.getLength() - RTL_CONSTASCII_LENGTH( "\">" ) - nStart;
+ rName.nspace = strdup( aFullName.copy( nStart, nLen ).getStr() );
+ }
+ else
+ {
+ // Add our namespace to our own properties.
+ rName.nspace = "http://ucb.openoffice.org/dav/props/";
+ rName.name
+ = strdup( OUStringToOString( rFullName,
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+}
+
+
+// static
+void DAVProperties::createUCBPropName( const char * nspace,
+ const char * name,
+ OUString & rFullName )
+{
+ OUString aNameSpace
+ = OStringToOUString( nspace, RTL_TEXTENCODING_UTF8 );
+ OUString aName
+ = OStringToOUString( name, RTL_TEXTENCODING_UTF8 );
+
+ if ( !aNameSpace.getLength() )
+ {
+ // Some servers send XML without proper namespaces. Assume "DAV:"
+ // in this case, if name is a well-known dav property name.
+ // Although this is not 100% correct, it solves many problems.
+
+ if ( DAVProperties::RESOURCETYPE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::SUPPORTEDLOCK.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::LOCKDISCOVERY.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::CREATIONDATE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::DISPLAYNAME.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTLANGUAGE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTLENGTH.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETCONTENTTYPE.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETETAG.matchIgnoreAsciiCase( aName, 4 ) ||
+ DAVProperties::GETLASTMODIFIED.matchIgnoreAsciiCase( aName, 4 ) )
+ {
+ aNameSpace = "DAV:";
+ }
+ }
+
+ // Note: Concatenating strings BEFORE comparing against known namespaces
+ // is important. See RFC 2815 ( 23.4.2 Meaning of Qualified Names ).
+ rFullName = aNameSpace;
+ rFullName += aName;
+
+ if ( rFullName.startsWith( "DAV:" ) )
+ {
+ // Okay, Just concat strings.
+ }
+ else if ( rFullName.startsWith( "http://apache.org/dav/props/" ) )
+ {
+ // Okay, Just concat strings.
+ }
+ else if ( rFullName.startsWith( "http://ucb.openoffice.org/dav/props/" ) )
+ {
+ // Remove namespace from our own properties.
+ rFullName = rFullName.copy(
+ RTL_CONSTASCII_LENGTH(
+ "http://ucb.openoffice.org/dav/props/" ) );
+ }
+ else
+ {
+ // Create property name that encodes, namespace and name ( XML ).
+ rFullName = "<prop:";
+ rFullName += aName;
+ rFullName += " xmlns:prop=\"";
+ rFullName += aNameSpace;
+ rFullName += "\">";
+ }
+}
+
+
+// static
+bool DAVProperties::isUCBDeadProperty( const SerfPropName & rName )
+{
+ return ( rName.nspace &&
+ ( rtl_str_compareIgnoreAsciiCase(
+ rName.nspace, "http://ucb.openoffice.org/dav/props/" )
+ == 0 ) );
+}
+
+bool DAVProperties::isUCBSpecialProperty(const OUString& rFullName, OUString& rParsedName)
+{
+ sal_Int32 nLen = rFullName.getLength();
+ if ( nLen <= 0 ||
+ !rFullName.startsWith( "<prop:" ) ||
+ !rFullName.endsWith( "\">" ) )
+ return false;
+
+ sal_Int32 nStart = RTL_CONSTASCII_LENGTH( "<prop:" );
+ sal_Int32 nEnd = rFullName.indexOf( ' ', nStart );
+ if ( nEnd == -1 )
+ return false;
+
+ OUString sPropName = rFullName.copy( nStart, nEnd - nStart );
+ if ( !sPropName.getLength() )
+ return false;
+
+ // TODO skip whitespaces?
+ if ( !rFullName.match( "xmlns:prop=\"", ++nEnd ) )
+ return false;
+
+ nStart = nEnd + RTL_CONSTASCII_LENGTH( "xmlns:prop=\"" );
+ nEnd = rFullName.indexOf( '"', nStart );
+ if ( nEnd != nLen - RTL_CONSTASCII_LENGTH( "\">" ) )
+ return false;
+
+ OUString sNamesp = rFullName.copy( nStart, nEnd - nStart );
+ if ( !( nLen = sNamesp.getLength() ) )
+ return false;
+
+ OUStringBuffer aBuff( sNamesp );
+ if ( sNamesp[nLen - 1] != '/' )
+ aBuff.append( '/' );
+ aBuff.append( sPropName );
+ rParsedName = aBuff.makeStringAndClear();
+
+ return rParsedName.getLength();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVProperties.hxx b/ucb/source/ucp/webdav/DAVProperties.hxx
new file mode 100644
index 000000000..926afd6a7
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVProperties.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVPROPERTIES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVPROPERTIES_HXX
+
+#include <rtl/ustring.hxx>
+
+namespace http_dav_ucp
+{
+
+typedef struct { const char *nspace, *name; } SerfPropName;
+
+struct DAVProperties
+{
+ static const OUString CREATIONDATE;
+ static const OUString DISPLAYNAME;
+ static const OUString GETCONTENTLANGUAGE;
+ static const OUString GETCONTENTLENGTH;
+ static const OUString GETCONTENTTYPE;
+ static const OUString GETETAG;
+ static const OUString GETLASTMODIFIED;
+ static const OUString LOCKDISCOVERY;
+ static const OUString RESOURCETYPE;
+ static const OUString SUPPORTEDLOCK;
+ static const OUString EXECUTABLE;
+
+ static void createSerfPropName( const OUString & rFullName,
+ SerfPropName & rName );
+ static void createUCBPropName ( const char * nspace,
+ const char * name,
+ OUString & rFullName );
+
+ static bool isUCBDeadProperty( const SerfPropName & rName );
+ static bool isUCBSpecialProperty( const OUString & rFullName,
+ OUString & rParsedName );
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVPROPERTIES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVRequestEnvironment.hxx b/ucb/source/ucp/webdav/DAVRequestEnvironment.hxx
new file mode 100644
index 000000000..1b1faff89
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVRequestEnvironment.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVREQUESTENVIRONMENT_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVREQUESTENVIRONMENT_HXX
+
+#include <vector>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "DAVAuthListener.hxx"
+
+namespace http_dav_ucp
+{
+ typedef std::pair< OUString, OUString > DAVRequestHeader;
+ typedef std::vector< DAVRequestHeader > DAVRequestHeaders;
+
+struct DAVRequestEnvironment
+{
+ OUString m_aRequestURI;
+ rtl::Reference< DAVAuthListener > m_xAuthListener;
+ DAVRequestHeaders m_aRequestHeaders;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv;
+
+DAVRequestEnvironment( const OUString & rRequestURI,
+ const rtl::Reference< DAVAuthListener > & xListener,
+ const DAVRequestHeaders & rRequestHeaders,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv)
+ : m_aRequestURI( rRequestURI ),
+ m_xAuthListener( xListener ),
+ m_aRequestHeaders( rRequestHeaders ),
+ m_xEnv( xEnv ){}
+
+ DAVRequestEnvironment() {}
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVREQUESTENVIRONMENT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVResource.hxx b/ucb/source/ucp/webdav/DAVResource.hxx
new file mode 100644
index 000000000..aca9f3846
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVResource.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCE_HXX
+
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace http_dav_ucp
+{
+
+struct DAVPropertyValue
+{
+ OUString Name;
+ css::uno::Any Value;
+ bool IsCaseSensitive;
+
+ DAVPropertyValue() : IsCaseSensitive( true ) {}
+};
+
+struct DAVResource
+{
+ OUString uri;
+ std::vector< DAVPropertyValue > properties;
+
+ DAVResource() {}
+ explicit DAVResource( const OUString & inUri ) : uri( inUri ) {}
+};
+
+struct DAVResourceInfo
+{
+ OUString uri;
+ std::vector < OUString > properties;
+
+ explicit DAVResourceInfo( const OUString & inUri ) : uri( inUri ) {}
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVResourceAccess.cxx b/ucb/source/ucp/webdav/DAVResourceAccess.cxx
new file mode 100644
index 000000000..90001a818
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVResourceAccess.cxx
@@ -0,0 +1,1116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/ucb/XWebDAVCommandEnvironment.hpp>
+
+#include <ucbhelper/simpleauthenticationrequest.hxx>
+#include <comphelper/seekableinput.hxx>
+#include <sal/log.hxx>
+
+#include "DAVAuthListenerImpl.hxx"
+#include "DAVResourceAccess.hxx"
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+
+using namespace http_dav_ucp;
+using namespace com::sun::star;
+
+
+// DAVAuthListener_Impl Implementation.
+
+
+// virtual
+int DAVAuthListener_Impl::authenticate(
+ const OUString & inRealm,
+ const OUString & inHostName,
+ OUString & inoutUserName,
+ OUString & outPassWord,
+ bool bCanUseSystemCredentials,
+ bool bUsePreviousCredentials )
+{
+ if ( m_xEnv.is() )
+ {
+ uno::Reference< task::XInteractionHandler > xIH
+ = m_xEnv->getInteractionHandler();
+
+ if ( xIH.is() )
+ {
+ // Providing previously retrieved credentials will cause the password
+ // container to reject these. Thus, the credential input dialog will be shown again.
+ // #102871# - Supply username and password from previous try.
+ // Password container service depends on this!
+ if ( inoutUserName.isEmpty() && bUsePreviousCredentials )
+ inoutUserName = m_aPrevUsername;
+
+ if ( outPassWord.isEmpty() && bUsePreviousCredentials )
+ outPassWord = m_aPrevPassword;
+
+ rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest
+ = new ucbhelper::SimpleAuthenticationRequest(
+ m_aURL, inHostName, inRealm, inoutUserName,
+ outPassWord,
+ true /*bAllowPersistentStoring*/,
+ bCanUseSystemCredentials );
+ xIH->handle( xRequest.get() );
+
+ rtl::Reference< ucbhelper::InteractionContinuation > xSelection
+ = xRequest->getSelection();
+
+ if ( xSelection.is() )
+ {
+ // Handler handled the request.
+ uno::Reference< task::XInteractionAbort > xAbort(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( !xAbort.is() )
+ {
+ const rtl::Reference<
+ ucbhelper::InteractionSupplyAuthentication > & xSupp
+ = xRequest->getAuthenticationSupplier();
+
+ bool bUseSystemCredentials = false;
+
+ if ( bCanUseSystemCredentials )
+ bUseSystemCredentials
+ = xSupp->getUseSystemCredentials();
+
+ if ( bUseSystemCredentials )
+ {
+ // This is the (strange) way to tell neon to use
+ // system credentials.
+ inoutUserName.clear();
+ outPassWord.clear();
+ }
+ else
+ {
+ inoutUserName = xSupp->getUserName();
+ outPassWord = xSupp->getPassword();
+ }
+
+ // #102871# - Remember username and password.
+ m_aPrevUsername = inoutUserName;
+ m_aPrevPassword = outPassWord;
+
+ // go on.
+ return 0;
+ }
+ }
+ }
+ }
+ // Abort.
+ return -1;
+}
+
+
+// DAVResourceAccess Implementation.
+
+
+DAVResourceAccess::DAVResourceAccess(
+ const uno::Reference< uno::XComponentContext > & rContext,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ const OUString & rURL )
+: m_aURL( rURL ),
+ m_xSessionFactory( rSessionFactory ),
+ m_xContext( rContext )
+{
+}
+
+
+DAVResourceAccess::DAVResourceAccess( const DAVResourceAccess & rOther )
+: m_aURL( rOther.m_aURL ),
+ m_aPath( rOther.m_aPath ),
+ m_xSession( rOther.m_xSession ),
+ m_xSessionFactory( rOther.m_xSessionFactory ),
+ m_xContext( rOther.m_xContext ),
+ m_aRedirectURIs( rOther.m_aRedirectURIs )
+{
+}
+
+
+DAVResourceAccess & DAVResourceAccess::operator=(
+ const DAVResourceAccess & rOther )
+{
+ m_aURL = rOther.m_aURL;
+ m_aPath = rOther.m_aPath;
+ m_xSession = rOther.m_xSession;
+ m_xSessionFactory = rOther.m_xSessionFactory;
+ m_xContext = rOther.m_xContext;
+ m_aRedirectURIs = rOther.m_aRedirectURIs;
+
+ return *this;
+}
+
+
+void DAVResourceAccess::PROPFIND(
+ const Depth nDepth,
+ const std::vector< OUString > & rPropertyNames,
+ std::vector< DAVResource > & rResources,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPFIND,
+ aHeaders );
+
+ m_xSession->PROPFIND( getRequestURI(),
+ nDepth,
+ rPropertyNames,
+ rResources,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::PROPFIND(
+ const Depth nDepth,
+ std::vector< DAVResourceInfo > & rResInfo,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPFIND,
+ aHeaders );
+
+ m_xSession->PROPFIND( getRequestURI(),
+ nDepth,
+ rResInfo,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) ) ;
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::PROPPATCH(
+ const std::vector< ProppatchValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PROPPATCH,
+ aHeaders );
+
+ m_xSession->PROPPATCH( getRequestURI(),
+ rValues,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::HEAD(
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_HEAD,
+ aHeaders );
+
+ m_xSession->HEAD( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::GET(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ xStream = m_xSession->GET( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+void DAVResourceAccess::GET(
+ uno::Reference< io::XOutputStream > & rStream,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ m_xSession->GET( getRequestURI(),
+ rStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::GET(
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ xStream = m_xSession->GET( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::GET(
+ DAVRequestHeaders &rRequestHeaders,
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ rRequestHeaders );
+
+ xStream = m_xSession->GET( getRequestURI(),
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ rRequestHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+void DAVResourceAccess::GET(
+ uno::Reference< io::XOutputStream > & rStream,
+ const std::vector< OUString > & rHeaderNames,
+ DAVResource & rResource,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ bool bRetry;
+ int errorCount = 0;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_GET,
+ aHeaders );
+
+ m_xSession->GET( getRequestURI(),
+ rStream,
+ rHeaderNames,
+ rResource,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::abort()
+{
+ // 17.11.09 (tkr): abort currently disabled caused by issue i106766
+ // initialize();
+ // m_xSession->abort();
+ SAL_INFO("ucb.ucp.webdav", "Not implemented. -> #i106766#" );
+}
+
+
+namespace {
+
+ /// @throws DAVException
+ void resetInputStream( const uno::Reference< io::XInputStream > & rStream )
+ {
+ try
+ {
+ uno::Reference< io::XSeekable > xSeekable(
+ rStream, uno::UNO_QUERY );
+ if ( xSeekable.is() )
+ {
+ xSeekable->seek( 0 );
+ return;
+ }
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ }
+ catch ( io::IOException const & )
+ {
+ }
+
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+} // namespace
+
+
+void DAVResourceAccess::PUT(
+ const uno::Reference< io::XInputStream > & rStream,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rStream, m_xContext );
+
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ resetInputStream( xSeekableStream );
+
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_PUT,
+ aHeaders );
+
+ m_xSession->PUT( getRequestURI(),
+ xSeekableStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+uno::Reference< io::XInputStream > DAVResourceAccess::POST(
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & rInputStream,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rInputStream, m_xContext );
+
+ uno::Reference< io::XInputStream > xStream;
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ {
+ resetInputStream( xSeekableStream );
+ bRetry = false;
+ }
+
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_POST,
+ aHeaders );
+
+ xStream = m_xSession->POST( getRequestURI(),
+ rContentType,
+ rReferer,
+ xSeekableStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl(
+ xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+
+ if ( e.getError() == DAVException::DAV_HTTP_REDIRECT )
+ {
+ // #i74980# - Upon POST redirect, do a GET.
+ return GET( xEnv );
+ }
+ }
+ }
+ while ( bRetry );
+
+ return xStream;
+}
+
+
+void DAVResourceAccess::POST(
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & rInputStream,
+ uno::Reference< io::XOutputStream > & rOutputStream,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ initialize();
+
+ // Make stream seekable, if it not. Needed, if request must be retried.
+ uno::Reference< io::XInputStream > xSeekableStream
+ = comphelper::OSeekableInputWrapper::CheckSeekableCanWrap(
+ rInputStream, m_xContext );
+
+ int errorCount = 0;
+ bool bRetry = false;
+ do
+ {
+ if ( bRetry )
+ {
+ resetInputStream( xSeekableStream );
+ bRetry = false;
+ }
+
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_POST,
+ aHeaders );
+
+ m_xSession->POST( getRequestURI(),
+ rContentType,
+ rReferer,
+ xSeekableStream,
+ rOutputStream,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+
+ if ( e.getError() == DAVException::DAV_HTTP_REDIRECT )
+ {
+ // #i74980# - Upon POST redirect, do a GET.
+ GET( rOutputStream, xEnv );
+ return;
+ }
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::MKCOL(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_MKCOL,
+ aHeaders );
+
+ m_xSession->MKCOL( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::COPY(
+ const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_COPY,
+ aHeaders );
+
+ m_xSession->COPY( rSourcePath,
+ rDestinationURI,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ),
+ bOverwrite );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::MOVE(
+ const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_MOVE,
+ aHeaders );
+
+ m_xSession->MOVE( rSourcePath,
+ rDestinationURI,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ),
+ bOverwrite );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::DESTROY(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_DELETE,
+ aHeaders );
+
+ m_xSession->DESTROY( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+// set new lock.
+void DAVResourceAccess::LOCK(
+ ucb::Lock & inLock,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_LOCK,
+ aHeaders );
+
+ m_xSession->LOCK( getRequestURI(),
+ inLock,
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+void DAVResourceAccess::UNLOCK(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ initialize();
+
+ int errorCount = 0;
+ bool bRetry;
+ do
+ {
+ bRetry = false;
+ try
+ {
+ DAVRequestHeaders aHeaders;
+ getUserRequestHeaders( xEnv,
+ getRequestURI(),
+ ucb::WebDAVHTTPMethod_UNLOCK,
+ aHeaders );
+
+ m_xSession->UNLOCK( getRequestURI(),
+ DAVRequestEnvironment(
+ getRequestURI(),
+ new DAVAuthListener_Impl( xEnv, m_aURL ),
+ aHeaders, xEnv ) );
+ }
+ catch ( DAVException & e )
+ {
+ errorCount++;
+ bRetry = handleException( e, errorCount );
+ if ( !bRetry )
+ throw;
+ }
+ }
+ while ( bRetry );
+}
+
+
+void DAVResourceAccess::setURL( const OUString & rNewURL )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_aURL = rNewURL;
+ m_aPath.clear(); // Next initialize() will create new session.
+}
+
+
+// init dav session and path
+void DAVResourceAccess::initialize()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ if ( m_aPath.isEmpty() )
+ {
+ SerfUri aURI( m_aURL );
+ OUString aPath( aURI.GetPath() );
+
+ /* #134089# - Check URI */
+ if ( aPath.isEmpty() )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ /* #134089# - Check URI */
+ if ( aURI.GetHost().isEmpty() )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ if ( !m_xSession.is() || !m_xSession->CanUse( m_aURL ) )
+ {
+ m_xSession.clear();
+
+ // create new webdav session
+ m_xSession
+ = m_xSessionFactory->createDAVSession( m_aURL, m_xContext );
+
+ if ( !m_xSession.is() )
+ return;
+ }
+
+ // Own URI is needed for redirect cycle detection.
+ m_aRedirectURIs.push_back( aURI );
+
+ // Success.
+ m_aPath = aPath;
+
+ // Not only the path has to be encoded
+ m_aURL = aURI.GetURI();
+ }
+}
+
+
+const OUString & DAVResourceAccess::getRequestURI() const
+{
+ SAL_WARN_IF( !m_xSession.is(), "ucb.ucp.webdav",
+ "DAVResourceAccess::getRequestURI - Not initialized!" );
+
+ // In case a proxy is used we have to use the absolute URI for a request.
+ if ( m_xSession->UsesProxy() )
+ return m_aURL;
+
+ return m_aPath;
+}
+
+
+// static
+void DAVResourceAccess::getUserRequestHeaders(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv,
+ const OUString & rURI,
+ ucb::WebDAVHTTPMethod eMethod,
+ DAVRequestHeaders & rRequestHeaders )
+{
+ if ( !xEnv.is() )
+ return;
+
+ uno::Reference< ucb::XWebDAVCommandEnvironment > xDAVEnv(
+ xEnv, uno::UNO_QUERY );
+
+ if ( !xDAVEnv.is() )
+ return;
+
+ uno::Sequence< beans::StringPair > aRequestHeaders
+ = xDAVEnv->getUserRequestHeaders( rURI, eMethod );
+
+ for ( sal_Int32 n = 0; n < aRequestHeaders.getLength(); ++n )
+ {
+ rRequestHeaders.push_back(
+ DAVRequestHeader( aRequestHeaders[ n ].First,
+ aRequestHeaders[ n ].Second ) );
+ }
+}
+
+
+bool DAVResourceAccess::detectRedirectCycle(
+ const OUString& rRedirectURL )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ SerfUri aUri( rRedirectURL );
+
+ return std::any_of(m_aRedirectURIs.begin(), m_aRedirectURIs.end(),
+ [&aUri](const SerfUri& rUri) { return aUri == rUri; });
+}
+
+
+void DAVResourceAccess::resetUri()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ if ( ! m_aRedirectURIs.empty() )
+ {
+ std::vector< SerfUri >::const_iterator it = m_aRedirectURIs.begin();
+
+ SerfUri aUri( *it );
+ m_aRedirectURIs.clear();
+ setURL ( aUri.GetURI() );
+ initialize();
+ }
+}
+
+
+bool DAVResourceAccess::handleException( DAVException & e, int errorCount )
+{
+ switch ( e.getError() )
+ {
+ case DAVException::DAV_HTTP_REDIRECT:
+ if ( !detectRedirectCycle( e.getData() ) )
+ {
+ // set new URL and path.
+ setURL( e.getData() );
+ initialize();
+ return true;
+ }
+ return false;
+ // --> tkr #67048# copy & paste images doesn't display.
+ // if we have a bad connection try again. Up to three times.
+ case DAVException::DAV_HTTP_ERROR:
+ // retry up to three times, if not a client-side error.
+ if ( ( e.getStatus() < 400 || e.getStatus() >= 500 ||
+ e.getStatus() == 413 ) &&
+ errorCount < 3 )
+ {
+ return true;
+ }
+ return false;
+ // <--
+ // --> tkr: if connection has said retry then retry!
+ case DAVException::DAV_HTTP_RETRY:
+ return true;
+ // <--
+ default:
+ return false; // Abort
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVResourceAccess.hxx b/ucb/source/ucp/webdav/DAVResourceAccess.hxx
new file mode 100644
index 000000000..37d76d83f
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVResourceAccess.hxx
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCEACCESS_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCEACCESS_HXX
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/WebDAVHTTPMethod.hpp>
+#include "DAVAuthListener.hxx"
+#include "DAVException.hxx"
+#include "DAVSession.hxx"
+#include "DAVResource.hxx"
+#include "DAVTypes.hxx"
+#include "SerfUri.hxx"
+
+namespace http_dav_ucp
+{
+
+class DAVSessionFactory;
+
+class DAVResourceAccess
+{
+ osl::Mutex m_aMutex;
+ OUString m_aURL;
+ OUString m_aPath;
+ rtl::Reference< DAVSession > m_xSession;
+ rtl::Reference< DAVSessionFactory > m_xSessionFactory;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ std::vector< SerfUri > m_aRedirectURIs;
+
+public:
+ DAVResourceAccess() = default;
+ DAVResourceAccess( const css::uno::Reference< css::uno::XComponentContext > & rContext,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ const OUString & rURL );
+ DAVResourceAccess( const DAVResourceAccess & rOther );
+
+ DAVResourceAccess & operator=( const DAVResourceAccess & rOther );
+
+ /// @throws DAVException
+ void setURL( const OUString & rNewURL );
+
+ void resetUri();
+
+ const OUString & getURL() const { return m_aURL; }
+
+ rtl::Reference< DAVSessionFactory > getSessionFactory() const
+ { return m_xSessionFactory; }
+
+ // DAV methods
+
+
+ // allprop & named
+ /// @throws DAVException
+ void
+ PROPFIND( const Depth nDepth,
+ const std::vector< OUString > & rPropertyNames,
+ std::vector< DAVResource > & rResources,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // propnames
+ /// @throws DAVException
+ void
+ PROPFIND( const Depth nDepth,
+ std::vector< DAVResourceInfo > & rResInfo,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ PROPPATCH( const std::vector< ProppatchValue > & rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ void
+ HEAD( const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ GET( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ GET( css::uno::Reference< css::io::XOutputStream > & rStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ GET( const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ GET( DAVRequestHeaders & rRequestHeaders,
+ const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ GET( css::uno::Reference< css::io::XOutputStream > & rStream,
+ const std::vector< OUString > & rHeaderNames, // empty == 'all'
+ DAVResource & rResource,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ PUT( const css::uno::Reference< css::io::XInputStream > & rStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & rInputStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ void
+ POST( const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & rInputStream,
+ css::uno::Reference< css::io::XOutputStream > & rOutputStream,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws DAVException
+ void
+ MKCOL( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ COPY( const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ MOVE( const OUString & rSourcePath,
+ const OUString & rDestinationURI,
+ bool bOverwrite,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ DESTROY( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // set new lock.
+ /// @throws DAVException
+ void
+ LOCK( css::ucb::Lock & inLock,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ UNLOCK( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws DAVException
+ void
+ static abort();
+
+ // helper
+ static void
+ getUserRequestHeaders(
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv,
+ const OUString & rURI,
+ css::ucb::WebDAVHTTPMethod eMethod,
+ DAVRequestHeaders & rRequestHeaders );
+
+private:
+ const OUString & getRequestURI() const;
+ /// @throws DAVException
+ bool detectRedirectCycle( const OUString& rRedirectURL );
+ /// @throws DAVException
+ bool handleException( DAVException & e, int errorCount );
+ /// @throws DAVException
+ void initialize();
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVRESOURCEACCESS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVSession.hxx b/ucb/source/ucp/webdav/DAVSession.hxx
new file mode 100644
index 000000000..aa47b1504
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVSession.hxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSION_HXX
+
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include "DAVResource.hxx"
+#include "DAVSessionFactory.hxx"
+#include "DAVTypes.hxx"
+#include "DAVRequestEnvironment.hxx"
+
+namespace com::sun::star::ucb {
+ struct Lock;
+}
+
+namespace http_dav_ucp
+{
+
+class DAVAuthListener;
+
+class DAVSession
+{
+public:
+ void acquire()
+ {
+ osl_atomic_increment( &m_nRefCount );
+ }
+
+ void release()
+ {
+ if ( osl_atomic_decrement( &m_nRefCount ) == 0 )
+ {
+ m_xFactory->releaseElement( this );
+ delete this;
+ }
+ }
+
+ virtual bool CanUse( const OUString & inPath ) = 0;
+
+ virtual bool UsesProxy() = 0;
+
+ // DAV methods
+
+
+ // NOT USED
+ /*
+ virtual void OPTIONS( const OUString & inPath,
+ DAVCapabilities & outCapabilities,
+ const DAVRequestEnvironment & rEnv )
+ throw( DAVException ) = 0;
+ */
+
+ // allprop & named
+ /// @throws DAVException
+ virtual void PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropertyNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // propnames
+ /// @throws DAVException
+ virtual void PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream >& o,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream >& o,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void PUT( const OUString & inPath,
+ const css::uno::Reference< css::io::XInputStream >& s,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ css::uno::Reference< css::io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void COPY( const OUString & inSource,
+ const OUString & inDestination,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverwrite = false ) = 0;
+
+ /// @throws DAVException
+ virtual void MOVE( const OUString & inSource,
+ const OUString & inDestination,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverwrite = false ) = 0;
+
+ /// @throws DAVException
+ virtual void DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // set new lock.
+ /// @throws DAVException
+ virtual void LOCK( const OUString & inPath,
+ css::ucb::Lock & inLock,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ // refresh existing lock.
+ /// @throws DAVException
+ virtual sal_Int64 LOCK( const OUString & inPath,
+ sal_Int64 nTimeout,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void UNLOCK( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) = 0;
+
+ /// @throws DAVException
+ virtual void abort() = 0;
+
+protected:
+ rtl::Reference< DAVSessionFactory > m_xFactory;
+
+ explicit DAVSession( rtl::Reference< DAVSessionFactory > const & rFactory )
+ : m_xFactory( rFactory ), m_nRefCount( 0 ) {}
+
+ virtual ~DAVSession() {}
+
+private:
+ DAVSessionFactory::Map::iterator m_aContainerIt;
+ oslInterlockedCount m_nRefCount;
+
+ friend class DAVSessionFactory;
+ friend struct std::default_delete< DAVSession >;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVSessionFactory.cxx b/ucb/source/ucp/webdav/DAVSessionFactory.cxx
new file mode 100644
index 000000000..6a0963f91
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVSessionFactory.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "DAVSessionFactory.hxx"
+#include "SerfSession.hxx"
+#include "SerfUri.hxx"
+
+using namespace http_dav_ucp;
+using namespace com::sun::star;
+
+DAVSessionFactory::~DAVSessionFactory()
+{
+}
+
+rtl::Reference< DAVSession > DAVSessionFactory::createDAVSession(
+ const OUString & inUri,
+ const uno::Reference< uno::XComponentContext > & rxContext )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !m_xProxyDecider.get() )
+ m_xProxyDecider.reset( new ucbhelper::InternetProxyDecider( rxContext ) );
+
+ Map::iterator aIt = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&inUri](const Map::value_type& rEntry) { return rEntry.second->CanUse( inUri ); });
+
+ if ( aIt == m_aMap.end() )
+ {
+ SerfUri aURI( inUri );
+
+ std::unique_ptr< DAVSession > xElement(
+ new SerfSession( this, inUri, *m_xProxyDecider ) );
+
+ aIt = m_aMap.emplace( inUri, xElement.get() ).first;
+ aIt->second->m_aContainerIt = aIt;
+ xElement.release();
+ return aIt->second;
+ }
+ else if ( osl_atomic_increment( &aIt->second->m_nRefCount ) > 1 )
+ {
+ rtl::Reference< DAVSession > xElement( aIt->second );
+ osl_atomic_decrement( &aIt->second->m_nRefCount );
+ return xElement;
+ }
+ else
+ {
+ osl_atomic_decrement( &aIt->second->m_nRefCount );
+ aIt->second->m_aContainerIt = m_aMap.end();
+
+ // If URL scheme is different from http or https we definitely
+ // have to use a proxy and therefore can optimize the getProxy
+ // call a little:
+ SerfUri aURI( inUri );
+
+ aIt->second = new SerfSession( this, inUri, *m_xProxyDecider );
+ aIt->second->m_aContainerIt = aIt;
+ return aIt->second;
+ }
+}
+
+void DAVSessionFactory::releaseElement( DAVSession * pElement )
+{
+ assert( pElement );
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( pElement->m_aContainerIt != m_aMap.end() )
+ m_aMap.erase( pElement->m_aContainerIt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVSessionFactory.hxx b/ucb/source/ucp/webdav/DAVSessionFactory.hxx
new file mode 100644
index 000000000..ec1fcfe3c
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVSessionFactory.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSIONFACTORY_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSIONFACTORY_HXX
+
+#ifdef min
+#undef min // GNU libstdc++ <memory> includes <limit> which defines methods called min...
+#endif
+#include <map>
+#include <memory>
+#include <osl/mutex.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ref.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <ucbhelper/proxydecider.hxx>
+
+using namespace com::sun::star;
+
+namespace com::sun::star::lang {
+ class XMultiServiceFactory;
+}
+
+namespace http_dav_ucp
+{
+
+class DAVSession;
+
+class DAVSessionFactory : public salhelper::SimpleReferenceObject
+{
+public:
+ virtual ~DAVSessionFactory() override;
+
+ /// @throws DAVException
+ rtl::Reference< DAVSession >
+ createDAVSession( const OUString & inUri,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+private:
+ typedef std::map< OUString, DAVSession * > Map;
+
+ Map m_aMap;
+ osl::Mutex m_aMutex;
+ std::unique_ptr< ucbhelper::InternetProxyDecider > m_xProxyDecider;
+
+ void releaseElement( DAVSession * pElement );
+
+ friend class DAVSession;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVSESSIONFACTORY_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DAVTypes.hxx b/ucb/source/ucp/webdav/DAVTypes.hxx
new file mode 100644
index 000000000..3cce18abb
--- /dev/null
+++ b/ucb/source/ucp/webdav/DAVTypes.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVTYPES_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVTYPES_HXX
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace http_dav_ucp
+{
+/* RFC 2518
+
+15.1 Class 1
+
+ A class 1 compliant resource MUST meet all "MUST" requirements in all
+ sections of this document.
+
+ Class 1 compliant resources MUST return, at minimum, the value "1" in
+ the DAV header on all responses to the OPTIONS method.
+
+15.2 Class 2
+
+ A class 2 compliant resource MUST meet all class 1 requirements and
+ support the LOCK method, the supportedlock property, the
+ lockdiscovery property, the Time-Out response header and the Lock-
+ Token request header. A class "2" compliant resource SHOULD also
+ support the Time-Out request header and the owner XML element.
+
+ Class 2 compliant resources MUST return, at minimum, the values "1"
+ and "2" in the DAV header on all responses to the OPTIONS method.
+*/
+
+struct DAVCapabilities
+{
+ bool class1;
+ bool class2;
+ bool executable; // supports "executable" property (introduced by mod_dav)
+
+ DAVCapabilities() : class1( false ), class2( false ), executable( false ) {}
+};
+
+enum Depth { DAVZERO = 0, DAVONE = 1, DAVINFINITY = -1 };
+
+enum ProppatchOperation { PROPSET = 0, PROPREMOVE = 1 };
+
+struct ProppatchValue
+{
+ ProppatchOperation operation;
+ OUString name;
+ css::uno::Any value;
+
+ ProppatchValue( const ProppatchOperation o,
+ const OUString & n,
+ const css::uno::Any & v )
+ : operation( o ), name( n ), value( v ) {}
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_DAVTYPES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DateTimeHelper.cxx b/ucb/source/ucp/webdav/DateTimeHelper.cxx
new file mode 100644
index 000000000..dfa21ed56
--- /dev/null
+++ b/ucb/source/ucp/webdav/DateTimeHelper.cxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/time.h>
+#include <com/sun/star/util/DateTime.hpp>
+#include "DateTimeHelper.hxx"
+
+using namespace com::sun::star::util;
+
+using namespace http_dav_ucp;
+
+bool DateTimeHelper::ISO8601_To_DateTime (const OUString& s,
+ DateTime& dateTime)
+{
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ int year, month, day, hours, minutes, off_hours, off_minutes, fix;
+ double seconds;
+
+ // 2001-01-01T12:30:00Z
+ int n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lfZ",
+ &year, &month, &day, &hours, &minutes, &seconds );
+ if ( n == 6 )
+ {
+ fix = 0;
+ }
+ else
+ {
+ // 2001-01-01T12:30:00+03:30
+ n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lf+%02d:%02d",
+ &year, &month, &day, &hours, &minutes, &seconds,
+ &off_hours, &off_minutes );
+ if ( n == 8 )
+ {
+ fix = - off_hours * 3600 - off_minutes * 60;
+ }
+ else
+ {
+ // 2001-01-01T12:30:00-03:30
+ n = sscanf( aDT.getStr(), "%04d-%02d-%02dT%02d:%02d:%lf-%02d:%02d",
+ &year, &month, &day, &hours, &minutes, &seconds,
+ &off_hours, &off_minutes );
+ if ( n == 8 )
+ {
+ fix = off_hours * 3600 + off_minutes * 60;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // Convert to local time...
+
+ oslDateTime aDateTime;
+ aDateTime.NanoSeconds = 0;
+ aDateTime.Seconds = sal::static_int_cast< sal_uInt16 >(seconds); // 0-59
+ aDateTime.Minutes = sal::static_int_cast< sal_uInt16 >(minutes); // 0-59
+ aDateTime.Hours = sal::static_int_cast< sal_uInt16 >(hours); // 0-23
+ aDateTime.Day = sal::static_int_cast< sal_uInt16 >(day); // 1-31
+ aDateTime.DayOfWeek = 0; // 0-6, 0 = Sunday
+ aDateTime.Month = sal::static_int_cast< sal_uInt16 >(month); // 1-12
+ aDateTime.Year = sal::static_int_cast< sal_Int16 >(year);
+
+ TimeValue aTimeValue;
+ if ( osl_getTimeValueFromDateTime( &aDateTime, &aTimeValue ) )
+ {
+ aTimeValue.Seconds += fix;
+
+ if ( osl_getLocalTimeFromSystemTime( &aTimeValue, &aTimeValue ) )
+ {
+ if ( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) )
+ {
+ dateTime.Year = aDateTime.Year;
+ dateTime.Month = aDateTime.Month;
+ dateTime.Day = aDateTime.Day;
+ dateTime.Hours = aDateTime.Hours;
+ dateTime.Minutes = aDateTime.Minutes;
+ dateTime.Seconds = aDateTime.Seconds;
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*
+sal_Int32 DateTimeHelper::convertDayToInt (const OUString& day)
+{
+ if (day.equalsAscii("Sun"))
+ return 0;
+ else if (day.equalsAscii("Mon"))
+ return 1;
+ else if (day.equalsAscii("Tue"))
+ return 2;
+ else if (day.equalsAscii("Wed"))
+ return 3;
+ else if (day.equalsAscii("Thu"))
+ return 4;
+ else if (day.equalsAscii("Fri"))
+ return 5;
+ else if (day.equalsAscii("Sat"))
+ return 6;
+ else
+ return -1;
+}
+*/
+
+sal_Int32 DateTimeHelper::convertMonthToInt (const OUString& month)
+{
+ if (month == "Jan")
+ return 1;
+ else if (month == "Feb")
+ return 2;
+ else if (month == "Mar")
+ return 3;
+ else if (month == "Apr")
+ return 4;
+ else if (month == "May")
+ return 5;
+ else if (month == "Jun")
+ return 6;
+ else if (month == "Jul")
+ return 7;
+ else if (month == "Aug")
+ return 8;
+ else if (month == "Sep")
+ return 9;
+ else if (month == "Oct")
+ return 10;
+ else if (month == "Nov")
+ return 11;
+ else if (month == "Dec")
+ return 12;
+ else
+ return 0;
+}
+
+bool DateTimeHelper::RFC2068_To_DateTime (const OUString& s,
+ DateTime& dateTime)
+{
+ int year;
+ int day;
+ int hours;
+ int minutes;
+ int seconds;
+ char string_month[3 + 1];
+ char string_day[3 + 1];
+
+ sal_Int32 found = s.indexOf (',');
+ if (found != -1)
+ {
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ // RFC 1123
+ found = sscanf (aDT.getStr(), "%3s, %2d %3s %4d %2d:%2d:%2d GMT",
+ string_day, &day, string_month, &year, &hours, &minutes, &seconds);
+ if (found != 7)
+ {
+ // RFC 1036
+ found = sscanf (aDT.getStr(), "%3s, %2d-%3s-%2d %2d:%2d:%2d GMT",
+ string_day, &day, string_month, &year, &hours, &minutes, &seconds);
+ }
+ found = (found == 7) ? 1 : 0;
+ }
+ else
+ {
+ OString aDT (s.getStr(), s.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ // ANSI C's asctime () format
+ found = sscanf (aDT.getStr(), "%3s %3s %d %2d:%2d:%2d %4d",
+ string_day, string_month,
+ &day, &hours, &minutes, &seconds, &year);
+ found = (found == 7) ? 1 : 0;
+ }
+
+ if (found)
+ {
+ found = 0;
+
+ int month = DateTimeHelper::convertMonthToInt (
+ OUString::createFromAscii (string_month));
+ if (month)
+ {
+ // Convert to local time...
+
+ oslDateTime aDateTime;
+ aDateTime.NanoSeconds = 0;
+ aDateTime.Seconds = sal::static_int_cast< sal_uInt16 >(seconds);
+ // 0-59
+ aDateTime.Minutes = sal::static_int_cast< sal_uInt16 >(minutes);
+ // 0-59
+ aDateTime.Hours = sal::static_int_cast< sal_uInt16 >(hours);
+ // 0-23
+ aDateTime.Day = sal::static_int_cast< sal_uInt16 >(day);
+ // 1-31
+ aDateTime.DayOfWeek = 0; //dayofweek; // 0-6, 0 = Sunday
+ aDateTime.Month = sal::static_int_cast< sal_uInt16 >(month);
+ // 1-12
+ aDateTime.Year = sal::static_int_cast< sal_Int16 >(year);
+
+ TimeValue aTimeValue;
+ if ( osl_getTimeValueFromDateTime( &aDateTime,
+ &aTimeValue ) )
+ {
+ if ( osl_getLocalTimeFromSystemTime( &aTimeValue,
+ &aTimeValue ) )
+ {
+ if ( osl_getDateTimeFromTimeValue( &aTimeValue,
+ &aDateTime ) )
+ {
+ dateTime.Year = aDateTime.Year;
+ dateTime.Month = aDateTime.Month;
+ dateTime.Day = aDateTime.Day;
+ dateTime.Hours = aDateTime.Hours;
+ dateTime.Minutes = aDateTime.Minutes;
+ dateTime.Seconds = aDateTime.Seconds;
+
+ found = 1;
+ }
+ }
+ }
+ }
+ }
+
+ return found;
+}
+
+bool DateTimeHelper::convert (const OUString& s, DateTime& dateTime)
+{
+ if (ISO8601_To_DateTime (s, dateTime))
+ return true;
+ else if (RFC2068_To_DateTime (s, dateTime))
+ return true;
+ else
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/DateTimeHelper.hxx b/ucb/source/ucp/webdav/DateTimeHelper.hxx
new file mode 100644
index 000000000..b63921533
--- /dev/null
+++ b/ucb/source/ucp/webdav/DateTimeHelper.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_DATETIMEHELPER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_DATETIMEHELPER_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star::util {
+ struct DateTime;
+}
+
+namespace rtl {
+ class OUString;
+}
+
+namespace http_dav_ucp
+{
+
+class DateTimeHelper
+{
+private:
+ static sal_Int32 convertMonthToInt (const OUString& );
+
+ static bool ISO8601_To_DateTime (const OUString&,
+ css::util::DateTime& );
+
+ static bool RFC2068_To_DateTime (const OUString&,
+ css::util::DateTime& );
+
+public:
+ static bool convert (const OUString&,
+ css::util::DateTime& );
+};
+
+} // namespace http_dav_ucp
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/PropertyMap.hxx b/ucb/source/ucp/webdav/PropertyMap.hxx
new file mode 100644
index 000000000..ef5693d76
--- /dev/null
+++ b/ucb/source/ucp/webdav/PropertyMap.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_PROPERTYMAP_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_PROPERTYMAP_HXX
+
+#include <com/sun/star/beans/Property.hpp>
+#include <unordered_set>
+
+namespace http_dav_ucp {
+
+struct equalPropertyName
+{
+ bool operator()( const css::beans::Property & p1,
+ const css::beans::Property & p2 ) const
+ {
+ return p1.Name == p2.Name;
+ }
+};
+
+struct hashPropertyName
+{
+ size_t operator()( const css::beans::Property & p ) const
+ {
+ return p.Name.hashCode();
+ }
+};
+
+typedef std::unordered_set
+<
+ css::beans::Property,
+ hashPropertyName,
+ equalPropertyName
+>
+PropertyMap;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfCallbacks.cxx b/ucb/source/ucp/webdav/SerfCallbacks.cxx
new file mode 100644
index 000000000..5d1194ea9
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfCallbacks.cxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfCallbacks.hxx"
+
+#include "SerfSession.hxx"
+#include "SerfRequestProcessor.hxx"
+
+using namespace http_dav_ucp;
+
+extern "C" apr_status_t Serf_ConnectSetup( apr_socket_t *skt,
+ serf_bucket_t **read_bkt,
+ serf_bucket_t **write_bkt,
+ void *setup_baton,
+ apr_pool_t *pool )
+{
+ SerfSession* pSerfSession = static_cast< SerfSession* >( setup_baton );
+ return pSerfSession->setupSerfConnection( skt,
+ read_bkt,
+ write_bkt,
+ pool );
+}
+
+extern "C" apr_status_t Serf_Credentials( char **username,
+ char **password,
+ serf_request_t *request,
+ void *baton,
+ int code,
+ const char *authn_type,
+ const char *realm,
+ apr_pool_t *pool )
+{
+ SerfRequestProcessor* pReqProc = static_cast< SerfRequestProcessor* >( baton );
+ return pReqProc->provideSerfCredentials( username,
+ password,
+ request,
+ code,
+ authn_type,
+ realm,
+ pool );
+}
+
+extern "C" apr_status_t Serf_CertificateChainValidation(
+ void* pSerfSession,
+ int nFailures,
+ int /*nErrorCode*/,
+ const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
+ apr_size_t nCertificateChainLength)
+{
+ return static_cast<SerfSession*>(pSerfSession)
+ ->verifySerfCertificateChain(nFailures, pCertificateChainBase64Encoded, nCertificateChainLength);
+}
+
+extern "C" apr_status_t Serf_SetupRequest( serf_request_t *request,
+ void *setup_baton,
+ serf_bucket_t **req_bkt,
+ serf_response_acceptor_t *acceptor,
+ void **acceptor_baton,
+ serf_response_handler_t *handler,
+ void **handler_baton,
+ apr_pool_t * pool )
+{
+ SerfRequestProcessor* pReqProc = static_cast< SerfRequestProcessor* >( setup_baton );
+ return pReqProc->setupSerfRequest( request,
+ req_bkt,
+ acceptor,
+ acceptor_baton,
+ handler,
+ handler_baton,
+ pool );
+}
+
+extern "C" serf_bucket_t* Serf_AcceptResponse( serf_request_t *request,
+ serf_bucket_t *stream,
+ void *acceptor_baton,
+ apr_pool_t *pool )
+{
+ SerfRequestProcessor* pReqProc = static_cast< SerfRequestProcessor* >( acceptor_baton );
+ return pReqProc->acceptSerfResponse( request,
+ stream,
+ pool );
+}
+
+extern "C" apr_status_t Serf_HandleResponse( serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *pool )
+{
+ SerfRequestProcessor* pReqProc = static_cast< SerfRequestProcessor* >( handler_baton );
+ return pReqProc->handleSerfResponse( request,
+ response,
+ pool );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfCallbacks.hxx b/ucb/source/ucp/webdav/SerfCallbacks.hxx
new file mode 100644
index 000000000..b82246e68
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfCallbacks.hxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCALLBACKS_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCALLBACKS_HXX
+
+#include <serf.h>
+
+extern "C" apr_status_t Serf_ConnectSetup( apr_socket_t *skt,
+ serf_bucket_t **read_bkt,
+ serf_bucket_t **write_bkt,
+ void *setup_baton,
+ apr_pool_t *pool );
+
+extern "C" apr_status_t Serf_Credentials( char **username,
+ char **password,
+ serf_request_t *request,
+ void *baton,
+ int code,
+ const char *authn_type,
+ const char *realm,
+ apr_pool_t *pool );
+
+extern "C" apr_status_t Serf_CertificateChainValidation(
+ void* pSerfSession,
+ int nFailures,
+ int error_depth,
+ const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
+ apr_size_t nCertificateChainLength);
+
+extern "C" apr_status_t Serf_SetupRequest( serf_request_t *request,
+ void *setup_baton,
+ serf_bucket_t **req_bkt,
+ serf_response_acceptor_t *acceptor,
+ void **acceptor_baton,
+ serf_response_handler_t *handler,
+ void **handler_baton,
+ apr_pool_t * pool );
+
+extern "C" serf_bucket_t* Serf_AcceptResponse( serf_request_t *request,
+ serf_bucket_t *stream,
+ void *acceptor_baton,
+ apr_pool_t *pool );
+
+extern "C" apr_status_t Serf_HandleResponse( serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *pool );
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCALLBACKS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfCopyReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfCopyReqProcImpl.cxx
new file mode 100644
index 000000000..0f9a4ea20
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfCopyReqProcImpl.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfCopyReqProcImpl.hxx"
+
+#include <serf.h>
+
+namespace http_dav_ucp
+{
+
+SerfCopyReqProcImpl::SerfCopyReqProcImpl( const char* inSourcePath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inDestinationPath,
+ const bool inOverwrite )
+ : SerfRequestProcessorImpl( inSourcePath, inRequestHeaders )
+ , mDestPathStr( inDestinationPath )
+ , mbOverwrite( inOverwrite )
+{
+}
+
+SerfCopyReqProcImpl::~SerfCopyReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfCopyReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "COPY",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // COPY specific header fields
+ serf_bucket_headers_set( hdrs_bkt, "Destination", mDestPathStr );
+ if ( mbOverwrite )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Overwrite", "T" );
+ }
+ else
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Overwrite", "F" );
+ }
+
+ return req_bkt;
+}
+
+void SerfCopyReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfCopyReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfCopyReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfCopyReqProcImpl.hxx
new file mode 100644
index 000000000..92d12abb6
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfCopyReqProcImpl.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCOPYREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCOPYREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfCopyReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfCopyReqProcImpl( const char* inSourcePath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inDestinationPath,
+ const bool inOverwrite );
+
+ virtual ~SerfCopyReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const char* mDestPathStr;
+ const bool mbOverwrite;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFCOPYREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.cxx
new file mode 100644
index 000000000..141bb0b47
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfDeleteReqProcImpl.hxx"
+
+#include <serf.h>
+
+namespace http_dav_ucp
+{
+
+SerfDeleteReqProcImpl::SerfDeleteReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+{
+}
+
+SerfDeleteReqProcImpl::~SerfDeleteReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfDeleteReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "DELETE",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ return req_bkt;
+}
+
+void SerfDeleteReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfDeleteReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.hxx
new file mode 100644
index 000000000..0bb8cd60e
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfDeleteReqProcImpl.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFDELETEREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFDELETEREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfDeleteReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfDeleteReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders );
+
+ virtual ~SerfDeleteReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFDELETEREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfGetReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfGetReqProcImpl.cxx
new file mode 100644
index 000000000..c06b94f16
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfGetReqProcImpl.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfGetReqProcImpl.hxx"
+
+using namespace com::sun::star;
+
+namespace http_dav_ucp
+{
+
+SerfGetReqProcImpl::SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const rtl::Reference< SerfInputStream > & xioInStrm )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , xInputStream( xioInStrm )
+ , xOutputStream()
+ , mpHeaderNames( nullptr )
+ , mpResource( nullptr )
+{
+}
+
+SerfGetReqProcImpl::SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const rtl::Reference< SerfInputStream > & xioInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , xInputStream( xioInStrm )
+ , xOutputStream()
+ , mpHeaderNames( &inHeaderNames )
+ , mpResource( &ioResource )
+{
+}
+
+SerfGetReqProcImpl::SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , xInputStream()
+ , xOutputStream( xioOutStrm )
+ , mpHeaderNames( nullptr )
+ , mpResource( nullptr )
+{
+}
+
+SerfGetReqProcImpl::SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , xInputStream()
+ , xOutputStream( xioOutStrm )
+ , mpHeaderNames( &inHeaderNames )
+ , mpResource( &ioResource )
+{
+}
+
+SerfGetReqProcImpl::~SerfGetReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfGetReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "GET",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ return req_bkt;
+}
+
+void SerfGetReqProcImpl::processChunkOfResponseData( const char* data,
+ apr_size_t len )
+{
+ if ( xInputStream.is() )
+ {
+ xInputStream->AddToStream( data, len );
+ }
+ else if ( xOutputStream.is() )
+ {
+ const uno::Sequence< sal_Int8 > aDataSeq( reinterpret_cast<const sal_Int8 *>(data), len );
+ xOutputStream->writeBytes( aDataSeq );
+ }
+}
+
+namespace
+{
+ apr_status_t Serf_ProcessResponseHeader( void* inUserData,
+ const char* inHeaderName,
+ const char* inHeaderValue )
+ {
+ SerfGetReqProcImpl* pReqProcImpl = static_cast< SerfGetReqProcImpl* >( inUserData );
+ pReqProcImpl->processSingleResponseHeader( inHeaderName,
+ inHeaderValue );
+
+ return APR_SUCCESS;
+ }
+} // end of anonymous namespace
+
+void SerfGetReqProcImpl::handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket )
+{
+ // read response header, if requested
+ if ( mpHeaderNames != nullptr && mpResource != nullptr )
+ {
+ serf_bucket_t* SerfHeaderBucket = serf_bucket_response_get_headers( inSerfResponseBucket );
+ if ( SerfHeaderBucket != nullptr )
+ {
+ serf_bucket_headers_do( SerfHeaderBucket,
+ Serf_ProcessResponseHeader,
+ this );
+ }
+ }
+}
+
+void SerfGetReqProcImpl::processSingleResponseHeader( const char* inHeaderName,
+ const char* inHeaderValue )
+{
+ OUString aHeaderName( OUString::createFromAscii( inHeaderName ) );
+
+ bool bStoreHeaderField = false;
+
+ if ( mpHeaderNames->empty() )
+ {
+ // store all header fields
+ bStoreHeaderField = true;
+ }
+ else
+ {
+ // store only header fields which are requested
+ bStoreHeaderField = std::any_of(mpHeaderNames->begin(), mpHeaderNames->end(),
+ [&aHeaderName](const OUString& rHeaderName) {
+ // header names are case insensitive
+ return rHeaderName.equalsIgnoreAsciiCase( aHeaderName );
+ });
+ }
+
+ if ( bStoreHeaderField )
+ {
+ DAVPropertyValue thePropertyValue;
+ thePropertyValue.IsCaseSensitive = false;
+ thePropertyValue.Name = aHeaderName;
+ thePropertyValue.Value <<= OUString::createFromAscii( inHeaderValue );
+ mpResource->properties.push_back( thePropertyValue );
+ }
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfGetReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfGetReqProcImpl.hxx
new file mode 100644
index 000000000..d043f3087
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfGetReqProcImpl.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFGETREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFGETREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "DAVResource.hxx"
+
+#include "SerfInputStream.hxx"
+#include <com/sun/star/io/XOutputStream.hpp>
+
+namespace http_dav_ucp
+{
+
+class SerfGetReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const rtl::Reference< SerfInputStream > & xioInStrm );
+
+ SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const rtl::Reference< SerfInputStream > & xioInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource );
+
+ SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm );
+
+ SerfGetReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource );
+
+ virtual ~SerfGetReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+ void processSingleResponseHeader( const char* inHeaderName,
+ const char* inHeaderValue );
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ rtl::Reference< SerfInputStream > xInputStream;
+ css::uno::Reference< css::io::XOutputStream > xOutputStream;
+ const std::vector< OUString > * mpHeaderNames;
+ DAVResource* mpResource;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFGETREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfHeadReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfHeadReqProcImpl.cxx
new file mode 100644
index 000000000..a771570da
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfHeadReqProcImpl.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfHeadReqProcImpl.hxx"
+
+using namespace com::sun::star;
+
+namespace http_dav_ucp
+{
+
+SerfHeadReqProcImpl::SerfHeadReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mpHeaderNames( &inHeaderNames )
+ , mpResource( &ioResource )
+{
+}
+
+SerfHeadReqProcImpl::~SerfHeadReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfHeadReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "HEAD",
+ getPathStr(),
+
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ return req_bkt;
+}
+
+void SerfHeadReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do
+}
+
+namespace
+{
+ apr_status_t Serf_ProcessResponseHeader( void* inUserData,
+ const char* inHeaderName,
+ const char* inHeaderValue )
+ {
+ SerfHeadReqProcImpl* pReqProcImpl = static_cast< SerfHeadReqProcImpl* >( inUserData );
+ pReqProcImpl->processSingleResponseHeader( inHeaderName,
+ inHeaderValue );
+
+ return APR_SUCCESS;
+ }
+} // end of anonymous namespace
+
+void SerfHeadReqProcImpl::handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket )
+{
+ // read response header, if requested
+ if ( mpHeaderNames != nullptr && mpResource != nullptr )
+ {
+ serf_bucket_t* SerfHeaderBucket = serf_bucket_response_get_headers( inSerfResponseBucket );
+ if ( SerfHeaderBucket != nullptr )
+ {
+ serf_bucket_headers_do( SerfHeaderBucket,
+ Serf_ProcessResponseHeader,
+ this );
+ }
+ }
+}
+
+void SerfHeadReqProcImpl::processSingleResponseHeader( const char* inHeaderName,
+ const char* inHeaderValue )
+{
+ OUString aHeaderName( OUString::createFromAscii( inHeaderName ) );
+
+ bool bStoreHeaderField = false;
+
+ if ( mpHeaderNames->empty() )
+ {
+ // store all header fields
+ bStoreHeaderField = true;
+ }
+ else
+ {
+ // store only header fields which are requested
+ bStoreHeaderField = std::any_of(mpHeaderNames->begin(), mpHeaderNames->end(),
+ [&aHeaderName](const OUString& rHeaderName) {
+ // header names are case insensitive
+ return rHeaderName.equalsIgnoreAsciiCase( aHeaderName );
+ });
+ }
+
+ if ( bStoreHeaderField )
+ {
+ DAVPropertyValue thePropertyValue;
+ thePropertyValue.IsCaseSensitive = false;
+ thePropertyValue.Name = aHeaderName;
+ thePropertyValue.Value <<= OUString::createFromAscii( inHeaderValue );
+ mpResource->properties.push_back( thePropertyValue );
+ }
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfHeadReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfHeadReqProcImpl.hxx
new file mode 100644
index 000000000..b1a03a07e
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfHeadReqProcImpl.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFHEADREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFHEADREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "DAVResource.hxx"
+
+#include "SerfInputStream.hxx"
+#include <com/sun/star/io/XOutputStream.hpp>
+
+namespace http_dav_ucp
+{
+
+class SerfHeadReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfHeadReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource );
+
+ virtual ~SerfHeadReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+ void processSingleResponseHeader( const char* inHeaderName,
+ const char* inHeaderValue );
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const std::vector< OUString > * mpHeaderNames;
+ DAVResource* mpResource;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFHEADREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfInputStream.cxx b/ucb/source/ucp/webdav/SerfInputStream.cxx
new file mode 100644
index 000000000..498ed26e6
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfInputStream.cxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfInputStream.hxx"
+
+#include <cppuhelper/queryinterface.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <string.h>
+
+using namespace cppu;
+using namespace com::sun::star::io;
+using namespace com::sun::star::uno;
+using namespace http_dav_ucp;
+
+// Constructor
+
+SerfInputStream::SerfInputStream()
+: mLen( 0 ),
+ mPos( 0 )
+{
+}
+
+
+// Destructor
+
+SerfInputStream::~SerfInputStream()
+{
+}
+
+
+// AddToStream
+// Allows the caller to add some data to the "end" of the stream
+
+void SerfInputStream::AddToStream( const char * inBuf, sal_Int32 inLen )
+{
+ mInputBuffer.realloc( sal::static_int_cast<sal_Int32>(mLen) + inLen );
+ memcpy( mInputBuffer.getArray() + mLen, inBuf, inLen );
+ mLen += inLen;
+}
+
+
+// queryInterface
+
+Any SerfInputStream::queryInterface( const Type &type )
+{
+ Any aRet = ::cppu::queryInterface( type,
+ static_cast< XInputStream * >( this ),
+ static_cast< XSeekable * >( this ) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( type );
+}
+
+
+// readBytes
+// "Reads" the specified number of bytes from the stream
+
+sal_Int32 SAL_CALL SerfInputStream::readBytes(
+ css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ // Work out how much we're actually going to write
+ sal_Int32 theBytes2Read = nBytesToRead;
+ sal_Int32 theBytesLeft = sal::static_int_cast<sal_Int32>(mLen - mPos);
+ if ( theBytes2Read > theBytesLeft )
+ theBytes2Read = theBytesLeft;
+
+ // Realloc buffer.
+ aData.realloc( theBytes2Read );
+
+ // Write the data
+ memcpy(
+ aData.getArray(), mInputBuffer.getConstArray() + mPos, theBytes2Read );
+
+ // Update our stream position for next time
+ mPos += theBytes2Read;
+
+ return theBytes2Read;
+}
+
+
+// readSomeBytes
+
+sal_Int32 SAL_CALL SerfInputStream::readSomeBytes(
+ css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ // Warning: What should this be doing ?
+ return readBytes( aData, nMaxBytesToRead );
+}
+
+
+// skipBytes
+// Moves the current stream position forward
+
+void SAL_CALL SerfInputStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ mPos += nBytesToSkip;
+ if ( mPos >= mLen )
+ mPos = mLen;
+}
+
+
+// available
+// Returns the number of unread bytes currently remaining on the stream
+
+sal_Int32 SAL_CALL SerfInputStream::available( )
+{
+ return std::min<sal_Int64>(SAL_MAX_INT32, mLen - mPos);
+}
+
+
+// closeInput
+
+void SAL_CALL SerfInputStream::closeInput()
+{
+}
+
+
+// seek
+
+void SAL_CALL SerfInputStream::seek( sal_Int64 location )
+{
+ if ( location < 0 || location > mLen )
+ throw css::lang::IllegalArgumentException();
+ mPos = location;
+}
+
+
+// getPosition
+
+sal_Int64 SAL_CALL SerfInputStream::getPosition()
+{
+ return mPos;
+}
+
+
+// getLength
+
+sal_Int64 SAL_CALL SerfInputStream::getLength()
+{
+ return mLen;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfInputStream.hxx b/ucb/source/ucp/webdav/SerfInputStream.hxx
new file mode 100644
index 000000000..ca9520ed8
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfInputStream.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFINPUTSTREAM_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFINPUTSTREAM_HXX
+
+#include <sal/types.h>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+
+
+namespace http_dav_ucp
+{
+
+
+// SerfInputStream
+// A simple XInputStream implementation provided specifically for use
+// by the DAVSession::GET method.
+
+class SerfInputStream : public css::io::XInputStream,
+ public css::io::XSeekable,
+ public ::cppu::OWeakObject
+{
+ private:
+ css::uno::Sequence< sal_Int8 > mInputBuffer;
+ sal_Int64 mLen;
+ sal_Int64 mPos;
+
+ public:
+ SerfInputStream();
+ virtual ~SerfInputStream() override;
+
+ // Add some data to the end of the stream
+ void AddToStream( const char * inBuf, sal_Int32 inLen );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & type ) override;
+
+ virtual void SAL_CALL acquire()
+ throw () override
+ { OWeakObject::acquire(); }
+
+ virtual void SAL_CALL release()
+ throw() override
+ { OWeakObject::release(); }
+
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes(
+ css::uno::Sequence< sal_Int8 > & aData,
+ sal_Int32 nBytesToRead ) override;
+
+ virtual sal_Int32 SAL_CALL readSomeBytes(
+ css::uno::Sequence< sal_Int8 > & aData,
+ sal_Int32 nMaxBytesToRead ) override;
+
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+
+ virtual sal_Int32 SAL_CALL available() override;
+
+ virtual void SAL_CALL closeInput() override;
+
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+
+ virtual sal_Int64 SAL_CALL getPosition() override;
+
+ virtual sal_Int64 SAL_CALL getLength() override;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFINPUTSTREAM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfLockReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfLockReqProcImpl.cxx
new file mode 100644
index 000000000..713bf5299
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfLockReqProcImpl.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfLockReqProcImpl.hxx"
+
+#include "AprEnv.hxx"
+#include "SerfSession.hxx"
+#include "DAVException.hxx"
+
+#include "webdavresponseparser.hxx"
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+namespace http_dav_ucp
+{
+
+SerfLockReqProcImpl::SerfLockReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ SerfSession& rSession,
+ const css::ucb::Lock& rLock,
+ sal_Int32* plastChanceToSendRefreshRequest )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , m_rSession( rSession )
+ , m_aLock( rLock )
+ , m_plastChanceToSendRefreshRequest( plastChanceToSendRefreshRequest )
+ , m_xInputStream( new SerfInputStream() )
+{
+}
+
+SerfLockReqProcImpl::~SerfLockReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfLockReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ serf_bucket_alloc_t* pSerfBucketAlloc = serf_request_get_alloc( inSerfRequest );
+
+ OStringBuffer aBody("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<lockinfo xmlns='DAV:'>\n <lockscope>");
+
+ // Set the lock scope
+ switch ( m_aLock.Scope )
+ {
+ case css::ucb::LockScope_EXCLUSIVE:
+ aBody.append("<exclusive/>");
+ break;
+ case css::ucb::LockScope_SHARED:
+ aBody.append("<shared/>");
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+ aBody.append("</lockscope>\n <locktype><write/></locktype>\n");
+
+ // Set the lock owner
+ OUString aValue;
+ if ((m_aLock.Owner >>= aValue) && !aValue.isEmpty())
+ {
+ aBody.append(" <owner>");
+ aBody.append(OUStringToOString(aValue, RTL_TEXTENCODING_UTF8));
+ aBody.append("</owner>\n");
+ }
+ aBody.append("</lockinfo>\n");
+
+ const OString aBodyText(aBody.makeStringAndClear());
+ serf_bucket_t* body_bkt = nullptr;
+
+ if (!m_plastChanceToSendRefreshRequest)
+ body_bkt = serf_bucket_simple_copy_create( aBodyText.getStr(),
+ aBodyText.getLength(),
+ pSerfBucketAlloc );
+
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "LOCK",
+ getPathStr(),
+ body_bkt,
+ pSerfBucketAlloc );
+ if (!m_plastChanceToSendRefreshRequest)
+ handleChunkedEncoding(req_bkt, aBodyText.getLength());
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // request specific header fields
+ const char * depth = nullptr;
+ switch( m_aLock.Depth )
+ {
+ case css::ucb::LockDepth_ZERO:
+ depth = "0";
+ break;
+ case css::ucb::LockDepth_ONE:
+ depth = "1";
+ break;
+ case css::ucb::LockDepth_INFINITY:
+ depth = "infinity";
+ break;
+ default:
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+ if (!m_plastChanceToSendRefreshRequest)
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Depth", depth );
+ serf_bucket_headers_set( hdrs_bkt, "Content-Type", "application/xml" );
+ }
+ else
+ {
+ const OString sToken( "(<" + OUStringToOString( apr_environment::AprEnv::getAprEnv()->
+ getSerfLockStore()->getLockToken( OUString::createFromAscii(getPathStr())),
+ RTL_TEXTENCODING_UTF8 ) + ">)" );
+ serf_bucket_headers_set( hdrs_bkt, "If", sToken.getStr() );
+ }
+
+ // Set the lock timeout
+ if (m_aLock.Timeout == -1)
+ serf_bucket_headers_set( hdrs_bkt, "Timeout", "Infinite" );
+ else if (m_aLock.Timeout > 0)
+ {
+ const OString aTimeValue("Second-" + OString::number(m_aLock.Timeout));
+ serf_bucket_headers_set( hdrs_bkt, "Timeout", aTimeValue.getStr() );
+ }
+ else
+ serf_bucket_headers_set( hdrs_bkt, "Timeout", "Second-180" );
+
+ osl_getSystemTime( &m_aStartCall );
+
+ return req_bkt;
+}
+
+void SerfLockReqProcImpl::processChunkOfResponseData( const char* data,
+ apr_size_t len )
+{
+ if ( m_xInputStream.is() )
+ {
+ m_xInputStream->AddToStream( data, len );
+ }
+}
+
+void SerfLockReqProcImpl::handleEndOfResponseData( serf_bucket_t * )
+{
+ const std::vector< css::ucb::Lock > aLocks( parseWebDAVLockResponse( m_xInputStream.get() ) );
+
+ if (!aLocks.empty())
+ {
+ for (size_t i = 0; i < aLocks.size(); ++i)
+ {
+ sal_Int64 timeout = aLocks[i].Timeout;
+ TimeValue aEnd;
+ osl_getSystemTime( &aEnd );
+ // Try to estimate a safe absolute time for sending the
+ // lock refresh request.
+ sal_Int32 lastChanceToSendRefreshRequest = -1;
+ if ( timeout != -1 )
+ {
+ sal_Int32 calltime = aEnd.Seconds - m_aStartCall.Seconds;
+ if ( calltime <= timeout )
+ lastChanceToSendRefreshRequest = aEnd.Seconds + timeout - calltime;
+ else
+ SAL_WARN("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
+ }
+ if (m_plastChanceToSendRefreshRequest)
+ {
+ *m_plastChanceToSendRefreshRequest = lastChanceToSendRefreshRequest;
+ assert(aLocks.size() == 1);
+ // We are just refreshing lock, do not add it into SerfLockStore
+ break;
+ }
+ apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->addLock(
+ OUString::createFromAscii(getPathStr()),
+ aLocks[i].LockTokens[0],
+ &m_rSession, lastChanceToSendRefreshRequest );
+ SAL_INFO("ucb.ucp.webdav", "SerfSession::LOCK: created lock for "
+ << getPathStr() << ". token: " << aLocks[i].LockTokens[0]);
+ }
+ }
+ else
+ {
+ SAL_INFO("ucb.ucp.webdav", "SerfSession::LOCK: obtaining lock failed!");
+ }
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfLockReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfLockReqProcImpl.hxx
new file mode 100644
index 000000000..29d1361eb
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfLockReqProcImpl.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+#include "SerfInputStream.hxx"
+
+#include <com/sun/star/ucb/Lock.hpp>
+#include <osl/time.h>
+
+namespace http_dav_ucp
+{
+
+class SerfSession;
+
+class SerfLockReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfLockReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ SerfSession& rSession,
+ const css::ucb::Lock& rLock,
+ sal_Int32* plastChanceToSendRefreshRequest = nullptr );
+
+ virtual ~SerfLockReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+private:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+ SerfSession& m_rSession;
+ css::ucb::Lock m_aLock;
+ // if m_plastChanceToSendRefreshRequest is not 0 we are sending just refresh request
+ sal_Int32* m_plastChanceToSendRefreshRequest;
+ TimeValue m_aStartCall;
+ rtl::Reference< SerfInputStream > m_xInputStream;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfLockStore.cxx b/ucb/source/ucp/webdav/SerfLockStore.cxx
new file mode 100644
index 000000000..ec2f6ae1b
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfLockStore.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <osl/time.h>
+#include <osl/thread.hxx>
+#include "SerfSession.hxx"
+#include "SerfLockStore.hxx"
+
+using namespace http_dav_ucp;
+
+namespace http_dav_ucp {
+
+class TickerThread : public osl::Thread
+{
+ bool m_bFinish;
+ SerfLockStore & m_rLockStore;
+
+public:
+
+ explicit TickerThread( SerfLockStore & rLockStore )
+ : osl::Thread(), m_bFinish( false ), m_rLockStore( rLockStore ) {}
+
+ void finish() { m_bFinish = true; }
+
+protected:
+
+ virtual void SAL_CALL run() override;
+};
+
+} // namespace http_dav_ucp
+
+
+void TickerThread::run()
+{
+ osl_setThreadName("http_dav_ucp::TickerThread");
+
+ SAL_INFO("ucb.ucp.webdav", "TickerThread: start." );
+
+ // we have to go through the loop more often to be able to finish ~quickly
+ const int nNth = 25;
+
+ int nCount = nNth;
+ while ( !m_bFinish )
+ {
+ if ( nCount-- <= 0 )
+ {
+ m_rLockStore.refreshLocks();
+ nCount = nNth;
+ }
+
+ TimeValue aTV;
+ aTV.Seconds = 0;
+ aTV.Nanosec = 1000000000 / nNth;
+ wait( aTV );
+ }
+
+ SAL_INFO("ucb.ucp.webdav", "TickerThread: stop." );
+}
+
+
+SerfLockStore::SerfLockStore()
+ : m_pTickerThread( nullptr )
+ , m_bFinishing( false )
+{
+}
+
+
+SerfLockStore::~SerfLockStore()
+{
+ stopTicker();
+ m_bFinishing = true;
+
+ // release active locks, if any.
+ SAL_WARN_IF( !m_aLockInfoMap.empty(), "ucb.ucp.webdav",
+ "SerfLockStore::~SerfLockStore - Releasing active locks!" );
+
+ for ( auto& rLockInfo : m_aLockInfoMap )
+ {
+ rLockInfo.second.m_xSession->UNLOCK( rLockInfo.first );
+ }
+}
+
+bool SerfLockStore::finishing() const
+{
+ return m_bFinishing;
+}
+
+void SerfLockStore::startTicker()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !m_pTickerThread )
+ {
+ m_pTickerThread = new TickerThread( *this );
+ m_pTickerThread->create();
+ }
+}
+
+
+void SerfLockStore::stopTicker()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pTickerThread )
+ {
+ m_pTickerThread->finish();
+ m_pTickerThread->join();
+ delete m_pTickerThread;
+ m_pTickerThread = nullptr;
+ }
+}
+
+OUString SerfLockStore::getLockToken( const OUString& rLock )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ LockInfoMap::const_iterator it( m_aLockInfoMap.find( rLock ) );
+ if ( it != m_aLockInfoMap.end() )
+ return (*it).second.m_sToken;
+
+ SAL_WARN("ucb.ucp.webdav", "SerfLockStore::getLockToken: lock not found!" );
+ return OUString();
+}
+
+void SerfLockStore::addLock( const OUString& rLock,
+ const OUString& sToken,
+ rtl::Reference< SerfSession > const & xSession,
+ sal_Int32 nLastChanceToSendRefreshRequest )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ m_aLockInfoMap[ rLock ]
+ = LockInfo( sToken, xSession, nLastChanceToSendRefreshRequest );
+
+ startTicker();
+}
+
+
+void SerfLockStore::updateLock( const OUString& rLock,
+ sal_Int32 nLastChanceToSendRefreshRequest )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ LockInfoMap::iterator it( m_aLockInfoMap.find( rLock ) );
+ SAL_WARN_IF( it == m_aLockInfoMap.end(), "ucb.ucp.webdav",
+ "SerfLockStore::updateLock: lock not found!" );
+
+ if ( it != m_aLockInfoMap.end() )
+ {
+ (*it).second.m_nLastChanceToSendRefreshRequest
+ = nLastChanceToSendRefreshRequest;
+ }
+}
+
+
+void SerfLockStore::removeLock( const OUString& rLock )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ m_aLockInfoMap.erase( rLock );
+
+ if ( m_aLockInfoMap.empty() )
+ stopTicker();
+}
+
+
+void SerfLockStore::refreshLocks()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ for ( auto& rLockInfo : m_aLockInfoMap )
+ {
+ LockInfo & rInfo = rLockInfo.second;
+ if ( rInfo.m_nLastChanceToSendRefreshRequest != -1 )
+ {
+ // 30 seconds or less remaining until lock expires?
+ TimeValue t1;
+ osl_getSystemTime( &t1 );
+ if ( rInfo.m_nLastChanceToSendRefreshRequest - 30
+ <= sal_Int32( t1.Seconds ) )
+ {
+ // refresh the lock.
+ sal_Int32 nlastChanceToSendRefreshRequest = -1;
+ if ( rInfo.m_xSession->LOCK(
+ rLockInfo.first, &nlastChanceToSendRefreshRequest ) )
+ {
+ rInfo.m_nLastChanceToSendRefreshRequest
+ = nlastChanceToSendRefreshRequest;
+ }
+ else
+ {
+ // refresh failed. stop auto-refresh.
+ rInfo.m_nLastChanceToSendRefreshRequest = -1;
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfLockStore.hxx b/ucb/source/ucp/webdav/SerfLockStore.hxx
new file mode 100644
index 000000000..6a927562b
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfLockStore.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKSTORE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKSTORE_HXX
+
+#include <map>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include "SerfSession.hxx"
+
+namespace http_dav_ucp
+{
+
+class TickerThread;
+
+struct LockInfo
+{
+ OUString m_sToken;
+ rtl::Reference< SerfSession > m_xSession;
+ sal_Int32 m_nLastChanceToSendRefreshRequest;
+
+ LockInfo()
+ : m_nLastChanceToSendRefreshRequest( -1 ) {}
+
+ LockInfo( const OUString& sToken,
+ rtl::Reference< SerfSession > const & xSession,
+ sal_Int32 nLastChanceToSendRefreshRequest )
+ : m_sToken( sToken ),
+ m_xSession( xSession ),
+ m_nLastChanceToSendRefreshRequest( nLastChanceToSendRefreshRequest ) {}
+};
+
+typedef std::map< OUString, LockInfo > LockInfoMap;
+
+class SerfLockStore
+{
+ osl::Mutex m_aMutex;
+ TickerThread * m_pTickerThread;
+ bool m_bFinishing;
+ LockInfoMap m_aLockInfoMap;
+
+public:
+ SerfLockStore();
+ ~SerfLockStore();
+
+ bool finishing() const;
+ OUString getLockToken( const OUString& rLock );
+
+ void addLock( const OUString& rLock,
+ const OUString& sToken,
+ rtl::Reference< SerfSession > const & xSession,
+ // time in seconds since Jan 1 1970
+ // -1: infinite lock, no refresh
+ sal_Int32 nLastChanceToSendRefreshRequest );
+
+ void updateLock( const OUString& rLock,
+ sal_Int32 nLastChanceToSendRefreshRequest );
+
+ void removeLock( const OUString& rLock );
+
+ void refreshLocks();
+
+private:
+ void startTicker();
+ void stopTicker();
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFLOCKSTORE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfMkColReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfMkColReqProcImpl.cxx
new file mode 100644
index 000000000..4641b3f35
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfMkColReqProcImpl.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfMkColReqProcImpl.hxx"
+
+#include <serf.h>
+
+namespace http_dav_ucp
+{
+
+SerfMkColReqProcImpl::SerfMkColReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders )
+ : SerfRequestProcessorImpl( inPath,inRequestHeaders )
+{
+}
+
+SerfMkColReqProcImpl::~SerfMkColReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfMkColReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "MKCOL",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ return req_bkt;
+}
+
+void SerfMkColReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfMkColReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfMkColReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfMkColReqProcImpl.hxx
new file mode 100644
index 000000000..08269ef47
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfMkColReqProcImpl.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMKCOLREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMKCOLREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfMkColReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfMkColReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders );
+
+ virtual ~SerfMkColReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMKCOLREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfMoveReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfMoveReqProcImpl.cxx
new file mode 100644
index 000000000..fc775abd3
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfMoveReqProcImpl.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfMoveReqProcImpl.hxx"
+
+#include <serf.h>
+
+namespace http_dav_ucp
+{
+
+SerfMoveReqProcImpl::SerfMoveReqProcImpl( const char* inSourcePath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inDestinationPath,
+ const bool inOverwrite )
+ : SerfRequestProcessorImpl( inSourcePath, inRequestHeaders )
+ , mDestPathStr( inDestinationPath )
+ , mbOverwrite( inOverwrite )
+{
+}
+
+SerfMoveReqProcImpl::~SerfMoveReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfMoveReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "MOVE",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // MOVE specific header fields
+ serf_bucket_headers_set( hdrs_bkt, "Destination", mDestPathStr );
+ if ( mbOverwrite )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Overwrite", "T" );
+ }
+ else
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Overwrite", "F" );
+ }
+
+ return req_bkt;
+}
+
+void SerfMoveReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfMoveReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfMoveReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfMoveReqProcImpl.hxx
new file mode 100644
index 000000000..73a8574b4
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfMoveReqProcImpl.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMOVEREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMOVEREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfMoveReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfMoveReqProcImpl( const char* inSourcePath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inDestinationPath,
+ const bool inOverwrite );
+
+ virtual ~SerfMoveReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const char* mDestPathStr;
+ const bool mbOverwrite;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFMOVEREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPostReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfPostReqProcImpl.cxx
new file mode 100644
index 000000000..bbef1970e
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPostReqProcImpl.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfPostReqProcImpl.hxx"
+
+#include <serf.h>
+
+using namespace com::sun::star;
+
+namespace http_dav_ucp
+{
+
+SerfPostReqProcImpl::SerfPostReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const char* inContentType,
+ const char* inReferer,
+ const rtl::Reference< SerfInputStream > & xioInStrm )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mpPostData( inData )
+ , mnPostDataLen( inDataLen )
+ , mpContentType( inContentType )
+ , mpReferer( inReferer )
+ , xInputStream( xioInStrm )
+ , xOutputStream()
+{
+}
+
+SerfPostReqProcImpl::SerfPostReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const char* inContentType,
+ const char* inReferer,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mpPostData( inData )
+ , mnPostDataLen( inDataLen )
+ , mpContentType( inContentType )
+ , mpReferer( inReferer )
+ , xInputStream()
+ , xOutputStream( xioOutStrm )
+{
+}
+
+SerfPostReqProcImpl::~SerfPostReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfPostReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ serf_bucket_alloc_t* pSerfBucketAlloc = serf_request_get_alloc( inSerfRequest );
+
+ // create body bucket
+ serf_bucket_t* body_bkt = nullptr;
+ if ( mpPostData != nullptr && mnPostDataLen > 0 )
+ {
+ body_bkt = SERF_BUCKET_SIMPLE_STRING_LEN( mpPostData, mnPostDataLen, pSerfBucketAlloc );
+ }
+
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "POST",
+ getPathStr(),
+ body_bkt,
+ serf_request_get_alloc( inSerfRequest ) );
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ handleChunkedEncoding(req_bkt, mnPostDataLen);
+
+ // request specific header fields
+ if ( mpContentType != nullptr )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Content-Type", mpContentType );
+ }
+ if ( mpReferer != nullptr )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Referer", mpReferer );
+ }
+
+ return req_bkt;
+}
+
+void SerfPostReqProcImpl::processChunkOfResponseData( const char* data,
+ apr_size_t len )
+{
+ if ( xInputStream.is() )
+ {
+ xInputStream->AddToStream( data, len );
+ }
+ else if ( xOutputStream.is() )
+ {
+ const uno::Sequence< sal_Int8 > aDataSeq( reinterpret_cast<const sal_Int8 *>(data), len );
+ xOutputStream->writeBytes( aDataSeq );
+ }
+}
+
+void SerfPostReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPostReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfPostReqProcImpl.hxx
new file mode 100644
index 000000000..aebd60b36
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPostReqProcImpl.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPOSTREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPOSTREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+#include "SerfInputStream.hxx"
+#include <com/sun/star/io/XOutputStream.hpp>
+
+namespace http_dav_ucp
+{
+
+class SerfPostReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfPostReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const char* inContentType,
+ const char* inReferer,
+ const rtl::Reference< SerfInputStream > & xioInStrm );
+
+ SerfPostReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const char* inContentType,
+ const char* inReferer,
+ const css::uno::Reference< css::io::XOutputStream > & xioOutStrm );
+
+ virtual ~SerfPostReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const char* mpPostData;
+ apr_size_t mnPostDataLen;
+ const char* mpContentType;
+ const char* mpReferer;
+ rtl::Reference< SerfInputStream > xInputStream;
+ css::uno::Reference< css::io::XOutputStream > xOutputStream;
+
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPOSTREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.cxx
new file mode 100644
index 000000000..39a471fee
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.cxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfPropFindReqProcImpl.hxx"
+#include "DAVProperties.hxx"
+
+#include "webdavresponseparser.hxx"
+#include <rtl/strbuf.hxx>
+
+
+using namespace com::sun::star;
+
+namespace http_dav_ucp
+{
+
+SerfPropFindReqProcImpl::SerfPropFindReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mDepthStr( nullptr )
+ , mpPropNames( &inPropNames )
+ , mpResources( &ioResources )
+ , mpResInfo( nullptr )
+ , mbOnlyPropertyNames( false )
+ , xInputStream( new SerfInputStream() )
+{
+ init( inDepth );
+}
+
+SerfPropFindReqProcImpl::SerfPropFindReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mDepthStr( nullptr )
+ , mpPropNames( nullptr )
+ , mpResources( nullptr )
+ , mpResInfo( &ioResInfo )
+ , mbOnlyPropertyNames( true )
+ , xInputStream( new SerfInputStream() )
+{
+ init( inDepth );
+}
+
+void SerfPropFindReqProcImpl::init( const Depth inDepth )
+{
+ switch ( inDepth )
+ {
+ case DAVZERO:
+ mDepthStr = "0";
+ break;
+ case DAVONE:
+ mDepthStr = "1";
+ break;
+ case DAVINFINITY:
+ mDepthStr = "infinity";
+ break;
+ }
+}
+
+SerfPropFindReqProcImpl::~SerfPropFindReqProcImpl()
+{
+}
+
+#define PROPFIND_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\">"
+#define PROPFIND_TRAILER "</propfind>"
+
+serf_bucket_t * SerfPropFindReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ serf_bucket_alloc_t* pSerfBucketAlloc = serf_request_get_alloc( inSerfRequest );
+
+ // body bucket - certain properties OR all properties OR only property names
+ serf_bucket_t* body_bkt = nullptr;
+ OString aBodyText;
+ {
+ OStringBuffer aBuffer;
+ aBuffer.append( PROPFIND_HEADER );
+
+ // create and fill body bucket with requested properties
+ const int nPropCount = ( !mbOnlyPropertyNames && mpPropNames )
+ ? mpPropNames->size()
+ : 0;
+ if ( nPropCount > 0 )
+ {
+ aBuffer.append( "<prop>" );
+ SerfPropName thePropName;
+ for ( int theIndex = 0; theIndex < nPropCount; theIndex ++ )
+ {
+ // split fullname into namespace and name!
+ DAVProperties::createSerfPropName( (*mpPropNames)[ theIndex ],
+ thePropName );
+
+ /* <*propname* xmlns="*propns*" /> */
+ aBuffer.append( "<" );
+ aBuffer.append( thePropName.name );
+ aBuffer.append( " xmlns=\"" );
+ aBuffer.append( thePropName.nspace );
+ aBuffer.append( "\"/>" );
+ }
+
+ aBuffer.append( "</prop>" );
+ }
+ else
+ {
+ if ( mbOnlyPropertyNames )
+ {
+ aBuffer.append( "<propname/>" );
+ }
+ else
+ {
+ aBuffer.append( "<allprop/>" );
+ }
+ }
+
+ aBuffer.append( PROPFIND_TRAILER );
+ aBodyText = aBuffer.makeStringAndClear();
+ body_bkt = serf_bucket_simple_copy_create( aBodyText.getStr(),
+ aBodyText.getLength(),
+ pSerfBucketAlloc );
+ }
+
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "PROPFIND",
+ getPathStr(),
+ body_bkt,
+ pSerfBucketAlloc );
+ handleChunkedEncoding(req_bkt, aBodyText.getLength());
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ if (hdrs_bkt != nullptr)
+ {
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // request specific header fields
+ serf_bucket_headers_set( hdrs_bkt, "Depth", mDepthStr );
+ if (hdrs_bkt!=nullptr && body_bkt != nullptr && aBodyText.getLength() > 0 )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Content-Type", "application/xml" );
+ }
+ }
+ else
+ {
+ assert(!"Headers Bucket missing");
+ }
+
+ return req_bkt;
+}
+
+void SerfPropFindReqProcImpl::processChunkOfResponseData( const char* data,
+ apr_size_t len )
+{
+ if ( xInputStream.is() )
+ {
+ xInputStream->AddToStream( data, len );
+ }
+}
+
+void SerfPropFindReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ if ( mbOnlyPropertyNames )
+ {
+ const std::vector< DAVResourceInfo > rResInfo( parseWebDAVPropNameResponse( xInputStream.get() ) );
+ *mpResInfo = rResInfo;
+ }
+ else
+ {
+ const std::vector< DAVResource > rResources( parseWebDAVPropFindResponse( xInputStream.get() ) );
+ *mpResources = rResources;
+ }
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.hxx
new file mode 100644
index 000000000..2acabda00
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPropFindReqProcImpl.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPFINDREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPFINDREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "DAVTypes.hxx"
+#include "DAVResource.hxx"
+
+#include "SerfInputStream.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfPropFindReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfPropFindReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources );
+
+ SerfPropFindReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo );
+
+ virtual ~SerfPropFindReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ void init( const Depth inDepth );
+
+ const char* mDepthStr;
+ const std::vector< OUString > * mpPropNames;
+ std::vector< DAVResource > * mpResources;
+ std::vector< DAVResourceInfo > * mpResInfo;
+
+ const bool mbOnlyPropertyNames;
+ rtl::Reference< SerfInputStream > xInputStream;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPFINDREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.cxx
new file mode 100644
index 000000000..4540ccbd0
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "DAVProperties.hxx"
+#include "UCBDeadPropertyValue.hxx"
+
+#include "SerfPropPatchReqProcImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+SerfPropPatchReqProcImpl::SerfPropPatchReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const std::vector< ProppatchValue > & inProperties )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mpProperties( &inProperties )
+{
+}
+
+SerfPropPatchReqProcImpl::~SerfPropPatchReqProcImpl()
+{
+}
+
+#define PROPPATCH_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?><propertyupdate xmlns=\"DAV:\">"
+#define PROPPATCH_TRAILER "</propertyupdate>"
+
+serf_bucket_t * SerfPropPatchReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ serf_bucket_alloc_t* pSerfBucketAlloc = serf_request_get_alloc( inSerfRequest );
+
+ // body bucket
+ serf_bucket_t* body_bkt = nullptr;
+ OString aBodyText;
+ {
+ // create and fill body bucket with properties to be set or removed
+ static const struct
+ {
+ const char *str;
+ sal_Int32 len;
+ }
+ OpCode [] = {
+ { RTL_CONSTASCII_STRINGPARAM( "set" ) },
+ { RTL_CONSTASCII_STRINGPARAM( "remove" ) }
+ };
+ const int nPropCount = ( mpProperties != nullptr )
+ ? mpProperties->size()
+ : 0;
+ if ( nPropCount > 0 )
+ {
+ OUStringBuffer aBuffer;
+ // add PropPatch xml header in front
+ aBuffer.append( PROPPATCH_HEADER );
+
+ // <*operation code*><prop>
+
+ ProppatchOperation lastOp = (*mpProperties)[ 0 ].operation;
+ aBuffer.append( "<" );
+ aBuffer.appendAscii( OpCode[lastOp].str, OpCode[lastOp].len );
+ aBuffer.append( "><prop>" );
+
+ SerfPropName thePropName;
+ for ( int n = 0; n < nPropCount; ++n )
+ {
+ const ProppatchValue & rProperty = (*mpProperties)[ n ];
+ // split fullname into namespace and name!
+ DAVProperties::createSerfPropName( rProperty.name,
+ thePropName );
+
+ if ( rProperty.operation != lastOp )
+ {
+ // </prop></*last operation code*><*operation code><prop>
+ aBuffer.append( "</prop></" );
+ aBuffer.appendAscii( OpCode[lastOp].str, OpCode[lastOp].len );
+ aBuffer.append( "><" );
+ aBuffer.appendAscii( OpCode[rProperty.operation].str, OpCode[rProperty.operation].len );
+ aBuffer.append( "><prop>" );
+ }
+
+ // <*propname* xmlns="*propns*"
+ aBuffer.append( "<" );
+ aBuffer.appendAscii( thePropName.name );
+ aBuffer.append( " xmlns=\"" );
+ aBuffer.appendAscii( thePropName.nspace );
+ aBuffer.append( "\"" );
+
+ if ( rProperty.operation == PROPSET )
+ {
+ // >*property value*</*propname*>
+ aBuffer.append( ">" );
+
+ OUString aStringValue;
+ if ( DAVProperties::isUCBDeadProperty( thePropName ) )
+ {
+ UCBDeadPropertyValue::toXML( rProperty.value,
+ aStringValue );
+ }
+ else
+ {
+ rProperty.value >>= aStringValue;
+ }
+ aBuffer.append( aStringValue );
+ aBuffer.append( "</" );
+ aBuffer.appendAscii( thePropName.name );
+ aBuffer.append( ">" );
+ }
+ else
+ {
+ aBuffer.append( "/>" );
+ }
+
+ lastOp = rProperty.operation;
+ }
+
+ // </prop></*last operation code*>
+ aBuffer.append( "</prop></" );
+ aBuffer.appendAscii( OpCode[lastOp].str, OpCode[lastOp].len );
+ aBuffer.append( ">" );
+
+ // add PropPatch xml trailer at end
+ aBuffer.append( PROPPATCH_TRAILER );
+
+ aBodyText = OUStringToOString( aBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
+ body_bkt = serf_bucket_simple_copy_create( aBodyText.getStr(),
+ aBodyText.getLength(),
+ pSerfBucketAlloc );
+ }
+ }
+
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "PROPPATCH",
+ getPathStr(),
+ body_bkt,
+ pSerfBucketAlloc ) ;
+ handleChunkedEncoding(req_bkt, aBodyText.getLength());
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ if (hdrs_bkt != nullptr)
+ {
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // request specific header fields
+ if ( body_bkt != nullptr && aBodyText.getLength() > 0 )
+ {
+ serf_bucket_headers_set( hdrs_bkt, "Content-Type", "application/xml" );
+ }
+ }
+ else
+ {
+ assert(!"Headers Bucket missing");
+ }
+
+ return req_bkt;
+}
+
+void SerfPropPatchReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfPropPatchReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.hxx
new file mode 100644
index 000000000..330e0d953
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPropPatchReqProcImpl.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPPATCHREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPPATCHREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+#include <vector>
+#include "DAVTypes.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfPropPatchReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfPropPatchReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const std::vector< ProppatchValue > & inProperties );
+
+ virtual ~SerfPropPatchReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const std::vector< ProppatchValue > * mpProperties;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPROPPATCHREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPutReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfPutReqProcImpl.cxx
new file mode 100644
index 000000000..466f7a10c
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPutReqProcImpl.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+
+#include "SerfPutReqProcImpl.hxx"
+
+#include <serf.h>
+
+namespace http_dav_ucp
+{
+
+SerfPutReqProcImpl::SerfPutReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const OUString& sToken )
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , mpData( inData )
+ , mnDataLen( inDataLen )
+ , msToken( sToken )
+{
+}
+
+SerfPutReqProcImpl::~SerfPutReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfPutReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ serf_bucket_alloc_t* pSerfBucketAlloc = serf_request_get_alloc( inSerfRequest );
+
+ // create body bucket
+ serf_bucket_t* body_bkt = nullptr;
+ if ( mpData != nullptr && mnDataLen > 0 )
+ {
+ body_bkt = SERF_BUCKET_SIMPLE_STRING_LEN( mpData, mnDataLen, pSerfBucketAlloc );
+ }
+
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "PUT",
+ getPathStr(),
+ body_bkt,
+ serf_request_get_alloc( inSerfRequest ) );
+ handleChunkedEncoding(req_bkt, mnDataLen);
+
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // 'If' header with token, so that we can save document locked by us
+ const OString sIfHeader( "<" + rtl::OStringView(getPathStr()) + "> (<" + OUStringToOString(
+ msToken, RTL_TEXTENCODING_UTF8) + ">)" );
+ serf_bucket_headers_set( hdrs_bkt, "If", sIfHeader.getStr() );
+
+ return req_bkt;
+}
+
+void SerfPutReqProcImpl::processChunkOfResponseData( const char* /*data*/,
+ apr_size_t /*len*/ )
+{
+ // nothing to do;
+}
+
+void SerfPutReqProcImpl::handleEndOfResponseData( serf_bucket_t * /*inSerfResponseBucket*/ )
+{
+ // nothing to do;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfPutReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfPutReqProcImpl.hxx
new file mode 100644
index 000000000..8eeb791ee
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfPutReqProcImpl.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPUTREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPUTREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfPutReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfPutReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const char* inData,
+ apr_size_t inDataLen,
+ const OUString& sToken );
+
+
+ virtual ~SerfPutReqProcImpl() override;
+
+ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) override;
+
+protected:
+ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) override;
+
+ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) override;
+
+private:
+ const char* mpData;
+ apr_size_t mnDataLen;
+ OUString msToken;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFPUTREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfRequestProcessor.cxx b/ucb/source/ucp/webdav/SerfRequestProcessor.cxx
new file mode 100644
index 000000000..21cc15ec0
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfRequestProcessor.cxx
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfRequestProcessor.hxx"
+
+#include "AprEnv.hxx"
+#include "SerfCallbacks.hxx"
+#include "SerfSession.hxx"
+#include "SerfPropFindReqProcImpl.hxx"
+#include "SerfPropPatchReqProcImpl.hxx"
+#include "SerfGetReqProcImpl.hxx"
+#include "SerfHeadReqProcImpl.hxx"
+#include "SerfPutReqProcImpl.hxx"
+#include "SerfPostReqProcImpl.hxx"
+#include "SerfDeleteReqProcImpl.hxx"
+#include "SerfMkColReqProcImpl.hxx"
+#include "SerfCopyReqProcImpl.hxx"
+#include "SerfMoveReqProcImpl.hxx"
+#include "SerfLockReqProcImpl.hxx"
+#include "SerfUnlockReqProcImpl.hxx"
+
+#include <apr_strings.h>
+
+namespace http_dav_ucp
+{
+
+SerfRequestProcessor::SerfRequestProcessor( SerfSession& rSerfSession,
+ const OUString & inPath,
+ const bool bUseChunkedEncoding )
+ : mrSerfSession( rSerfSession )
+ , mPathStr( nullptr )
+ , mbUseChunkedEncoding( bUseChunkedEncoding )
+ , mDestPathStr( nullptr )
+ , mContentType( nullptr )
+ , mReferer( nullptr )
+ , mpProcImpl( nullptr )
+ , mbProcessingDone( false )
+ , mpDAVException()
+ , mnHTTPStatusCode( SC_NONE )
+ , mHTTPStatusCodeText()
+ , mRedirectLocation()
+ , mnSuccessfulCredentialAttempts( 0 )
+ , mbInputOfCredentialsAborted( false )
+ , mbSetupSerfRequestCalled( false )
+ , mbAcceptSerfResponseCalled( false )
+ , mbHandleSerfResponseCalled( false )
+{
+ mPathStr = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+}
+
+SerfRequestProcessor::~SerfRequestProcessor()
+{
+ delete mpProcImpl;
+ delete mpDAVException;
+}
+
+void SerfRequestProcessor::prepareProcessor()
+{
+ delete mpDAVException;
+ mpDAVException = nullptr;
+ mnHTTPStatusCode = SC_NONE;
+ mHTTPStatusCodeText.clear();
+ mRedirectLocation.clear();
+
+ mnSuccessfulCredentialAttempts = 0;
+ mbInputOfCredentialsAborted = false;
+ mbSetupSerfRequestCalled = false;
+ mbAcceptSerfResponseCalled = false;
+ mbHandleSerfResponseCalled = false;
+}
+
+// PROPFIND - allprop & named
+bool SerfRequestProcessor::processPropFind( const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfPropFindReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inDepth,
+ inPropNames,
+ ioResources );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// PROPFIND - property names
+bool SerfRequestProcessor::processPropFind( const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfPropFindReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inDepth,
+ ioResInfo );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// PROPPATCH
+bool SerfRequestProcessor::processPropPatch( const std::vector< ProppatchValue > & inProperties,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfPropPatchReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inProperties );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// GET
+bool SerfRequestProcessor::processGet( const rtl::Reference< SerfInputStream >& xioInStrm,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfGetReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ xioInStrm );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// GET inclusive header fields
+bool SerfRequestProcessor::processGet( const rtl::Reference< SerfInputStream >& xioInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfGetReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ xioInStrm,
+ inHeaderNames,
+ ioResource );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// GET
+bool SerfRequestProcessor::processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfGetReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ xioOutStrm );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// GET inclusive header fields
+bool SerfRequestProcessor::processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfGetReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ xioOutStrm,
+ inHeaderNames,
+ ioResource );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// HEAD
+bool SerfRequestProcessor::processHead( const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfHeadReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inHeaderNames,
+ ioResource );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// PUT
+bool SerfRequestProcessor::processPut( const char* inData,
+ apr_size_t inDataLen,
+ apr_status_t& outSerfStatus )
+{
+ // get the lock from lock store
+ const OUString sToken(
+ apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->getLockToken(
+ OUString::createFromAscii(mPathStr)) );
+
+ mpProcImpl = new SerfPutReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inData,
+ inDataLen,
+ sToken );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// POST
+bool SerfRequestProcessor::processPost( const char* inData,
+ apr_size_t inDataLen,
+ const OUString & inContentType,
+ const OUString & inReferer,
+ const rtl::Reference< SerfInputStream >& xioInStrm,
+ apr_status_t& outSerfStatus )
+{
+ mContentType = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mReferer = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mpProcImpl = new SerfPostReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inData,
+ inDataLen,
+ mContentType,
+ mReferer,
+ xioInStrm );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// POST
+bool SerfRequestProcessor::processPost( const char* inData,
+ apr_size_t inDataLen,
+ const OUString & inContentType,
+ const OUString & inReferer,
+ const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ apr_status_t& outSerfStatus )
+{
+ mContentType = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inContentType, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mReferer = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inReferer, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mpProcImpl = new SerfPostReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ inData,
+ inDataLen,
+ mContentType,
+ mReferer,
+ xioOutStrm );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// DELETE
+bool SerfRequestProcessor::processDelete( apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfDeleteReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// MKCOL
+bool SerfRequestProcessor::processMkCol( apr_status_t& outSerfStatus )
+{
+ mpProcImpl = new SerfMkColReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// COPY
+bool SerfRequestProcessor::processCopy( const OUString & inDestinationPath,
+ const bool inOverwrite,
+ apr_status_t& outSerfStatus )
+{
+ mDestPathStr = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mpProcImpl = new SerfCopyReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ mDestPathStr,
+ inOverwrite );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+// MOVE
+bool SerfRequestProcessor::processMove( const OUString & inDestinationPath,
+ const bool inOverwrite,
+ apr_status_t& outSerfStatus )
+{
+ mDestPathStr = apr_pstrdup( SerfSession::getAprPool(),
+ OUStringToOString( inDestinationPath, RTL_TEXTENCODING_UTF8 ).getStr() );
+ mpProcImpl = new SerfMoveReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ mDestPathStr,
+ inOverwrite );
+ outSerfStatus = runProcessor();
+
+ return outSerfStatus == APR_SUCCESS;
+}
+
+
+bool SerfRequestProcessor::processLock( const css::ucb::Lock & rLock, sal_Int32 *plastChanceToSendRefreshRequest )
+{
+ mpProcImpl = new SerfLockReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ mrSerfSession,
+ rLock,
+ plastChanceToSendRefreshRequest );
+
+ return runProcessor() == APR_SUCCESS;
+}
+
+bool SerfRequestProcessor::processUnlock()
+{
+ // get the lock from lock store
+ const OUString sToken(
+ apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->getLockToken(
+ OUString::createFromAscii(mPathStr)) );
+ if ( sToken.isEmpty() )
+ throw DAVException( DAVException::DAV_NOT_LOCKED );
+
+ mpProcImpl = new SerfUnlockReqProcImpl( mPathStr,
+ mrSerfSession.getRequestEnvironment().m_aRequestHeaders,
+ sToken );
+
+ return runProcessor() == APR_SUCCESS;
+}
+
+apr_status_t SerfRequestProcessor::runProcessor()
+{
+ prepareProcessor();
+
+ // activate chunked encoding, if requested
+ if ( mbUseChunkedEncoding )
+ {
+ mpProcImpl->activateChunkedEncoding();
+ }
+
+ // create serf request
+ serf_connection_request_create( mrSerfSession.getSerfConnection(),
+ Serf_SetupRequest,
+ this );
+
+ // perform serf request
+ mbProcessingDone = false;
+ apr_status_t status = APR_SUCCESS;
+ serf_context_t* pSerfContext = mrSerfSession.getSerfContext();
+ apr_pool_t* pAprPool = SerfSession::getAprPool();
+ while ( true )
+ {
+ status = serf_context_run( pSerfContext,
+ SERF_DURATION_FOREVER,
+ pAprPool );
+ if ( APR_STATUS_IS_TIMEUP( status ) )
+ {
+ continue;
+ }
+ if ( status != APR_SUCCESS )
+ {
+ break;
+ }
+ if ( mbProcessingDone )
+ {
+ break;
+ }
+ }
+
+ postprocessProcessor( status );
+
+ return status;
+}
+
+void SerfRequestProcessor::postprocessProcessor( const apr_status_t inStatus )
+{
+ if ( inStatus == APR_SUCCESS )
+ {
+ return;
+ }
+
+ switch ( inStatus )
+ {
+ case APR_EGENERAL:
+ case SERF_ERROR_AUTHN_FAILED:
+ // general error; <mnHTTPStatusCode> provides more information
+ {
+ switch ( mnHTTPStatusCode )
+ {
+ case SC_NONE:
+ if ( !mbSetupSerfRequestCalled )
+ {
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_LOOKUP,
+ SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
+ mrSerfSession.getPort() ) );
+ }
+ else if ( mbInputOfCredentialsAborted )
+ {
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_NOAUTH,
+ SerfUri::makeConnectionEndPointString( mrSerfSession.getHostName(),
+ mrSerfSession.getPort() ) );
+ }
+ else
+ {
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
+ mHTTPStatusCodeText,
+ mnHTTPStatusCode );
+ }
+ break;
+ case SC_MOVED_PERMANENTLY:
+ case SC_MOVED_TEMPORARILY:
+ case SC_SEE_OTHER:
+ case SC_TEMPORARY_REDIRECT:
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_REDIRECT,
+ mRedirectLocation );
+ break;
+ default:
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR,
+ mHTTPStatusCodeText,
+ mnHTTPStatusCode );
+ break;
+ }
+ }
+ break;
+
+ default:
+ mpDAVException = new DAVException( DAVException::DAV_HTTP_ERROR );
+ break;
+ }
+
+}
+
+apr_status_t SerfRequestProcessor::provideSerfCredentials( char ** outUsername,
+ char ** outPassword,
+ serf_request_t * inRequest,
+ int inCode,
+ const char *inAuthProtocol,
+ const char *inRealm,
+ apr_pool_t *inAprPool )
+{
+ // as each successful provided credentials are tried twice - see below - the
+ // number of real attempts is half of the value of <mnSuccessfulCredentialAttempts>
+ if ( (mnSuccessfulCredentialAttempts / 2) >= 5 ||
+ mbInputOfCredentialsAborted )
+ {
+ mbInputOfCredentialsAborted = true;
+ return SERF_ERROR_AUTHN_FAILED;
+ }
+
+ // because serf keeps credentials only for a connection in case of digest authentication
+ // we give each successful provided credentials a second try in order to workaround the
+ // situation that the connection for which the credentials have been provided has been closed
+ // before the provided credentials could be applied for the request.
+ apr_status_t status = mrSerfSession.provideSerfCredentials( (mnSuccessfulCredentialAttempts % 2) == 1,
+ outUsername,
+ outPassword,
+ inRequest,
+ inCode,
+ inAuthProtocol,
+ inRealm,
+ inAprPool );
+ if ( status != APR_SUCCESS )
+ {
+ mbInputOfCredentialsAborted = true;
+ }
+ else
+ {
+ ++mnSuccessfulCredentialAttempts;
+ }
+
+ return status;
+}
+
+apr_status_t SerfRequestProcessor::setupSerfRequest( serf_request_t * inSerfRequest,
+ serf_bucket_t ** outSerfRequestBucket,
+ serf_response_acceptor_t * outSerfResponseAcceptor,
+ void ** outSerfResponseAcceptorBaton,
+ serf_response_handler_t * outSerfResponseHandler,
+ void ** outSerfResponseHandlerBaton,
+ apr_pool_t * /*inAprPool*/ )
+{
+ mbSetupSerfRequestCalled = true;
+ *outSerfRequestBucket = mpProcImpl->createSerfRequestBucket( inSerfRequest );
+
+ // apply callbacks for accepting response and handling response
+ *outSerfResponseAcceptor = Serf_AcceptResponse;
+ *outSerfResponseAcceptorBaton = this;
+ *outSerfResponseHandler = Serf_HandleResponse;
+ *outSerfResponseHandlerBaton = this;
+
+ return APR_SUCCESS;
+}
+
+serf_bucket_t* SerfRequestProcessor::acceptSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfStreamBucket,
+ apr_pool_t * inAprPool )
+{
+ mbAcceptSerfResponseCalled = true;
+ return mrSerfSession.acceptSerfResponse( inSerfRequest,
+ inSerfStreamBucket,
+ inAprPool );
+}
+
+apr_status_t SerfRequestProcessor::handleSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfResponseBucket,
+ apr_pool_t * inAprPool )
+{
+ mbHandleSerfResponseCalled = true;
+
+ // some general response handling and error handling
+ {
+ if ( !inSerfResponseBucket )
+ {
+ /* A NULL response can come back if the request failed completely */
+ mbProcessingDone = true;
+ return APR_EGENERAL;
+ }
+
+ serf_status_line sl;
+ apr_status_t status = serf_bucket_response_status( inSerfResponseBucket, &sl );
+ if ( status )
+ {
+ mbProcessingDone = false; // allow another try in order to get a response
+ return status;
+ }
+ // TODO - check, if response status code handling is correct
+ mnHTTPStatusCode = ( sl.version != 0 && sl.code >= 0 )
+ ? static_cast< sal_uInt16 >( sl.code )
+ : SC_NONE;
+ if ( sl.reason )
+ {
+ mHTTPStatusCodeText = OUString::createFromAscii( sl.reason );
+ }
+ if ( ( sl.version == 0 || sl.code < 0 ) ||
+ mnHTTPStatusCode >= 300 )
+ {
+ if ( mnHTTPStatusCode == 301 ||
+ mnHTTPStatusCode == 302 ||
+ mnHTTPStatusCode == 303 ||
+ mnHTTPStatusCode == 307 )
+ {
+ // new location for certain redirections
+ serf_bucket_t *headers = serf_bucket_response_get_headers( inSerfResponseBucket );
+ const char* location = serf_bucket_headers_get( headers, "Location" );
+ if ( location )
+ {
+ mRedirectLocation = OUString::createFromAscii( location );
+ }
+ mbProcessingDone = true;
+ return APR_EGENERAL;
+ }
+ else if ( mrSerfSession.isHeadRequestInProgress() &&
+ ( mnHTTPStatusCode == 401 || mnHTTPStatusCode == 407 ) )
+ {
+ // keep going as authentication is not required on HEAD request.
+ // the response already contains header fields.
+ }
+ else
+ {
+ mbProcessingDone = true;
+ return APR_EGENERAL;
+ }
+ }
+ }
+
+ // request specific processing of the response bucket
+ apr_status_t status = APR_SUCCESS;
+ mbProcessingDone = mpProcImpl->processSerfResponseBucket( inSerfRequest,
+ inSerfResponseBucket,
+ inAprPool,
+ status );
+
+ return status;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfRequestProcessor.hxx b/ucb/source/ucp/webdav/SerfRequestProcessor.hxx
new file mode 100644
index 000000000..dea753fed
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfRequestProcessor.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSOR_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSOR_HXX
+
+#include <apr_errno.h>
+#include <apr_pools.h>
+
+#include <serf.h>
+
+#include "DAVTypes.hxx"
+#include "DAVResource.hxx"
+#include "DAVException.hxx"
+
+#include "SerfInputStream.hxx"
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+
+#include <rtl/ref.hxx>
+
+namespace http_dav_ucp
+{
+
+class SerfSession;
+class SerfRequestProcessorImpl;
+
+class SerfRequestProcessor
+{
+public:
+ SerfRequestProcessor( SerfSession& rSerfSession,
+ const OUString & inPath,
+ const bool bUseChunkedEncoding );
+ ~SerfRequestProcessor();
+
+ // PROPFIND - allprop & named
+ bool processPropFind( const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ apr_status_t& outSerfStatus );
+
+ // PROPFIND - property names
+ bool processPropFind( const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ apr_status_t& outSerfStatus );
+
+ // PROPPATCH
+ bool processPropPatch( const std::vector< ProppatchValue > & inProperties,
+ apr_status_t& outSerfStatus );
+
+ // GET
+ bool processGet( const rtl::Reference< SerfInputStream >& xioInStrm,
+ apr_status_t& outSerfStatus );
+
+ // GET inclusive header fields
+ bool processGet( const rtl::Reference< SerfInputStream >& xioInStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus );
+
+ // GET
+ bool processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ apr_status_t& outSerfStatus );
+
+ // GET inclusive header fields
+ bool processGet( const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus );
+
+ // HEAD
+ bool processHead( const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ apr_status_t& outSerfStatus );
+
+ // PUT
+ bool processPut( const char* inData,
+ apr_size_t inDataLen,
+ apr_status_t& outSerfStatus );
+
+ // POST
+ bool processPost( const char* inData,
+ apr_size_t inDataLen,
+ const OUString & inContentType,
+ const OUString & inReferer,
+ const rtl::Reference< SerfInputStream >& xioInStrm,
+ apr_status_t& outSerfStatus );
+
+ // POST
+ bool processPost( const char* inData,
+ apr_size_t inDataLen,
+ const OUString & inContentType,
+ const OUString & inReferer,
+ const css::uno::Reference< css::io::XOutputStream >& xioOutStrm,
+ apr_status_t& outSerfStatus );
+
+ // DELETE
+ bool processDelete( apr_status_t& outSerfStatus );
+
+ // MKCOL
+ bool processMkCol( apr_status_t& outSerfStatus );
+
+ // COPY
+ bool processCopy( const OUString & inDestinationPath,
+ const bool inOverwrite,
+ apr_status_t& outSerfStatus );
+
+ // MOVE
+ bool processMove( const OUString & inDestinationPath,
+ const bool inOverwrite,
+ apr_status_t& outSerfStatus );
+
+ //LOCK
+ bool processLock( const css::ucb::Lock & rLock, sal_Int32 *plastChanceToSendRefreshRequest = nullptr );
+
+ //UNLOCK
+ bool processUnlock();
+
+ apr_status_t provideSerfCredentials( char ** outUsername,
+ char ** outPassword,
+ serf_request_t * inRequest,
+ int inCode,
+ const char *inAuthProtocol,
+ const char *inRealm,
+ apr_pool_t *inAprPool );
+
+ apr_status_t setupSerfRequest( serf_request_t * inSerfRequest,
+ serf_bucket_t ** outSerfRequestBucket,
+ serf_response_acceptor_t * outSerfResponseAcceptor,
+ void ** outSerfResponseAcceptorBaton,
+ serf_response_handler_t * outSerfResponseHandler,
+ void ** outSerfResponseHandlerBaton,
+ apr_pool_t * inAprPool );
+
+ serf_bucket_t* acceptSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfStreamBucket,
+ apr_pool_t* inAprPool );
+
+ apr_status_t handleSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfResponseBucket,
+ apr_pool_t * inAprPool );
+
+//private:
+ void prepareProcessor();
+ apr_status_t runProcessor();
+ void postprocessProcessor( const apr_status_t inStatus );
+
+ SerfSession& mrSerfSession;
+ const char* mPathStr;
+ const bool mbUseChunkedEncoding;
+ const char* mDestPathStr;
+ const char* mContentType;
+ const char* mReferer;
+ SerfRequestProcessorImpl* mpProcImpl;
+
+ bool mbProcessingDone;
+
+ DAVException* mpDAVException;
+ sal_uInt16 mnHTTPStatusCode;
+ OUString mHTTPStatusCodeText;
+ OUString mRedirectLocation;
+
+ sal_uInt8 mnSuccessfulCredentialAttempts;
+ bool mbInputOfCredentialsAborted;
+ bool mbSetupSerfRequestCalled;
+ bool mbAcceptSerfResponseCalled;
+ bool mbHandleSerfResponseCalled;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfRequestProcessorImpl.cxx b/ucb/source/ucp/webdav/SerfRequestProcessorImpl.cxx
new file mode 100644
index 000000000..dffd5e907
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfRequestProcessorImpl.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfRequestProcessorImpl.hxx"
+#include <sal/log.hxx>
+
+namespace
+{
+// Define a magic value that is used by serf to reset chunked
+// encoding. The value definition is not supported by serf, hence the
+// definition here.
+static const apr_int64_t SERF_UNKNOWN_LENGTH (-1);
+}
+
+namespace http_dav_ucp
+{
+
+SerfRequestProcessorImpl::SerfRequestProcessorImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders )
+ : mPathStr( inPath )
+ , mrRequestHeaders( inRequestHeaders )
+ , mbUseChunkedEncoding( false )
+{
+}
+
+SerfRequestProcessorImpl::~SerfRequestProcessorImpl()
+{
+}
+
+const char* SerfRequestProcessorImpl::getPathStr() const
+{
+ return mPathStr;
+}
+
+void SerfRequestProcessorImpl::activateChunkedEncoding()
+{
+ mbUseChunkedEncoding = true;
+}
+
+bool SerfRequestProcessorImpl::useChunkedEncoding() const
+{
+ return mbUseChunkedEncoding;
+}
+
+
+void SerfRequestProcessorImpl::handleChunkedEncoding (
+ serf_bucket_t* pRequestBucket,
+ apr_int64_t nLength) const
+{
+ if (pRequestBucket != nullptr)
+ {
+ if (useChunkedEncoding())
+ {
+ // Activate chunked encoding.
+ serf_bucket_request_set_CL(pRequestBucket, SERF_UNKNOWN_LENGTH);
+ }
+ else
+ {
+ // Deactivate chunked encoding by setting the length.
+ serf_bucket_request_set_CL(pRequestBucket, nLength);
+ }
+ }
+}
+
+
+void SerfRequestProcessorImpl::setRequestHeaders( serf_bucket_t* inoutSerfHeaderBucket )
+{
+ bool bHasUserAgent( false );
+
+ for ( const auto& rHeader : mrRequestHeaders )
+ {
+ const OString aHeader = OUStringToOString( rHeader.first, RTL_TEXTENCODING_UTF8 );
+ const OString aValue = OUStringToOString( rHeader.second, RTL_TEXTENCODING_UTF8 );
+
+ SAL_INFO("ucb.ucp.webdav", "Request Header - \"" << aHeader << ": " << aValue << "\"");
+ if ( !bHasUserAgent )
+ bHasUserAgent = rHeader.first == "User-Agent";
+
+ serf_bucket_headers_setc( inoutSerfHeaderBucket,
+ aHeader.getStr(),
+ aValue.getStr() );
+ }
+
+ if ( !bHasUserAgent )
+ {
+ serf_bucket_headers_set( inoutSerfHeaderBucket,
+ "User-Agent", "LibreOffice" );
+ }
+
+ serf_bucket_headers_set( inoutSerfHeaderBucket, "Accept-Encoding", "gzip");
+}
+
+bool SerfRequestProcessorImpl::processSerfResponseBucket( serf_request_t * /*inSerfRequest*/,
+ serf_bucket_t * inSerfResponseBucket,
+ apr_pool_t * /*inAprPool*/,
+ apr_status_t & outStatus )
+{
+ const char* data;
+ apr_size_t len;
+
+ while (true) {
+ outStatus = serf_bucket_read(inSerfResponseBucket, 8096, &data, &len);
+ if (SERF_BUCKET_READ_ERROR(outStatus))
+ {
+ return true;
+ }
+
+ if ( len > 0 )
+ {
+ processChunkOfResponseData( data, len );
+ }
+
+ /* are we done yet? */
+ if (APR_STATUS_IS_EOF(outStatus))
+ {
+ handleEndOfResponseData( inSerfResponseBucket );
+
+ outStatus = APR_EOF;
+ return true;
+ }
+
+ /* have we drained the response so far? */
+ if ( APR_STATUS_IS_EAGAIN( outStatus ) )
+ {
+ return false;
+ }
+ }
+
+ /* NOTREACHED */
+ return true;
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfRequestProcessorImpl.hxx b/ucb/source/ucp/webdav/SerfRequestProcessorImpl.hxx
new file mode 100644
index 000000000..3909dca5a
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfRequestProcessorImpl.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSORIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSORIMPL_HXX
+
+#include <serf.h>
+
+#include <sal/types.h>
+#include "DAVRequestEnvironment.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfRequestProcessorImpl
+{
+public:
+ SerfRequestProcessorImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders );
+
+ virtual ~SerfRequestProcessorImpl();
+
+ /*pure*/ virtual
+ serf_bucket_t * createSerfRequestBucket( serf_request_t * inSerfRequest ) = 0;
+
+ bool processSerfResponseBucket( serf_request_t * inSerfRequest,
+
+ serf_bucket_t * inSerfResponseBucket,
+ apr_pool_t * inAprPool,
+ apr_status_t & outStatus );
+
+ void activateChunkedEncoding();
+
+ /** Turn chunked encoding on or off, depending on the result of
+ useChunkedEncoding().
+ */
+ void handleChunkedEncoding (
+ serf_bucket_t* pRequestBucket,
+ apr_int64_t nLength) const;
+
+protected:
+ void setRequestHeaders( serf_bucket_t* inoutSerfHeaderBucket );
+
+ /*pure*/ virtual
+ void processChunkOfResponseData( const char* data, apr_size_t len ) = 0;
+
+ /*pure*/ virtual
+ void handleEndOfResponseData( serf_bucket_t * inSerfResponseBucket ) = 0;
+
+ const char* getPathStr() const;
+ bool useChunkedEncoding() const;
+
+private:
+ const char* mPathStr;
+ const DAVRequestHeaders& mrRequestHeaders;
+ bool mbUseChunkedEncoding;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFREQUESTPROCESSORIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfSession.cxx b/ucb/source/ucp/webdav/SerfSession.cxx
new file mode 100644
index 000000000..8f7a7e38c
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfSession.cxx
@@ -0,0 +1,1489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+#include <string.h>
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/simplecertificatevalidationrequest.hxx>
+
+#include "AprEnv.hxx"
+#include <apr_strings.h>
+
+#include "DAVAuthListener.hxx"
+#include "SerfSession.hxx"
+#include "SerfUri.hxx"
+#include "SerfRequestProcessor.hxx"
+#include "SerfCallbacks.hxx"
+#include "SerfInputStream.hxx"
+
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/security/CertificateContainerStatus.hpp>
+#include <com/sun/star/security/CertificateContainer.hpp>
+#include <com/sun/star/security/XCertificateContainer.hpp>
+#include <com/sun/star/security/CertAltNameEntry.hpp>
+#include <com/sun/star/security/XSanExtension.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#define OID_SUBJECT_ALTERNATIVE_NAME "2.5.29.17"
+
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+// Constructor
+
+SerfSession::SerfSession(
+ const rtl::Reference< DAVSessionFactory > & rSessionFactory,
+ const OUString& inUri,
+ const ucbhelper::InternetProxyDecider & rProxyDecider )
+ : DAVSession( rSessionFactory )
+ , m_aMutex()
+ , m_aUri( inUri )
+ , m_aProxyName()
+ , m_nProxyPort( 0 )
+ , m_pSerfConnection( nullptr )
+ , m_pSerfContext( nullptr )
+ , m_bIsHeadRequestInProgress( false )
+ , m_bUseChunkedEncoding( false )
+ , m_bNoOfTransferEncodingSwitches( 0 )
+ , m_rProxyDecider( rProxyDecider )
+ , m_aEnv()
+{
+ m_pSerfContext = serf_context_create( getAprPool() );
+
+ m_pSerfBucket_Alloc = serf_bucket_allocator_create( getAprPool(), nullptr, nullptr );
+}
+
+
+// Destructor
+
+SerfSession::~SerfSession( )
+{
+ if ( m_pSerfConnection )
+ {
+ serf_connection_close( m_pSerfConnection );
+ m_pSerfConnection = nullptr;
+ }
+}
+
+
+void SerfSession::Init( const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+ m_aEnv = rEnv;
+ Init();
+}
+
+
+void SerfSession::Init()
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ bool bCreateNewSession = false;
+
+ if ( m_pSerfConnection == nullptr )
+ {
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ // Not yet initialized. Create new session.
+ bCreateNewSession = true;
+ }
+ else
+ {
+ const ucbhelper::InternetProxyServer & rProxyCfg = getProxySettings();
+
+ if ( ( rProxyCfg.aName != m_aProxyName )
+ || ( rProxyCfg.nPort != m_nProxyPort ) )
+ {
+ m_aProxyName = rProxyCfg.aName;
+ m_nProxyPort = rProxyCfg.nPort;
+
+ // new session needed, destroy old first
+ serf_connection_close( m_pSerfConnection );
+ m_pSerfConnection = nullptr;
+ bCreateNewSession = true;
+ }
+ }
+
+ if ( bCreateNewSession )
+ {
+ // TODO - close_connection callback
+ apr_status_t status = serf_connection_create2( &m_pSerfConnection,
+ m_pSerfContext,
+ m_aUri.getAprUri(),
+ Serf_ConnectSetup, this,
+ nullptr /* close connection callback */, nullptr /* close connection baton */,
+ getAprPool() );
+
+ if ( m_pSerfConnection == nullptr ||status != APR_SUCCESS )
+ {
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) );
+ }
+
+ // Register the session with the lock store
+// m_aSerfLockStore.registerSession( m_pSerfConnection );
+
+ if ( m_aProxyName.getLength() )
+ {
+ apr_sockaddr_t *proxy_address = nullptr;
+ status = apr_sockaddr_info_get( &proxy_address,
+ OUStringToOString( m_aProxyName, RTL_TEXTENCODING_UTF8 ).getStr(),
+ APR_UNSPEC,
+ static_cast<apr_port_t>(m_nProxyPort),
+ 0, getAprPool() );
+
+ if ( status != APR_SUCCESS )
+ {
+ throw DAVException( DAVException::DAV_SESSION_CREATE,
+ SerfUri::makeConnectionEndPointString( m_aUri.GetHost(), m_aUri.GetPort() ) );
+ }
+
+ serf_config_proxy( m_pSerfContext, proxy_address );
+ }
+
+
+ serf_config_credentials_callback( m_pSerfContext, Serf_Credentials );
+
+ m_bUseChunkedEncoding = isSSLNeeded();
+ }
+}
+
+apr_pool_t* SerfSession::getAprPool()
+{
+ return apr_environment::AprEnv::getAprEnv()->getAprPool();
+}
+
+serf_bucket_alloc_t* SerfSession::getSerfBktAlloc()
+{
+ return m_pSerfBucket_Alloc;
+}
+
+serf_context_t* SerfSession::getSerfContext()
+{
+ return m_pSerfContext;
+}
+
+serf_connection_t* SerfSession::getSerfConnection()
+{
+ return m_pSerfConnection;
+}
+
+bool SerfSession::isHeadRequestInProgress()
+{
+ return m_bIsHeadRequestInProgress;
+}
+
+bool SerfSession::isSSLNeeded()
+{
+ return m_aUri.GetScheme().equalsIgnoreAsciiCase( "https" );
+}
+
+char* SerfSession::getHostinfo()
+{
+ return m_aUri.getAprUri().hostinfo;
+}
+
+
+// virtual
+bool SerfSession::CanUse( const OUString & inUri )
+{
+ try
+ {
+ SerfUri theUri( inUri );
+ if ( ( theUri.GetPort() == m_aUri.GetPort() ) &&
+ ( theUri.GetHost() == m_aUri.GetHost() ) &&
+ ( theUri.GetScheme() == m_aUri.GetScheme() ) )
+ {
+ return true;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ return false;
+ }
+ return false;
+}
+
+
+// virtual
+bool SerfSession::UsesProxy()
+{
+ Init();
+ return ( m_aProxyName.getLength() > 0 );
+}
+
+apr_status_t SerfSession::setupSerfConnection( apr_socket_t * inAprSocket,
+ serf_bucket_t **outSerfInputBucket,
+ serf_bucket_t **outSerfOutputBucket,
+ apr_pool_t* /*inAprPool*/ )
+{
+ serf_bucket_t *tmpInputBkt;
+ tmpInputBkt = serf_context_bucket_socket_create( getSerfContext(),
+ inAprSocket,
+ getSerfBktAlloc() );
+
+ if ( isSSLNeeded() )
+ {
+ tmpInputBkt = serf_bucket_ssl_decrypt_create( tmpInputBkt,
+ nullptr,
+ getSerfBktAlloc() );
+ /** Set the callback that is called to authenticate the
+ certificate (chain).
+ */
+ serf_ssl_server_cert_chain_callback_set(
+ serf_bucket_ssl_decrypt_context_get(tmpInputBkt),
+ nullptr,
+ Serf_CertificateChainValidation,
+ this);
+ serf_ssl_set_hostname( serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
+ getHostinfo() );
+
+ *outSerfOutputBucket = serf_bucket_ssl_encrypt_create( *outSerfOutputBucket,
+ serf_bucket_ssl_decrypt_context_get( tmpInputBkt ),
+ getSerfBktAlloc() );
+ }
+
+ *outSerfInputBucket = tmpInputBkt;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t SerfSession::provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry,
+ char ** outUsername,
+ char ** outPassword,
+ serf_request_t * /*inRequest*/,
+ int /*inCode*/,
+ const char *inAuthProtocol,
+ const char *inRealm,
+ apr_pool_t *inAprPool )
+{
+ DAVAuthListener * pListener = getRequestEnvironment().m_xAuthListener.get();
+ if ( !pListener )
+ {
+ // abort
+ return SERF_ERROR_AUTHN_FAILED;
+ }
+
+ OUString theUserName;
+ OUString thePassWord;
+ try
+ {
+ SerfUri uri( getRequestEnvironment().m_aRequestURI );
+ OUString aUserInfo( uri.GetUserInfo() );
+ if ( aUserInfo.getLength() )
+ {
+ sal_Int32 nPos = aUserInfo.indexOf( '@' );
+ if ( nPos == -1 )
+ {
+ theUserName = aUserInfo;
+ }
+ else
+ {
+ theUserName = aUserInfo.copy( 0, nPos );
+ thePassWord = aUserInfo.copy( nPos + 1 );
+ }
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // abort
+ return SERF_ERROR_AUTHN_FAILED;
+ }
+
+ const bool bCanUseSystemCreds = ( ( strcasecmp( inAuthProtocol, "NTLM" ) == 0 ) ||
+ ( strcasecmp( inAuthProtocol, "Negotiate" ) == 0 ) );
+
+ int theRetVal = pListener->authenticate( OUString::createFromAscii( inRealm ),
+ getHostName(),
+ theUserName,
+ thePassWord,
+ bCanUseSystemCreds,
+ bGiveProvidedCredentialsASecondTry );
+
+ if ( theRetVal == 0 )
+ {
+ *outUsername = apr_pstrdup( inAprPool, OUStringToOString( theUserName, RTL_TEXTENCODING_UTF8 ).getStr() );
+ *outPassword = apr_pstrdup( inAprPool, OUStringToOString( thePassWord, RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+
+ return theRetVal != 0 ? SERF_ERROR_AUTHN_FAILED : APR_SUCCESS;
+}
+
+apr_status_t SerfSession::verifySerfCertificateChain (
+ int,
+ const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
+ int nCertificateChainLength)
+{
+ // Check arguments.
+ if (pCertificateChainBase64Encoded == nullptr || nCertificateChainLength<=0)
+ {
+ assert(pCertificateChainBase64Encoded != nullptr);
+ assert(nCertificateChainLength>0);
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+
+ // When called from SerfLockStore::~SerfLockStore(),
+ // css::xml::crypto::SEInitializer::create() will fail
+ // but we want to send unlock commands anyway,
+ // so just ignore certificates and return here.
+ if (apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->finishing())
+ return APR_SUCCESS;
+
+ // Create some crypto objects to decode and handle the base64
+ // encoded certificate chain.
+ uno::Reference< security::XCertificateContainer > xCertificateContainer;
+ uno::Reference< xml::crypto::XXMLSecurityContext > xSecurityContext;
+ uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnv;
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext =
+ ::comphelper::getProcessComponentContext();
+ // Create a certificate container.
+ xCertificateContainer = security::CertificateContainer::create( xContext );
+
+ css::uno::Reference< css::xml::crypto::XSEInitializer > xSEInitializer =
+ css::xml::crypto::SEInitializer::create( xContext );
+
+ xSecurityContext = xSEInitializer->createSecurityContext( OUString() );
+ if (xSecurityContext.is())
+ xSecurityEnv = xSecurityContext->getSecurityEnvironment();
+
+ if ( ! xSecurityContext.is() || ! xSecurityEnv.is())
+ {
+ // Do we have to dispose xSEInitializer or xCertificateContainer?
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+ }
+ catch ( uno::Exception const &)
+ {
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+
+ // Decode the server certificate.
+ const char* sBase64EncodedServerCertificate (
+ serf_ssl_cert_export(
+ pCertificateChainBase64Encoded[0],
+ getAprPool()));
+ uno::Reference< security::XCertificate > xServerCertificate(
+ xSecurityEnv->createCertificateFromAscii(
+ OUString::createFromAscii(sBase64EncodedServerCertificate)));
+ if ( ! xServerCertificate.is())
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+
+ // Get the subject from the server certificate.
+ OUString sServerCertificateSubject (xServerCertificate->getSubjectName());
+ sal_Int32 nIndex = 0;
+ while (nIndex >= 0)
+ {
+ const OUString sToken (sServerCertificateSubject.getToken(0, ',', nIndex));
+ if (sToken.startsWith("CN="))
+ {
+ sServerCertificateSubject = sToken.copy(3);
+ break;
+ }
+ else if (sToken.startsWith(" CN="))
+ {
+ sServerCertificateSubject = sToken.copy(4);
+ break;
+ }
+ }
+
+ // When the certificate container already contains a (trusted)
+ // entry for the server then we do not have to authenticate any
+ // certificate.
+ const security::CertificateContainerStatus eStatus (
+ xCertificateContainer->hasCertificate(
+ getHostName(), sServerCertificateSubject ) );
+ if (eStatus != security::CertificateContainerStatus_NOCERT)
+ {
+ return eStatus == security::CertificateContainerStatus_TRUSTED
+ ? APR_SUCCESS
+ : SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+
+ // The shortcut failed, so try to verify the whole chain. This is
+ // done outside the isDomainMatch() block because the result is
+ // used by the interaction handler.
+ std::vector< uno::Reference< security::XCertificate > > aChain;
+ for (nIndex = 1; nIndex < nCertificateChainLength; ++nIndex)
+ {
+ const char* sBase64EncodedCertificate (
+ serf_ssl_cert_export(
+ pCertificateChainBase64Encoded[nIndex],
+ getAprPool()));
+ uno::Reference< security::XCertificate > xCertificate(
+ xSecurityEnv->createCertificateFromAscii(
+ OUString::createFromAscii(sBase64EncodedCertificate)));
+ if ( ! xCertificate.is())
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ aChain.push_back(xCertificate);
+ }
+ const sal_Int64 nVerificationResult (xSecurityEnv->verifyCertificate(
+ xServerCertificate,
+ ::comphelper::containerToSequence(aChain)));
+
+ // When the certificate matches the host name then we can use the
+ // result of the verification.
+ bool bHostnameMatchesCertHostnames = false;
+ {
+ uno::Sequence< uno::Reference< security::XCertificateExtension > > extensions = xServerCertificate->getExtensions();
+ uno::Sequence< security::CertAltNameEntry > altNames;
+ for (sal_Int32 i = 0 ; i < extensions.getLength(); ++i)
+ {
+ uno::Reference< security::XCertificateExtension >element = extensions[i];
+
+ const OString aId ( reinterpret_cast<const char *>(const_cast<const signed char *>(element->getExtensionId().getArray())), element->getExtensionId().getLength());
+ if ( aId.equals( OID_SUBJECT_ALTERNATIVE_NAME ) )
+ {
+ uno::Reference< security::XSanExtension > sanExtension ( element, uno::UNO_QUERY );
+ altNames = sanExtension->getAlternativeNames();
+ break;
+ }
+ }
+
+ uno::Sequence< OUString > certHostNames(altNames.getLength() + 1);
+ certHostNames[0] = sServerCertificateSubject;
+ for( int n = 0; n < altNames.getLength(); ++n )
+ {
+ if (altNames[n].Type == security::ExtAltNameType_DNS_NAME)
+ {
+ altNames[n].Value >>= certHostNames[n+1];
+ }
+ }
+
+ for ( int i = 0; i < certHostNames.getLength() && !bHostnameMatchesCertHostnames; ++i )
+ {
+ bHostnameMatchesCertHostnames = isDomainMatch( certHostNames[i] );
+ }
+
+ }
+ if ( bHostnameMatchesCertHostnames )
+ {
+
+ if (nVerificationResult == 0)
+ {
+ // Certificate (chain) is valid.
+ xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, true);
+ return APR_SUCCESS;
+ }
+ else if ((nVerificationResult & security::CertificateValidity::CHAIN_INCOMPLETE) != 0)
+ {
+ // We do not have enough information for verification,
+ // neither automatically (as we just discovered) nor
+ // manually (so there is no point in showing any dialog.)
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+ else if ((nVerificationResult &
+ (security::CertificateValidity::INVALID | security::CertificateValidity::REVOKED)) != 0)
+ {
+ // Certificate (chain) is invalid.
+ xCertificateContainer->addCertificate(getHostName(), sServerCertificateSubject, false);
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+ else
+ {
+ // For all other we have to ask the user.
+ }
+ }
+
+ // We have not been able to automatically verify (or falsify) the
+ // certificate chain. To resolve this we have to ask the user.
+ const uno::Reference< ucb::XCommandEnvironment > xEnv( getRequestEnvironment().m_xEnv );
+ if ( xEnv.is() )
+ {
+ uno::Reference< task::XInteractionHandler > xIH( xEnv->getInteractionHandler() );
+ if ( xIH.is() )
+ {
+ rtl::Reference< ucbhelper::SimpleCertificateValidationRequest >
+ xRequest( new ucbhelper::SimpleCertificateValidationRequest(
+ static_cast<sal_Int32>(nVerificationResult), xServerCertificate, getHostName() ) );
+ xIH->handle( xRequest.get() );
+
+ rtl::Reference< ucbhelper::InteractionContinuation > xSelection
+ = xRequest->getSelection();
+
+ if ( xSelection.is() )
+ {
+ uno::Reference< task::XInteractionApprove > xApprove( xSelection.get(), uno::UNO_QUERY );
+ if ( xApprove.is() )
+ {
+ xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, true );
+ return APR_SUCCESS;
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, false );
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+ }
+ }
+ else
+ {
+ // Don't trust cert
+ xCertificateContainer->addCertificate( getHostName(), sServerCertificateSubject, false );
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+ }
+ }
+
+ return SERF_SSL_CERT_UNKNOWN_FAILURE;
+}
+
+serf_bucket_t* SerfSession::acceptSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfStreamBucket,
+ apr_pool_t* /*inAprPool*/ )
+{
+ // get the per-request bucket allocator
+ serf_bucket_alloc_t* SerfBktAlloc = serf_request_get_alloc( inSerfRequest );
+
+ // create a barrier bucket so the response doesn't eat us!
+ serf_bucket_t *responseBkt = serf_bucket_barrier_create( inSerfStreamBucket,
+ SerfBktAlloc );
+
+ // create response bucket
+ responseBkt = serf_bucket_response_create( responseBkt,
+ SerfBktAlloc );
+
+ if ( isHeadRequestInProgress() )
+ {
+ // advise the response bucket that this was from a HEAD request and that it should not expect to see a response body.
+ serf_bucket_response_set_head( responseBkt );
+ }
+
+ return responseBkt;
+}
+
+SerfRequestProcessor* SerfSession::createReqProc( const OUString & inPath )
+{
+ return new SerfRequestProcessor( *this,
+ inPath,
+ m_bUseChunkedEncoding );
+}
+
+
+// PROPFIND - allprop & named
+
+void SerfSession::PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ apr_status_t status = APR_SUCCESS;
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processPropFind( inDepth,
+ inPropNames,
+ ioResources,
+ status );
+
+ if ( status == APR_SUCCESS &&
+ aReqProc->mpDAVException == nullptr &&
+ ioResources.empty() )
+ {
+ m_aEnv = DAVRequestEnvironment();
+ throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, APR_EGENERAL );
+ }
+ HandleError( aReqProc );
+}
+
+
+// PROPFIND - propnames
+
+void SerfSession::PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo > & ioResInfo,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ apr_status_t status = APR_SUCCESS;
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processPropFind( inDepth,
+ ioResInfo,
+ status );
+
+ if ( status == APR_SUCCESS &&
+ aReqProc->mpDAVException == nullptr &&
+ ioResInfo.empty() )
+ {
+ m_aEnv = DAVRequestEnvironment();
+ throw DAVException( DAVException::DAV_HTTP_ERROR, inPath, APR_EGENERAL );
+ }
+ HandleError( aReqProc );
+}
+
+
+// PROPPATCH
+
+void SerfSession::PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ apr_status_t status = APR_SUCCESS;
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processPropPatch( inValues,
+ status );
+
+ HandleError( aReqProc );
+}
+
+
+// HEAD
+
+void SerfSession::HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ m_bIsHeadRequestInProgress = true;
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processHead( inHeaderNames,
+ ioResource,
+ status );
+
+ m_bIsHeadRequestInProgress = false;
+
+ HandleError( aReqProc );
+}
+
+
+// GET
+
+uno::Reference< io::XInputStream >
+SerfSession::GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream );
+ apr_status_t status = APR_SUCCESS;
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processGet( xInputStream,
+ status );
+
+ HandleError( aReqProc );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+
+// GET
+
+void SerfSession::GET( const OUString & inPath,
+ uno::Reference< io::XOutputStream > & ioOutputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ apr_status_t status = APR_SUCCESS;
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processGet( ioOutputStream,
+ status );
+
+ HandleError( aReqProc );
+}
+
+
+// GET
+
+uno::Reference< io::XInputStream >
+SerfSession::GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream );
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processGet( xInputStream,
+ inHeaderNames,
+ ioResource,
+ status );
+
+ HandleError( aReqProc );
+
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+
+// GET
+
+void SerfSession::GET( const OUString & inPath,
+ uno::Reference< io::XOutputStream > & ioOutputStream,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ ioResource.uri = inPath;
+ ioResource.properties.clear();
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processGet( ioOutputStream,
+ inHeaderNames,
+ ioResource,
+ status );
+
+ HandleError( aReqProc );
+}
+
+
+// PUT
+
+void SerfSession::PUT( const OUString & inPath,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, false ) )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processPut( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
+ aDataToSend.getLength(),
+ status );
+
+ HandleError( aReqProc );
+}
+
+
+// POST
+
+uno::Reference< io::XInputStream >
+SerfSession::POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ {
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ rtl::Reference< SerfInputStream > xInputStream( new SerfInputStream );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
+ aDataToSend.getLength(),
+ rContentType,
+ rReferer,
+ xInputStream,
+ status );
+
+ HandleError( aReqProc );
+ return uno::Reference< io::XInputStream >( xInputStream.get() );
+}
+
+
+// POST
+
+void SerfSession::POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const uno::Reference< io::XInputStream > & inInputStream,
+ uno::Reference< io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ uno::Sequence< sal_Int8 > aDataToSend;
+ if ( !getDataFromInputStream( inInputStream, aDataToSend, true ) )
+ {
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processPost( reinterpret_cast< const char * >( aDataToSend.getConstArray() ),
+ aDataToSend.getLength(),
+ rContentType,
+ rReferer,
+ oOutputStream,
+ status );
+
+ HandleError( aReqProc );
+}
+
+
+// MKCOL
+
+void SerfSession::MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processMkCol( status );
+
+ HandleError( aReqProc );
+}
+
+
+// COPY
+
+void SerfSession::COPY( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ SerfUri theSourceUri( inSourceURL );
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processCopy( inDestinationURL, inOverWrite, status );
+
+ HandleError( aReqProc );
+}
+
+
+// MOVE
+
+void SerfSession::MOVE( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ SerfUri theSourceUri( inSourceURL );
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( theSourceUri.GetPath() ) );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processMove( inDestinationURL, inOverWrite, status );
+
+ HandleError( aReqProc );
+}
+
+
+// DESTROY
+
+void SerfSession::DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ apr_status_t status = APR_SUCCESS;
+ aReqProc->processDelete( status );
+
+ HandleError( aReqProc );
+}
+
+
+/*
+namespace
+{
+ sal_Int32 lastChanceToSendRefreshRequest( TimeValue const & rStart,
+ int timeout )
+ {
+ TimeValue aEnd;
+ osl_getSystemTime( &aEnd );
+
+ // Try to estimate a safe absolute time for sending the
+ // lock refresh request.
+ sal_Int32 lastChanceToSendRefreshRequest = -1;
+ if ( timeout != NE_TIMEOUT_INFINITE )
+ {
+ sal_Int32 calltime = aEnd.Seconds - rStart.Seconds;
+ if ( calltime <= timeout )
+ {
+ lastChanceToSendRefreshRequest
+ = aEnd.Seconds + timeout - calltime;
+ }
+ else
+ {
+ SAL_INFO("ucb.ucp.webdav", "No chance to refresh lock before timeout!" );
+ }
+ }
+ return lastChanceToSendRefreshRequest;
+ }
+
+} // namespace
+*/
+
+// LOCK (set new lock)
+
+void SerfSession::LOCK( const OUString & inPath,
+ ucb::Lock & rLock,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processLock( rLock );
+
+ HandleError( aReqProc );
+}
+
+
+// LOCK (refresh existing lock)
+
+sal_Int64 SerfSession::LOCK( const OUString & /*inPath*/,
+ sal_Int64 nTimeout,
+ const DAVRequestEnvironment & /*rEnv*/ )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ return nTimeout;
+ /*
+ // Try to get the neon lock from lock store
+ SerfLock * theLock
+ = m_aSerfLockStore.findByUri( makeAbsoluteURL( inPath ) );
+ if ( !theLock )
+ throw DAVException( DAVException::DAV_NOT_LOCKED );
+
+ Init( rEnv );
+
+ // refresh existing lock.
+ theLock->timeout = static_cast< long >( nTimeout );
+
+ TimeValue startCall;
+ osl_getSystemTime( &startCall );
+
+ int theRetVal = ne_lock_refresh( m_pHttpSession, theLock );
+
+ if ( theRetVal == NE_OK )
+ {
+ m_aSerfLockStore.updateLock( theLock,
+ lastChanceToSendRefreshRequest(
+ startCall, theLock->timeout ) );
+ }
+
+ HandleError( theRetVal, inPath, rEnv );
+
+ return theLock->timeout;
+ */
+}
+
+
+// LOCK (refresh existing lock)
+
+bool SerfSession::LOCK( const OUString& rLock,
+ sal_Int32 *plastChanceToSendRefreshRequest )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) );
+ aReqProc->processLock( ucb::Lock(), plastChanceToSendRefreshRequest );
+
+ try
+ {
+ HandleError( aReqProc );
+ SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " succeeded." );
+ return true;
+ }
+ catch(...)
+ {
+ SAL_INFO("ucb.ucp.webdav", "Refreshing LOCK of " << rLock << " failed!" );
+ return false;
+ }
+}
+
+
+// UNLOCK
+
+void SerfSession::UNLOCK( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ Init( rEnv );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( inPath ) );
+ aReqProc->processUnlock();
+
+ try
+ {
+ HandleError( aReqProc );
+ SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " succeeded." );
+ apr_environment::AprEnv::getAprEnv()->getSerfLockStore()->removeLock( inPath );
+ }
+ catch(...)
+ {
+ SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << inPath << " failed!" );
+ }
+}
+
+
+// UNLOCK
+
+void SerfSession::UNLOCK( const OUString& rLock )
+{
+ osl::Guard< osl::Mutex > theGuard( m_aMutex );
+
+ std::shared_ptr<SerfRequestProcessor> aReqProc( createReqProc( rLock ) );
+ aReqProc->processUnlock();
+
+ try
+ {
+ HandleError( aReqProc );
+ SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " succeeded." );
+ }
+ catch(...)
+ {
+ SAL_INFO("ucb.ucp.webdav", "UNLOCK of " << rLock << " failed!" );
+ }
+}
+
+
+void SerfSession::abort()
+{
+ // 11.11.09 (tkr): The following code lines causing crashes if
+ // closing a ongoing connection. It turned out that this existing
+ // solution doesn't work in multi-threading environments.
+ // So I disabled them in 3.2. . Issue #73893# should fix it in OOo 3.3.
+ //if ( m_pHttpSession )
+ // ne_close_connection( m_pHttpSession );
+}
+
+
+ucbhelper::InternetProxyServer SerfSession::getProxySettings() const
+{
+ if ( m_aUri.GetScheme() == "http" || m_aUri.GetScheme() == "https" )
+ {
+ return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
+ m_aUri.GetHost(),
+ m_aUri.GetPort() );
+ }
+ else
+ {
+ // TODO: figure out, if this case can occur
+ return m_rProxyDecider.getProxy( m_aUri.GetScheme(),
+ OUString() /* not used */,
+ -1 /* not used */ );
+ }
+}
+
+/*
+
+namespace {
+
+bool containsLocktoken( const uno::Sequence< ucb::Lock > & rLocks,
+ const char * token )
+{
+ for ( sal_Int32 n = 0; n < rLocks.getLength(); ++n )
+ {
+ const uno::Sequence< OUString > & rTokens
+ = rLocks[ n ].LockTokens;
+ for ( sal_Int32 m = 0; m < rTokens.getLength(); ++m )
+ {
+ if ( rTokens[ m ].equalsAscii( token ) )
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+*/
+
+
+bool SerfSession::removeExpiredLocktoken( const OUString & /*inURL*/,
+ const DAVRequestEnvironment & /*rEnv*/ )
+{
+ return true;
+ /*
+ SerfLock * theLock = m_aSerfLockStore.findByUri( inURL );
+ if ( !theLock )
+ return false;
+
+ // do a lockdiscovery to check whether this lock is still valid.
+ try
+ {
+ // @@@ Alternative: use ne_lock_discover() => less overhead
+
+ std::vector< DAVResource > aResources;
+ std::vector< OUString > aPropNames;
+ aPropNames.push_back( DAVProperties::LOCKDISCOVERY );
+
+ PROPFIND( rEnv.m_aRequestURI, DAVZERO, aPropNames, aResources, rEnv );
+
+ if ( aResources.empty() )
+ return false;
+
+ std::vector< DAVPropertyValue >::const_iterator it
+ = aResources[ 0 ].properties.begin();
+ std::vector< DAVPropertyValue >::const_iterator end
+ = aResources[ 0 ].properties.end();
+
+ while ( it != end )
+ {
+ if ( (*it).Name.equals( DAVProperties::LOCKDISCOVERY ) )
+ {
+ uno::Sequence< ucb::Lock > aLocks;
+ if ( !( (*it).Value >>= aLocks ) )
+ return false;
+
+ if ( !containsLocktoken( aLocks, theLock->token ) )
+ {
+ // expired!
+ break;
+ }
+
+ // still valid.
+ return false;
+ }
+ ++it;
+ }
+
+ // No lockdiscovery prop in propfind result / locktoken not found
+ // in propfind result -> not locked
+ SAL_INFO("ucb.ucp.webdav", "SerfSession::removeExpiredLocktoken: Removing "
+ " expired lock token for " << inURL << ". token: " << theLock->token );
+
+ m_aSerfLockStore.removeLock( theLock );
+ ne_lock_destroy( theLock );
+ return true;
+ }
+ catch ( DAVException const & )
+ {
+ }
+ return false;
+ */
+}
+
+
+// HandleError
+// Common Error Handler
+
+void SerfSession::HandleError( std::shared_ptr<SerfRequestProcessor> rReqProc )
+{
+ m_aEnv = DAVRequestEnvironment();
+
+ if ( rReqProc->mpDAVException )
+ {
+ DAVException* mpDAVExp( rReqProc->mpDAVException );
+
+ serf_connection_reset( getSerfConnection() );
+
+ if ( mpDAVExp->getStatus() == 413 &&
+ m_bNoOfTransferEncodingSwitches < 2 )
+ {
+ m_bUseChunkedEncoding = !m_bUseChunkedEncoding;
+ ++m_bNoOfTransferEncodingSwitches;
+ }
+
+ throw DAVException( mpDAVExp->getError(),
+ mpDAVExp->getData(),
+ mpDAVExp->getStatus() );
+ }
+
+ /*
+ // Map error code to DAVException.
+ switch ( nError )
+ {
+ case NE_OK:
+ return;
+
+ case NE_ERROR: // Generic error
+ {
+ OUString aText = OUString::createFromAscii(
+ ne_get_error( m_pHttpSession ) );
+
+ sal_uInt16 code = makeStatusCode( aText );
+
+ if ( code == SC_LOCKED )
+ {
+ if ( m_aSerfLockStore.findByUri(
+ makeAbsoluteURL( inPath ) ) == 0 )
+ {
+ // locked by 3rd party
+ throw DAVException( DAVException::DAV_LOCKED );
+ }
+ else
+ {
+ // locked by ourself
+ throw DAVException( DAVException::DAV_LOCKED_SELF );
+ }
+ }
+
+ // Special handling for 400 and 412 status codes, which may indicate
+ // that a lock previously obtained by us has been released meanwhile
+ // by the server. Unfortunately, RFC is not clear at this point,
+ // thus server implementations behave different...
+ else if ( code == SC_BAD_REQUEST || code == SC_PRECONDITION_FAILED )
+ {
+ if ( removeExpiredLocktoken( makeAbsoluteURL( inPath ), rEnv ) )
+ throw DAVException( DAVException::DAV_LOCK_EXPIRED );
+ }
+
+ throw DAVException( DAVException::DAV_HTTP_ERROR, aText, code );
+ }
+ case NE_LOOKUP: // Name lookup failed.
+ throw DAVException( DAVException::DAV_HTTP_LOOKUP,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_AUTH: // User authentication failed on server
+ throw DAVException( DAVException::DAV_HTTP_AUTH,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_PROXYAUTH: // User authentication failed on proxy
+ throw DAVException( DAVException::DAV_HTTP_AUTHPROXY,
+ SerfUri::makeConnectionEndPointString(
+ m_aProxyName, m_nProxyPort ) );
+
+ case NE_CONNECT: // Could not connect to server
+ throw DAVException( DAVException::DAV_HTTP_CONNECT,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_TIMEOUT: // Connection timed out
+ throw DAVException( DAVException::DAV_HTTP_TIMEOUT,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_FAILED: // The precondition failed
+ throw DAVException( DAVException::DAV_HTTP_FAILED,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_RETRY: // Retry request (ne_end_request ONLY)
+ throw DAVException( DAVException::DAV_HTTP_RETRY,
+ SerfUri::makeConnectionEndPointString(
+ m_aHostName, m_nPort ) );
+
+ case NE_REDIRECT:
+ {
+ SerfUri aUri( ne_redirect_location( m_pHttpSession ) );
+ throw DAVException(
+ DAVException::DAV_HTTP_REDIRECT, aUri.GetURI() );
+ }
+ default:
+ {
+ SAL_INFO("ucb.ucp.webdav", "SerfSession::HandleError : Unknown Serf error code!" );
+ throw DAVException( DAVException::DAV_HTTP_ERROR,
+ OUString::createFromAscii(
+ ne_get_error( m_pHttpSession ) ) );
+ }
+ }
+ */
+}
+
+
+// static
+bool
+SerfSession::getDataFromInputStream(
+ const uno::Reference< io::XInputStream > & xStream,
+ uno::Sequence< sal_Int8 > & rData,
+ bool bAppendTrailingZeroByte )
+{
+ if ( xStream.is() )
+ {
+ uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY );
+ if ( xSeekable.is() )
+ {
+ try
+ {
+ sal_Int32 nSize
+ = sal::static_int_cast<sal_Int32>(xSeekable->getLength());
+ sal_Int32 nRead
+ = xStream->readBytes( rData, nSize );
+
+ if ( nRead == nSize )
+ {
+ if ( bAppendTrailingZeroByte )
+ {
+ rData.realloc( nSize + 1 );
+ rData[ nSize ] = sal_Int8( 0 );
+ }
+ return true;
+ }
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // readBytes
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // readBytes
+ }
+ catch ( io::IOException const & )
+ {
+ // getLength, readBytes
+ }
+ }
+ else
+ {
+ try
+ {
+ uno::Sequence< sal_Int8 > aBuffer;
+ sal_Int32 nPos = 0;
+
+ sal_Int32 nRead = xStream->readSomeBytes( aBuffer, 65536 );
+ while ( nRead > 0 )
+ {
+ if ( rData.getLength() < ( nPos + nRead ) )
+ rData.realloc( nPos + nRead );
+
+ aBuffer.realloc( nRead );
+ memcpy( rData.getArray() + nPos, aBuffer.getConstArray(), nRead );
+ nPos += nRead;
+
+ aBuffer.realloc( 0 );
+ nRead = xStream->readSomeBytes( aBuffer, 65536 );
+ }
+
+ if ( bAppendTrailingZeroByte )
+ {
+ rData.realloc( nPos + 1 );
+ rData[ nPos ] = sal_Int8( 0 );
+ }
+ return true;
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // readBytes
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // readBytes
+ }
+ catch ( io::IOException const & )
+ {
+ // readBytes
+ }
+ }
+ }
+ return false;
+}
+
+
+bool
+SerfSession::isDomainMatch( const OUString & certHostName )
+{
+ OUString hostName = getHostName();
+
+ if (hostName.equalsIgnoreAsciiCase( certHostName ) )
+ return true;
+
+ if ( certHostName.startsWith( "*" ) &&
+ hostName.getLength() >= certHostName.getLength() )
+ {
+ OUString cmpStr = certHostName.copy( 1 );
+
+ if ( hostName.matchIgnoreAsciiCase(
+ cmpStr, hostName.getLength() - cmpStr.getLength() ) )
+ return true;
+ }
+ return false;
+}
+
+/*
+
+OUString SerfSession::makeAbsoluteURL( OUString const & rURL ) const
+{
+ try
+ {
+ // Is URL relative or already absolute?
+ if ( rURL[ 0 ] != '/' )
+ {
+ // absolute.
+ return OUString( rURL );
+ }
+ else
+ {
+ ne_uri aUri;
+ memset( &aUri, 0, sizeof( aUri ) );
+
+ ne_fill_server_uri( m_pHttpSession, &aUri );
+ aUri.path
+ = ne_strdup( OUStringToOString(
+ rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+ SerfUri aSerfUri( &aUri );
+ ne_uri_free( &aUri );
+ return aSerfUri.GetURI();
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ // error.
+ return OUString();
+}
+*/
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfSession.hxx b/ucb/source/ucp/webdav/SerfSession.hxx
new file mode 100644
index 000000000..8c2a14439
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfSession.hxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFSESSION_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFSESSION_HXX
+
+#include <osl/mutex.hxx>
+#include <memory>
+#include <vector>
+#include "DAVSession.hxx"
+#include "SerfUri.hxx"
+
+#include <serf.h>
+
+namespace ucbhelper { class ProxyDecider; }
+
+namespace http_dav_ucp
+{
+
+class SerfRequestProcessor;
+
+
+// SerfSession
+// A DAVSession implementation using the neon/expat library
+
+
+class SerfSession : public DAVSession
+{
+private:
+ osl::Mutex m_aMutex;
+
+ SerfUri m_aUri;
+
+ OUString m_aProxyName;
+ sal_Int32 m_nProxyPort;
+
+ serf_connection_t* m_pSerfConnection;
+ serf_context_t* m_pSerfContext;
+ serf_bucket_alloc_t* m_pSerfBucket_Alloc;
+ bool m_bIsHeadRequestInProgress;
+ bool m_bUseChunkedEncoding;
+ sal_Int16 m_bNoOfTransferEncodingSwitches;
+
+ const ucbhelper::InternetProxyDecider & m_rProxyDecider;
+
+ DAVRequestEnvironment m_aEnv;
+
+ char* getHostinfo();
+ bool isSSLNeeded();
+
+ SerfRequestProcessor* createReqProc( const OUString & inPath );
+
+protected:
+ virtual ~SerfSession() override;
+
+public:
+ /// @throws DAVException
+ SerfSession( const rtl::Reference< DAVSessionFactory > & rSessionFactory,
+ const OUString& inUri,
+ const ucbhelper::InternetProxyDecider & rProxyDecider );
+
+ // Serf library callbacks
+ apr_status_t setupSerfConnection( apr_socket_t * inAprSocket,
+ serf_bucket_t **outSerfInputBucket,
+ serf_bucket_t **outSerfOutputBucket,
+ apr_pool_t* inAprPool );
+
+ apr_status_t provideSerfCredentials( bool bGiveProvidedCredentialsASecondTry,
+ char ** outUsername,
+ char ** outPassword,
+ serf_request_t * inRequest,
+ int inCode,
+ const char *inAuthProtocol,
+ const char *inRealm,
+ apr_pool_t *inAprPool );
+
+ apr_status_t verifySerfCertificateChain (
+ int nFailures,
+ const serf_ssl_certificate_t * const * pCertificateChainBase64Encoded,
+ int nCertificateChainLength);
+
+ serf_bucket_t* acceptSerfResponse( serf_request_t * inSerfRequest,
+ serf_bucket_t * inSerfStreamBucket,
+ apr_pool_t* inAprPool );
+
+ // Serf-related data structures
+ static apr_pool_t* getAprPool();
+ serf_bucket_alloc_t* getSerfBktAlloc();
+ serf_context_t* getSerfContext();
+ serf_connection_t* getSerfConnection();
+
+ // DAVSession methods
+ virtual bool CanUse( const OUString & inUri ) override;
+
+ virtual bool UsesProxy() override;
+
+ const DAVRequestEnvironment & getRequestEnvironment() const
+ { return m_aEnv; }
+
+ // allprop & named
+ virtual void
+ PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ const std::vector< OUString > & inPropNames,
+ std::vector< DAVResource > & ioResources,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // propnames
+ virtual void
+ PROPFIND( const OUString & inPath,
+ const Depth inDepth,
+ std::vector< DAVResourceInfo >& ioResInfo,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ PROPPATCH( const OUString & inPath,
+ const std::vector< ProppatchValue > & inValues,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ HEAD( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ bool isHeadRequestInProgress();
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream > & ioOutputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ GET( const OUString & inPath,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ GET( const OUString & inPath,
+ css::uno::Reference< css::io::XOutputStream > & ioOutputStream,
+ const std::vector< OUString > & inHeaderNames,
+ DAVResource & ioResource,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ PUT( const OUString & inPath,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual css::uno::Reference< css::io::XInputStream >
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ POST( const OUString & inPath,
+ const OUString & rContentType,
+ const OUString & rReferer,
+ const css::uno::Reference< css::io::XInputStream > & inInputStream,
+ css::uno::Reference< css::io::XOutputStream > & oOutputStream,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ MKCOL( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void
+ COPY( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite = false ) override;
+
+ virtual void
+ MOVE( const OUString & inSourceURL,
+ const OUString & inDestinationURL,
+ const DAVRequestEnvironment & rEnv,
+ bool inOverWrite = false ) override;
+
+ virtual void DESTROY( const OUString & inPath,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // set new lock.
+ virtual void LOCK( const OUString & inURL,
+ css::ucb::Lock & inLock,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // refresh existing lock.
+ virtual sal_Int64 LOCK( const OUString & inURL,
+ sal_Int64 nTimeout,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ virtual void UNLOCK( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv ) override;
+
+ // helpers
+ virtual void abort() override;
+
+ const OUString & getHostName() const { return m_aUri.GetHost(); }
+ int getPort() const { return m_aUri.GetPort(); }
+
+ bool isDomainMatch( const OUString & certHostName );
+
+private:
+ friend class SerfLockStore;
+
+ /// @throws DAVException
+ void Init();
+
+ /// @throws DAVException
+ void Init( const DAVRequestEnvironment & rEnv );
+
+ /// @throws DAVException
+ void HandleError( std::shared_ptr<SerfRequestProcessor> rReqProc );
+
+ ucbhelper::InternetProxyServer getProxySettings() const;
+
+ static bool removeExpiredLocktoken( const OUString & inURL,
+ const DAVRequestEnvironment & rEnv );
+
+ // refresh lock, called by SerfLockStore::refreshLocks
+ bool LOCK( const OUString& rLock, sal_Int32 *plastChanceToSendRefreshRequest );
+
+ // unlock, called by SerfLockStore::~SerfLockStore
+ void UNLOCK( const OUString& rLock );
+
+ // Helper: XInputStream -> Sequence< sal_Int8 >
+ static bool getDataFromInputStream(
+ const css::uno::Reference<
+ css::io::XInputStream > & xStream,
+ css::uno::Sequence< sal_Int8 > & rData,
+ bool bAppendTrailingZeroByte );
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFSESSION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.cxx b/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.cxx
new file mode 100644
index 000000000..53826ace6
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SerfUnlockReqProcImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+SerfUnlockReqProcImpl::SerfUnlockReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const OUString& sToken)
+ : SerfRequestProcessorImpl( inPath, inRequestHeaders )
+ , m_sToken( sToken )
+{
+}
+
+SerfUnlockReqProcImpl::~SerfUnlockReqProcImpl()
+{
+}
+
+serf_bucket_t * SerfUnlockReqProcImpl::createSerfRequestBucket( serf_request_t * inSerfRequest )
+{
+ // create serf request
+ serf_bucket_t *req_bkt = serf_request_bucket_request_create( inSerfRequest,
+ "UNLOCK",
+ getPathStr(),
+ nullptr,
+ serf_request_get_alloc( inSerfRequest ) );
+ // set request header fields
+ serf_bucket_t* hdrs_bkt = serf_bucket_request_get_headers( req_bkt );
+
+ // general header fields provided by caller
+ setRequestHeaders( hdrs_bkt );
+
+ // token header field
+ serf_bucket_headers_set( hdrs_bkt, "Lock-Token",
+ OUStringToOString(m_sToken, RTL_TEXTENCODING_UTF8).getStr() );
+
+ return req_bkt;
+}
+
+void SerfUnlockReqProcImpl::processChunkOfResponseData( const char* , apr_size_t )
+{
+}
+
+void SerfUnlockReqProcImpl::handleEndOfResponseData( serf_bucket_t * )
+{
+}
+
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.hxx b/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.hxx
new file mode 100644
index 000000000..afef3f28b
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfUnlockReqProcImpl.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFUNLOCKREQPROCIMPL_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFUNLOCKREQPROCIMPL_HXX
+
+#include "SerfRequestProcessorImpl.hxx"
+
+namespace http_dav_ucp
+{
+
+class SerfUnlockReqProcImpl : public SerfRequestProcessorImpl
+{
+public:
+ SerfUnlockReqProcImpl( const char* inPath,
+ const DAVRequestHeaders& inRequestHeaders,
+ const OUString& sToken);
+
+ virtual ~SerfUnlockReqProcImpl() override;
+
+ virtual serf_bucket_t *createSerfRequestBucket(
+ serf_request_t * inSerfRequest ) override;
+
+private:
+ virtual void processChunkOfResponseData(
+ const char* data, apr_size_t len ) override;
+
+ virtual void handleEndOfResponseData(
+ serf_bucket_t * inSerfResponseBucket ) override;
+
+ OUString m_sToken;
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFUNLOCKREQPROCIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfUri.cxx b/ucb/source/ucp/webdav/SerfUri.cxx
new file mode 100644
index 000000000..dab11a64c
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfUri.cxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/uri.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "SerfUri.hxx"
+#include "DAVException.hxx"
+#include "AprEnv.hxx"
+
+#include <urihelper.hxx>
+
+using namespace http_dav_ucp;
+
+
+SerfUri::SerfUri( const apr_uri_t * inUri )
+ : mAprUri( *inUri )
+ , mURI()
+ , mScheme()
+ , mUserInfo()
+ , mHostName()
+ , mPort()
+ , mPath()
+{
+ if ( inUri == nullptr )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ char * uri = apr_uri_unparse( apr_environment::AprEnv::getAprEnv()->getAprPool(), &mAprUri, 0 );
+
+ if ( uri == nullptr )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ init( &mAprUri );
+
+ calculateURI();
+}
+
+SerfUri::SerfUri( const OUString & inUri )
+ : mAprUri()
+ , mURI()
+ , mScheme()
+ , mUserInfo()
+ , mHostName()
+ , mPort()
+ , mPath()
+{
+ if ( inUri.getLength() <= 0 )
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+
+ // #i77023#
+ OUString aEscapedUri( ucb_impl::urihelper::encodeURI( inUri ) );
+
+ OString theInputUri(
+ aEscapedUri.getStr(), aEscapedUri.getLength(), RTL_TEXTENCODING_UTF8 );
+
+ if ( apr_uri_parse( apr_environment::AprEnv::getAprEnv()->getAprPool(),
+ theInputUri.getStr(), &mAprUri ) != APR_SUCCESS )
+ {
+ throw DAVException( DAVException::DAV_INVALID_ARG );
+ }
+ if ( !mAprUri.port )
+ {
+ mAprUri.port = apr_uri_port_of_scheme( mAprUri.scheme );
+ }
+ if ( !mAprUri.path )
+ {
+ mAprUri.path = const_cast<char *>("/");
+ }
+
+ init( &mAprUri );
+
+ calculateURI();
+}
+
+void SerfUri::init( const apr_uri_t * pUri )
+{
+ mScheme = OStringToOUString( pUri->scheme, RTL_TEXTENCODING_UTF8 );
+ mUserInfo = OStringToOUString( pUri->user, RTL_TEXTENCODING_UTF8 );
+ mHostName = OStringToOUString( pUri->hostname, RTL_TEXTENCODING_UTF8 );
+ mPort = pUri->port;
+ mPath = OStringToOUString( pUri->path, RTL_TEXTENCODING_UTF8 );
+
+ if ( pUri->query )
+ {
+ mPath += "?";
+ mPath += OStringToOUString( pUri->query, RTL_TEXTENCODING_UTF8 );
+ }
+
+ if ( pUri->fragment )
+ {
+ mPath += "#";
+ mPath += OStringToOUString( pUri->fragment, RTL_TEXTENCODING_UTF8 );
+ }
+}
+
+void SerfUri::calculateURI ()
+{
+ OUStringBuffer aBuf( mScheme );
+ aBuf.append( "://" );
+ if ( mUserInfo.getLength() > 0 )
+ {
+ aBuf.append( mUserInfo );
+ aBuf.append( "@" );
+ }
+ // Is host a numeric IPv6 address?
+ if ( ( mHostName.indexOf( ':' ) != -1 ) &&
+ ( mHostName[ 0 ] != '[' ) )
+ {
+ aBuf.append( "[" );
+ aBuf.append( mHostName );
+ aBuf.append( "]" );
+ }
+ else
+ {
+ aBuf.append( mHostName );
+ }
+
+ // append port, but only, if not default port.
+ bool bAppendPort = true;
+ switch ( mPort )
+ {
+ case DEFAULT_HTTP_PORT:
+ bAppendPort = (mScheme != "http");
+ break;
+
+ case DEFAULT_HTTPS_PORT:
+ bAppendPort = (mScheme != "https");
+ break;
+ }
+ if ( bAppendPort )
+ {
+ aBuf.append( ":" );
+ aBuf.append( OUString::number( mPort ) );
+ }
+ aBuf.append( mPath );
+
+ mURI = aBuf.makeStringAndClear();
+}
+
+OUString SerfUri::GetPathBaseName () const
+{
+ sal_Int32 nPos = mPath.lastIndexOf ('/');
+ sal_Int32 nTrail = 0;
+ if (nPos == mPath.getLength () - 1)
+ {
+ // Trailing slash found. Skip.
+ nTrail = 1;
+ nPos = mPath.lastIndexOf ('/', nPos);
+ }
+ if (nPos != -1)
+ {
+ OUString aTemp(
+ mPath.copy (nPos + 1, mPath.getLength () - nPos - 1 - nTrail) );
+
+ // query, fragment present?
+ nPos = aTemp.indexOf( '?' );
+ if ( nPos == -1 )
+ nPos = aTemp.indexOf( '#' );
+
+ if ( nPos != -1 )
+ aTemp = aTemp.copy( 0, nPos );
+
+ return aTemp;
+ }
+ else
+ return "/";
+}
+
+bool SerfUri::operator== ( const SerfUri & rOther ) const
+{
+ return ( mURI == rOther.mURI );
+}
+
+OUString SerfUri::GetPathBaseNameUnescaped () const
+{
+ return unescape( GetPathBaseName() );
+}
+
+void SerfUri::AppendPath (const OUString& rPath)
+{
+ if (mPath.lastIndexOf ('/') != mPath.getLength () - 1)
+ mPath += "/";
+
+ mPath += rPath;
+ calculateURI ();
+};
+
+// static
+OUString SerfUri::escapeSegment( const OUString& segment )
+{
+ return rtl::Uri::encode( segment,
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+}
+
+// static
+OUString SerfUri::unescape( const OUString& segment )
+{
+ return rtl::Uri::decode( segment,
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 );
+}
+
+// static
+OUString SerfUri::makeConnectionEndPointString(
+ const OUString & rHostName, int nPort )
+{
+ OUStringBuffer aBuf;
+
+ // Is host a numeric IPv6 address?
+ if ( ( rHostName.indexOf( ':' ) != -1 ) &&
+ ( rHostName[ 0 ] != '[' ) )
+ {
+ aBuf.append( "[" );
+ aBuf.append( rHostName );
+ aBuf.append( "]" );
+ }
+ else
+ {
+ aBuf.append( rHostName );
+ }
+
+ if ( ( nPort != DEFAULT_HTTP_PORT ) && ( nPort != DEFAULT_HTTPS_PORT ) )
+ {
+ aBuf.append( ":" );
+ aBuf.append( OUString::number( sal_Int32( nPort ) ) );
+ }
+ return aBuf.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/SerfUri.hxx b/ucb/source/ucp/webdav/SerfUri.hxx
new file mode 100644
index 000000000..f47b2892e
--- /dev/null
+++ b/ucb/source/ucp/webdav/SerfUri.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFURI_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFURI_HXX
+
+#include <apr_uri.h>
+#include <rtl/ustring.hxx>
+#include "DAVException.hxx"
+
+namespace http_dav_ucp
+{
+
+#define DEFAULT_HTTP_PORT 80
+#define DEFAULT_HTTPS_PORT 443
+
+
+// SerfUri
+// A URI implementation for use with the neon/expat library
+
+class SerfUri
+{
+ private:
+ apr_uri_t mAprUri;
+ OUString mURI;
+ OUString mScheme;
+ OUString mUserInfo;
+ OUString mHostName;
+ sal_Int32 mPort;
+ OUString mPath;
+
+ void init( const apr_uri_t * pUri );
+ void calculateURI ();
+
+ public:
+ /// @throws DAVException
+ explicit SerfUri( const OUString & inUri );
+ /// @throws DAVException
+ explicit SerfUri( const apr_uri_t * inUri );
+
+ bool operator== ( const SerfUri & rOther ) const;
+ bool operator!= ( const SerfUri & rOther ) const
+ { return !operator==( rOther ); }
+
+ apr_uri_t& getAprUri()
+ {
+ return mAprUri;
+ }
+ const OUString & GetURI() const
+ { return mURI; };
+ const OUString & GetScheme() const
+ { return mScheme; };
+ const OUString & GetUserInfo() const
+ { return mUserInfo; };
+ const OUString & GetHost() const
+ { return mHostName; };
+ sal_Int32 GetPort() const
+ { return mPort; };
+ const OUString & GetPath() const
+ { return mPath; };
+
+ OUString GetPathBaseName() const;
+
+ OUString GetPathBaseNameUnescaped() const;
+
+ void SetScheme (const OUString& scheme)
+ { mScheme = scheme; calculateURI (); };
+
+ void AppendPath (const OUString& rPath);
+
+ static OUString escapeSegment( const OUString& segment );
+ static OUString unescape( const OUString& string );
+
+ // "host:port", omit ":port" for port 80 and 443
+ static OUString makeConnectionEndPointString(
+ const OUString & rHostName,
+ int nPort );
+ OUString makeConnectionEndPointString() const
+ { return makeConnectionEndPointString( GetHost(), GetPort() ); }
+};
+
+} // namespace http_dav_ucp
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_SERFURI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/UCBDeadPropertyValue.cxx b/ucb/source/ucp/webdav/UCBDeadPropertyValue.cxx
new file mode 100644
index 000000000..0f3543012
--- /dev/null
+++ b/ucb/source/ucp/webdav/UCBDeadPropertyValue.cxx
@@ -0,0 +1,526 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include "UCBDeadPropertyValue.hxx"
+
+using namespace http_dav_ucp;
+using namespace ::com::sun::star;
+
+
+// static
+const OUString UCBDeadPropertyValue::aTypeString
+ = "string";
+const OUString UCBDeadPropertyValue::aTypeLong
+ = "long";
+const OUString UCBDeadPropertyValue::aTypeShort
+ = "short";
+const OUString UCBDeadPropertyValue::aTypeBoolean
+ = "boolean";
+const OUString UCBDeadPropertyValue::aTypeChar
+ = "char";
+const OUString UCBDeadPropertyValue::aTypeByte
+ = "byte";
+const OUString UCBDeadPropertyValue::aTypeHyper
+ = "hyper";
+const OUString UCBDeadPropertyValue::aTypeFloat
+ = "float";
+const OUString UCBDeadPropertyValue::aTypeDouble
+ = "double";
+
+// static
+const OUString UCBDeadPropertyValue::aXMLPre
+ = "<ucbprop><type>";
+const OUString UCBDeadPropertyValue::aXMLMid
+ = "</type><value>";
+const OUString UCBDeadPropertyValue::aXMLEnd
+ = "</value></ucbprop>";
+
+/*
+
+#define STATE_TOP (1)
+
+#define STATE_UCBPROP (STATE_TOP)
+#define STATE_TYPE (STATE_TOP + 1)
+#define STATE_VALUE (STATE_TOP + 2)
+
+extern "C" int UCBDeadPropertyValue_startelement_callback(
+ void *,
+ int parent,
+ const char * nspace,
+ const char *name,
+ const char ** )
+{
+ if ( name != 0 )
+ {
+ switch ( parent )
+ {
+ case NE_XML_STATEROOT:
+ if ( strcmp( name, "ucbprop" ) == 0 )
+ return STATE_UCBPROP;
+ break;
+
+ case STATE_UCBPROP:
+ if ( strcmp( name, "type" ) == 0 )
+ return STATE_TYPE;
+ else if ( strcmp( name, "value" ) == 0 )
+ return STATE_VALUE;
+ break;
+ }
+ }
+ return NE_XML_DECLINE;
+}
+
+
+extern "C" int UCBDeadPropertyValue_chardata_callback(
+ void *userdata,
+ int state,
+ const char *buf,
+ size_t len )
+{
+ UCBDeadPropertyValueParseContext * pCtx
+ = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
+
+ switch ( state )
+ {
+ case STATE_TYPE:
+ SAL_WARN_IF( pCtx->pType, "ucb.ucp.webdav",
+ "UCBDeadPropertyValue_endelement_callback - "
+ "Type already set!" );
+ pCtx->pType
+ = new OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ break;
+
+ case STATE_VALUE:
+ SAL_WARN_IF( pCtx->pValue, "ucb.ucp.webdav",
+ "UCBDeadPropertyValue_endelement_callback - "
+ "Value already set!" );
+ pCtx->pValue
+ = new OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+
+
+extern "C" int UCBDeadPropertyValue_endelement_callback(
+ void *userdata,
+ int state,
+ const char *,
+ const char * )
+{
+ UCBDeadPropertyValueParseContext * pCtx
+ = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
+
+ switch ( state )
+ {
+ case STATE_TYPE:
+ if ( !pCtx->pType )
+ return 1; // abort
+ break;
+
+ case STATE_VALUE:
+ if ( !pCtx->pValue )
+ return 1; // abort
+ break;
+
+ case STATE_UCBPROP:
+ if ( !pCtx->pType || ! pCtx->pValue )
+ return 1; // abort
+ break;
+ }
+ return 0; // zero to continue, non-zero to abort parsing
+}
+*/
+
+
+static OUString encodeValue( const OUString & rValue )
+{
+ // Note: I do not use the usual &amp; + &lt; + &gt; encoding, because
+ // I want to prevent any XML parser from trying to 'understand'
+ // the value. This caused problems:
+
+ // Example:
+ // - Unencoded property value: x<z
+ // PROPPATCH:
+ // - Encoded property value: x&lt;z
+ // - UCBDeadPropertyValue::toXML result:
+ // <ucbprop><type>string</type><value>x&lt;z</value></ucbprop>
+ // PROPFIND:
+ // - parser replaces &lt; by > ==> error (not well formed)
+
+ OUStringBuffer aResult;
+ const sal_Unicode * pValue = rValue.getStr();
+
+ sal_Int32 nCount = rValue.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const sal_Unicode c = pValue[ n ];
+
+ if ( '%' == c )
+ aResult.append( "%per;" );
+ else if ( '<' == c )
+ aResult.append( "%lt;" );
+ else if ( '>' == c )
+ aResult.append( "%gt;" );
+ else
+ aResult.append( c );
+ }
+ return aResult.makeStringAndClear();
+}
+
+/*
+
+static OUString decodeValue( const OUString & rValue )
+{
+ OUStringBuffer aResult;
+ const sal_Unicode * pValue = rValue.getStr();
+
+ sal_Int32 nPos = 0;
+ sal_Int32 nEnd = rValue.getLength();
+
+ while ( nPos < nEnd )
+ {
+ sal_Unicode c = pValue[ nPos ];
+
+ if ( '%' == c )
+ {
+ nPos++;
+
+ if ( nPos == nEnd )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+
+ c = pValue[ nPos ];
+
+ if ( 'p' == c )
+ {
+ // %per;
+
+ if ( nPos > nEnd - 4 )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 'e' == pValue[ nPos + 1 ] )
+ &&
+ ( 'r' == pValue[ nPos + 2 ] )
+ &&
+ ( ';' == pValue[ nPos + 3 ] ) )
+ {
+ aResult.append( '%' );
+ nPos += 3;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+ }
+ else if ( 'l' == c )
+ {
+ // %lt;
+
+ if ( nPos > nEnd - 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 't' == pValue[ nPos + 1 ] )
+ &&
+ ( ';' == pValue[ nPos + 2 ] ) )
+ {
+ aResult.append( '<' );
+ nPos += 2;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+ }
+ else if ( 'g' == c )
+ {
+ // %gt;
+
+ if ( nPos > nEnd - 3 )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+
+ if ( ( 't' == pValue[ nPos + 1 ] )
+ &&
+ ( ';' == pValue[ nPos + 2 ] ) )
+ {
+ aResult.append( '>' );
+ nPos += 2;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::decodeValue - syntax error!" );
+ return OUString();
+ }
+ }
+ else
+ aResult.append( c );
+
+ nPos++;
+ }
+
+ return OUString( aResult );
+}
+*/
+
+
+// static
+bool UCBDeadPropertyValue::supportsType( const uno::Type & rType )
+{
+ if ( ( rType != cppu::UnoType<OUString>::get() )
+ &&
+ ( rType != cppu::UnoType<sal_Int32>::get() )
+ &&
+ ( rType != cppu::UnoType<sal_Int16>::get() )
+ &&
+ ( rType != cppu::UnoType<bool>::get() )
+ &&
+ ( rType != cppu::UnoType<cppu::UnoCharType>::get() )
+ &&
+ ( rType != cppu::UnoType<sal_Int8>::get() )
+ &&
+ ( rType != cppu::UnoType<sal_Int64>::get() )
+ &&
+ ( rType != cppu::UnoType<float>::get() )
+ &&
+ ( rType != cppu::UnoType<double>::get() ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+// static
+bool UCBDeadPropertyValue::createFromXML( const OString & /*rInData*/,
+ uno::Any & /*rOutData*/ )
+{
+ bool success = false;
+
+ /*
+ ne_xml_parser * parser = ne_xml_create();
+ if ( parser )
+ {
+ UCBDeadPropertyValueParseContext aCtx;
+ ne_xml_push_handler( parser,
+ UCBDeadPropertyValue_startelement_callback,
+ UCBDeadPropertyValue_chardata_callback,
+ UCBDeadPropertyValue_endelement_callback,
+ &aCtx );
+
+ ne_xml_parse( parser, rInData.getStr(), rInData.getLength() );
+
+ success = !ne_xml_failed( parser );
+
+ ne_xml_destroy( parser );
+
+ if ( success )
+ {
+ if ( aCtx.pType && aCtx.pValue )
+ {
+ // Decode aCtx.pValue! It may contain XML reserved chars.
+ OUString aStringValue = decodeValue( *aCtx.pValue );
+ if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeString ) )
+ {
+ rOutData <<= aStringValue;
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeLong ) )
+ {
+ rOutData <<= aStringValue.toInt32();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeShort ) )
+ {
+ rOutData <<= sal_Int16( aStringValue.toInt32() );
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeBoolean ) )
+ {
+ if ( aStringValue.equalsIgnoreAsciiCase(
+ OUString( "true" ) ) )
+ rOutData <<= sal_Bool( sal_True );
+ else
+ rOutData <<= sal_Bool( sal_False );
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeChar ) )
+ {
+ rOutData <<= aStringValue.toChar();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeByte ) )
+ {
+ rOutData <<= sal_Int8( aStringValue.toChar() );
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeHyper ) )
+ {
+ rOutData <<= aStringValue.toInt64();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeFloat ) )
+ {
+ rOutData <<= aStringValue.toFloat();
+ }
+ else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeDouble ) )
+ {
+ rOutData <<= aStringValue.toDouble();
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::createFromXML - "
+ "Unsupported property type!" );
+ success = false;
+ }
+ }
+ else
+ success = false;
+ }
+ }
+ */
+ return success;
+}
+
+
+// static
+bool UCBDeadPropertyValue::toXML( const uno::Any & rInData,
+ OUString & rOutData )
+{
+ // <ucbprop><type>the_type</type><value>the_value</value></ucbprop>
+
+ // Check property type. Extract type and value as string.
+
+ const uno::Type& rType = rInData.getValueType();
+ OUString aStringValue;
+ OUString aStringType;
+
+ if ( rType == cppu::UnoType<OUString>::get() )
+ {
+ // string
+ rInData >>= aStringValue;
+ aStringType = aTypeString;
+ }
+ else if ( rType == cppu::UnoType<sal_Int32>::get() )
+ {
+ // long
+ sal_Int32 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeLong;
+ }
+ else if ( rType == cppu::UnoType<sal_Int16>::get() )
+ {
+ // short
+ sal_Int32 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeShort;
+ }
+ else if ( rType == cppu::UnoType<bool>::get() )
+ {
+ // boolean
+ bool bValue = false;
+ rInData >>= bValue;
+ aStringValue = OUString::boolean( bValue );
+ aStringType = aTypeBoolean;
+ }
+ else if ( rType == cppu::UnoType<cppu::UnoCharType>::get() )
+ {
+ // char
+ sal_Unicode cValue = 0;
+ rInData >>= cValue;
+ aStringValue = OUString( cValue );
+ aStringType = aTypeChar;
+ }
+ else if ( rType == cppu::UnoType<sal_Int8>::get() )
+ {
+ // byte
+ sal_Int8 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString( sal_Unicode( nValue ) );
+ aStringType = aTypeByte;
+ }
+ else if ( rType == cppu::UnoType<sal_Int64>::get() )
+ {
+ // hyper
+ sal_Int64 nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeHyper;
+ }
+ else if ( rType == cppu::UnoType<float>::get() )
+ {
+ // float
+ float nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeFloat;
+ }
+ else if ( rType == cppu::UnoType<double>::get() )
+ {
+ // double
+ double nValue = 0;
+ rInData >>= nValue;
+ aStringValue = OUString::number( nValue );
+ aStringType = aTypeDouble;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "UCBDeadPropertyValue::toXML - "
+ "Unsupported property type!" );
+ return false;
+ }
+
+ // Encode value! It must not contain XML reserved chars!
+ aStringValue = encodeValue( aStringValue );
+
+ rOutData = aXMLPre;
+ rOutData += aStringType;
+ rOutData += aXMLMid;
+ rOutData += aStringValue;
+ rOutData += aXMLEnd;
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/UCBDeadPropertyValue.hxx b/ucb/source/ucp/webdav/UCBDeadPropertyValue.hxx
new file mode 100644
index 000000000..dc066f48a
--- /dev/null
+++ b/ucb/source/ucp/webdav/UCBDeadPropertyValue.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_UCBDEADPROPERTYVALUE_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_UCBDEADPROPERTYVALUE_HXX
+
+#include <rtl/string.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace http_dav_ucp
+{
+
+class UCBDeadPropertyValue
+{
+private:
+ static const OUString aTypeString;
+ static const OUString aTypeLong;
+ static const OUString aTypeShort;
+ static const OUString aTypeBoolean;
+ static const OUString aTypeChar;
+ static const OUString aTypeByte;
+ static const OUString aTypeHyper;
+ static const OUString aTypeFloat;
+ static const OUString aTypeDouble;
+
+ static const OUString aXMLPre;
+ static const OUString aXMLMid;
+ static const OUString aXMLEnd;
+
+public:
+ static bool supportsType( const css::uno::Type & rType );
+
+ static bool createFromXML( const OString & rInData,
+ css::uno::Any & rOutData );
+ static bool toXML( const css::uno::Any & rInData,
+ OUString & rOutData );
+};
+
+}
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_UCBDEADPROPERTYVALUE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/ucpdav1.component b/ucb/source/ucp/webdav/ucpdav1.component
new file mode 100644
index 000000000..50a3d87b2
--- /dev/null
+++ b/ucb/source/ucp/webdav/ucpdav1.component
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+
+
+<component loader="com.sun.star.loader.SharedLibrary"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.WebDAVContentProvider">
+ <service name="com.sun.star.ucb.WebDAVContentProvider"/>
+ </implementation>
+</component>
diff --git a/ucb/source/ucp/webdav/webdavcontent.cxx b/ucb/source/ucp/webdav/webdavcontent.cxx
new file mode 100644
index 000000000..6c0198199
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavcontent.cxx
@@ -0,0 +1,3292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <cppuhelper/queryinterface.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/macros.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/simpleinteractionrequest.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertySetInfoChange.hpp>
+#include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandEnvironment.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "webdavcontent.hxx"
+#include "webdavprovider.hxx"
+#include "webdavresultset.hxx"
+#include "ContentProperties.hxx"
+#include "SerfUri.hxx"
+#include "UCBDeadPropertyValue.hxx"
+#include "DAVException.hxx"
+#include "DAVProperties.hxx"
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+namespace
+{
+void lcl_sendPartialGETRequest( bool &bError,
+ DAVException &aLastException,
+ const std::vector< OUString >& rProps,
+ std::vector< OUString > &aHeaderNames,
+ const std::unique_ptr< DAVResourceAccess > &xResAccess,
+ std::unique_ptr< ContentProperties > &xProps,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ DAVResource aResource;
+ DAVRequestHeaders aPartialGet;
+ aPartialGet.push_back(
+ DAVRequestHeader(
+ OUString( "Range" ),
+ OUString( "bytes=0-0" )));
+
+ bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
+ [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
+
+ if ( bIsRequestSize )
+ {
+ // we need to know if the server accepts range requests for a resource
+ // and the range unit it uses
+ aHeaderNames.push_back( OUString( "Accept-Ranges" ) );
+ aHeaderNames.push_back( OUString( "Content-Range" ) );
+ }
+ try
+ {
+ uno::Reference< io::XInputStream > xIn = xResAccess->GET( aPartialGet,
+ aHeaderNames,
+ aResource,
+ xEnv );
+ bError = false;
+
+ if ( bIsRequestSize )
+ {
+ // the ContentProperties maps "Content-Length" to the UCB "Size" property
+ // This would have an unrealistic value of 1 byte because we did only a partial GET
+ // Solution: if "Content-Range" is present, map it with UCB "Size" property
+ OUString aAcceptRanges, aContentRange, aContentLength;
+ std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
+ for ( const auto& rResponseProp : aResponseProps )
+ {
+ if ( rResponseProp.Name == "Accept-Ranges" )
+ rResponseProp.Value >>= aAcceptRanges;
+ else if ( rResponseProp.Name == "Content-Range" )
+ rResponseProp.Value >>= aContentRange;
+ else if ( rResponseProp.Name == "Content-Length" )
+ rResponseProp.Value >>= aContentLength;
+ }
+
+ sal_Int64 nSize = 1;
+ if ( aContentLength.getLength() )
+ {
+ nSize = aContentLength.toInt64();
+ }
+
+ // according to http://tools.ietf.org/html/rfc2616#section-3.12
+ // the only range unit defined is "bytes" and implementations
+ // MAY ignore ranges specified using other units.
+ if ( nSize == 1 &&
+ aContentRange.getLength() &&
+ aAcceptRanges == "bytes" )
+ {
+ // Parse the Content-Range to get the size
+ // vid. http://tools.ietf.org/html/rfc2616#section-14.16
+ // Content-Range: <range unit> <bytes range>/<size>
+ sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
+ if ( nSlash != -1 )
+ {
+ OUString aSize = aContentRange.copy( nSlash + 1 );
+ // "*" means that the instance-length is unknown at the time when the response was generated
+ if ( aSize != "*" )
+ {
+ auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
+ [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
+ if (it != aResponseProps.end())
+ {
+ it->Value <<= aSize;
+ }
+ }
+ }
+ }
+ }
+
+ if ( xProps.get() )
+ xProps->addProperties(
+ rProps,
+ ContentProperties( aResource ) );
+ else
+ xProps.reset ( new ContentProperties( aResource ) );
+ }
+ catch ( DAVException const & ex )
+ {
+ aLastException = ex;
+ }
+}
+}
+
+
+// Content Implementation.
+
+
+// ctr for content on an existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( false ),
+ m_bLocked( false ),
+ m_bCollection( false ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext,
+ rSessionFactory,
+ Identifier->getContentIdentifier() ) );
+
+ SerfUri aURI( Identifier->getContentIdentifier() );
+ m_aEscapedTitle = aURI.GetPathBaseName();
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+}
+
+
+// ctr for content on a non-existing webdav resource
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ bool isCollection )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_eResourceType( UNKNOWN ),
+ m_pProvider( pProvider ),
+ m_bTransient( true ),
+ m_bLocked( false ),
+ m_bCollection( isCollection ),
+ m_bDidGetOrHead( false )
+{
+ try
+ {
+ m_xResAccess.reset( new DAVResourceAccess(
+ rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
+ }
+ catch ( DAVException const & )
+ {
+ throw ucb::ContentCreationException();
+ }
+
+ // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
+}
+
+
+// virtual
+Content::~Content()
+{
+ if (m_bLocked)
+ unlock(uno::Reference< ucb::XCommandEnvironment >());
+}
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL Content::acquire()
+ throw( )
+{
+ ContentImplHelper::acquire();
+}
+
+
+// virtual
+void SAL_CALL Content::release()
+ throw( )
+{
+ ContentImplHelper::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
+{
+ // Note: isFolder may require network activities! So call it only
+ // if it is really necessary!!!
+ uno::Any aRet = cppu::queryInterface(
+ rType,
+ static_cast< ucb::XContentCreator * >( this ) );
+ if ( aRet.hasValue() )
+ {
+ try
+ {
+ uno::Reference< beans::XPropertySet > const xProps(
+ m_xContext, uno::UNO_QUERY_THROW );
+ uno::Reference< uno::XComponentContext > xCtx;
+ xCtx.set( xProps->getPropertyValue( "DefaultContext" ),
+ uno::UNO_QUERY_THROW );
+
+ uno::Reference< task::XInteractionHandler > xIH(
+ task::PasswordContainerInteractionHandler::create( xCtx ) );
+
+ // Supply a command env to isFolder() that contains an interaction
+ // handler that uses the password container service to obtain
+ // credentials without displaying a password gui.
+
+ uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
+ ucb::CommandEnvironment::create(
+ xCtx,
+ xIH,
+ uno::Reference< ucb::XProgressHandler >() ) );
+
+ return isFolder( xCmdEnv ) ? aRet : uno::Any();
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ return uno::Any();
+ }
+ }
+ return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( Content );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ {
+ static cppu::OTypeCollection s_aFolderTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ),
+ CPPU_TYPE_REF( ucb::XContentCreator ) );
+
+ return s_aFolderTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aDocumentTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+
+ return s_aDocumentTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL Content::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.WebDAVContent";
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSNS { WEBDAV_CONTENT_SERVICE_NAME };
+ return aSNS;
+}
+
+
+// XContent methods.
+
+
+// virtual
+OUString SAL_CALL Content::getContentType()
+{
+ bool bFolder = false;
+ try
+ {
+ bFolder
+ = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+
+ if ( bFolder )
+ return WEBDAV_COLLECTION_TYPE;
+
+ return WEBDAV_CONTENT_TYPE;
+}
+
+
+// XCommandProcessor methods.
+
+
+// virtual
+uno::Any SAL_CALL Content::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ SAL_INFO("ucb.ucp.webdav", ">>>>> Content::execute: start: command: " << aCommand.Name
+ << ", env: " << (Environment.is() ? "present" : "missing") );
+
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+
+ // getPropertyValues
+
+
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties, Environment );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+
+ // setPropertyValues
+
+
+ uno::Sequence< beans::PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !aProperties.getLength() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "No properties!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties, Environment );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+
+ // getPropertySetInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getPropertySetInfo( Environment,
+ false /* don't cache data */ );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+
+ // getCommandInfo
+
+
+ // Note: Implemented by base class.
+ aRet <<= getCommandInfo( Environment, false );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+
+ // open
+
+
+ ucb::OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet = open( aOpenCommand, Environment );
+
+ if ( (aOpenCommand.Mode == ucb::OpenMode::DOCUMENT ||
+ aOpenCommand.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE) &&
+ supportsExclusiveWriteLock( Environment ) )
+ lock( Environment );
+ }
+ else if ( aCommand.Name == "insert" )
+ {
+
+ // insert
+
+
+ ucb::InsertCommandArgument arg;
+ if ( !( aCommand.Argument >>= arg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ insert( arg.Data, arg.ReplaceExisting, Environment );
+ }
+ else if ( aCommand.Name == "delete" )
+ {
+
+ // delete
+
+
+ bool bDeletePhysical = false;
+ aCommand.Argument >>= bDeletePhysical;
+
+// KSO: Ignore parameter and destroy the content, if you don't support
+// putting objects into trashcan. ( Since we do not have a trash can
+// service yet (src603), you actually have no other choice. )
+// if ( bDeletePhysical )
+// {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ xResAccess->DESTROY( Environment );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+// }
+
+ // Propagate destruction.
+ destroy( bDeletePhysical );
+
+ // Remove own and all children's Additional Core Properties.
+ removeAdditionalPropertySet();
+ }
+ else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
+ {
+
+ // transfer
+ // ( Not available at documents )
+
+
+ ucb::TransferInfo transferArgs;
+ if ( !( aCommand.Argument >>= transferArgs ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ transfer( transferArgs, Environment );
+ }
+ else if ( aCommand.Name == "post" )
+ {
+
+ // post
+
+
+ ucb::PostCommandArgument2 aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ post( aArg, Environment );
+ }
+ else if ( aCommand.Name == "lock" &&
+ supportsExclusiveWriteLock( Environment ) )
+ {
+
+ // lock
+
+
+ lock( Environment );
+ }
+ else if ( aCommand.Name == "unlock" &&
+ supportsExclusiveWriteLock( Environment ) )
+ {
+
+ // unlock
+
+
+ unlock( Environment );
+ }
+ else if ( aCommand.Name == "createNewContent" &&
+ isFolder( Environment ) )
+ {
+
+ // createNewContent
+
+
+ ucb::ContentInfo aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= createNewContent( aArg );
+ }
+ else if ( aCommand.Name == "addProperty" )
+ {
+ ucb::PropertyCommandArgument aPropArg;
+ if ( !( aCommand.Argument >>= aPropArg ))
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in addProperty
+ try
+ {
+ addProperty( aPropArg, Environment );
+ }
+ catch ( const beans::PropertyExistException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch ( const beans::IllegalTypeException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch ( const lang::IllegalArgumentException&e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ }
+ else if ( aCommand.Name == "removeProperty" )
+ {
+ OUString sPropName;
+ if ( !( aCommand.Argument >>= sPropName ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ }
+
+ // TODO when/if XPropertyContainer is removed,
+ // the command execution can be canceled in removeProperty
+ try
+ {
+ removeProperty( sPropName, Environment );
+ }
+ catch( const beans::UnknownPropertyException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ catch( const beans::NotRemoveableException &e )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny( e ), Environment );
+ }
+ }
+ else
+ {
+
+ // Unsupported command
+
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::UnsupportedCommandException(
+ aCommand.Name,
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ SAL_INFO("ucb.ucp.webdav", "<<<<< Content::execute: end: command: " << aCommand.Name);
+ return aRet;
+}
+
+
+// virtual
+void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
+{
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ DAVResourceAccess::abort();
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // abort failed!
+ }
+}
+
+
+// XPropertyContainer methods.
+
+
+void Content::addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+// if ( m_bTransient )
+// @@@ ???
+ const beans::Property aProperty = aCmdArg.Property;
+ const uno::Any aDefaultValue = aCmdArg.DefaultValue;
+
+ // check property Name
+ if ( !aProperty.Name.getLength() )
+ throw lang::IllegalArgumentException(
+ "\"addProperty\" with empty Property.Name",
+ static_cast< ::cppu::OWeakObject * >( this ),
+ -1 );
+
+ // Check property type.
+ if ( !UCBDeadPropertyValue::supportsType( aProperty.Type ) )
+ throw beans::IllegalTypeException(
+ "\"addProperty\" unsupported Property.Type",
+ static_cast< ::cppu::OWeakObject * >( this ) );
+
+ // check default value
+ if ( aDefaultValue.hasValue() && aDefaultValue.getValueType() != aProperty.Type )
+ throw beans::IllegalTypeException(
+ "\"addProperty\" DefaultValue does not match Property.Type",
+ static_cast< ::cppu::OWeakObject * >( this ) );
+
+
+ // Make sure a property with the requested name does not already
+ // exist in dynamic and static(!) properties.
+
+
+ // Take into account special properties with custom namespace
+ // using <prop:the_propname xmlns:prop="the_namespace">
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty( aProperty.Name, aSpecialName );
+
+ // Note: This requires network access!
+ if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
+ ->hasPropertyByName( bIsSpecial ? aSpecialName : aProperty.Name ) )
+ {
+ // Property does already exist.
+ throw beans::PropertyExistException();
+ }
+
+
+ // Add a new dynamic property.
+
+
+ ProppatchValue aValue( PROPSET, aProperty.Name, aDefaultValue );
+
+ std::vector< ProppatchValue > aProppatchValues;
+ aProppatchValues.push_back( aValue );
+
+ try
+ {
+ // Set property value at server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ bIsSpecial ? aSpecialName : aProperty.Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_INSERTED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Store property locally.
+ ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
+ aProperty.Attributes,
+ aDefaultValue );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ const ResourceType & rType = getResourceType( xEnv );
+ switch ( rType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw lang::IllegalArgumentException();
+
+ case NON_DAV:
+ // Store property locally.
+ ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
+ aProperty.Attributes,
+ aDefaultValue );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::addProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ }
+}
+
+void Content::removeProperty( const OUString& Name,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+#if 0
+ // @@@ REMOVABLE at the moment not properly set in the PropSetInfo
+ try
+ {
+ beans::Property aProp
+ = getPropertySetInfo( xEnv, false /* don't cache data */ )
+ ->getPropertyByName( Name );
+
+ if ( !( aProp.Attributes & beans::PropertyAttribute::REMOVABLE ) )
+ {
+ // Not removable!
+ throw beans::NotRemoveableException();
+ }
+ }
+ catch ( beans::UnknownPropertyException const & )
+ {
+ //SAL_WARN( "ucb.ucp.webdav", "removeProperty - Unknown property!" );
+ throw;
+ }
+#endif
+
+ // Try to remove property from server.
+ try
+ {
+ std::vector< ProppatchValue > aProppatchValues;
+ ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
+ aProppatchValues.push_back( aValue );
+
+ // Remove property value from server.
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Notify propertyset info change listeners.
+ beans::PropertySetInfoChangeEvent evt(
+ static_cast< cppu::OWeakObject * >( this ),
+ Name,
+ -1, // No handle available
+ beans::PropertySetInfoChange::PROPERTY_REMOVED );
+ notifyPropertySetInfoChange( evt );
+ }
+ catch ( DAVException const & e )
+ {
+ if ( e.getStatus() == SC_FORBIDDEN )
+ {
+ // Support for setting arbitrary dead properties is optional!
+
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ }
+ else
+ {
+ if ( shouldAccessNetworkAfterException( e ) )
+ {
+ try
+ {
+ const ResourceType & rType = getResourceType( xEnv );
+ switch ( rType )
+ {
+ case UNKNOWN:
+ case DAV:
+ throw beans::UnknownPropertyException(Name);
+
+ case NON_DAV:
+ // Try to remove property from local store.
+ ContentImplHelper::removeProperty( Name );
+ break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unsupported resource type!" );
+ break;
+ }
+ }
+ catch ( uno::Exception const & )
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unable to determine resource type!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::removeProperty - "
+ "Unable to determine resource type!" );
+// throw beans::UnknownPropertyException();
+ }
+ }
+ }
+}
+
+// virtual
+void SAL_CALL Content::addProperty( const OUString& Name,
+ sal_Int16 Attributes,
+ const uno::Any& DefaultValue )
+{
+ beans::Property aProperty;
+ aProperty.Name = Name;
+ aProperty.Type = DefaultValue.getValueType();
+ aProperty.Attributes = Attributes;
+ aProperty.Handle = -1;
+
+ addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
+ uno::Reference< ucb::XCommandEnvironment >());
+}
+
+// virtual
+void SAL_CALL Content::removeProperty( const OUString& Name )
+{
+ removeProperty( Name,
+ uno::Reference< ucb::XCommandEnvironment >() );
+}
+
+
+// XContentCreator methods.
+
+
+// virtual
+uno::Sequence< ucb::ContentInfo > SAL_CALL
+Content::queryCreatableContentsInfo()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< ucb::ContentInfo > aSeq( 2 );
+
+ // document.
+ aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes
+ = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
+ | ucb::ContentInfoAttribute::KIND_DOCUMENT;
+
+ beans::Property aProp;
+ m_pProvider->getProperty( "Title", aProp );
+
+ uno::Sequence< beans::Property > aDocProps( 1 );
+ aDocProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 0 ].Properties = aDocProps;
+
+ // folder.
+ aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
+ aSeq.getArray()[ 1 ].Attributes
+ = ucb::ContentInfoAttribute::KIND_FOLDER;
+
+ uno::Sequence< beans::Property > aFolderProps( 1 );
+ aFolderProps.getArray()[ 0 ] = aProp;
+ aSeq.getArray()[ 1 ].Properties = aFolderProps;
+ return aSeq;
+}
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+Content::createNewContent( const ucb::ContentInfo& Info )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( !Info.Type.getLength() )
+ return uno::Reference< ucb::XContent >();
+
+ if ( ( Info.Type != WEBDAV_COLLECTION_TYPE )
+ &&
+ ( Info.Type != WEBDAV_CONTENT_TYPE ) )
+ return uno::Reference< ucb::XContent >();
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ SAL_WARN_IF( aURL.isEmpty(), "ucb.ucp.webdav",
+ "WebdavContent::createNewContent - empty identifier!" );
+
+ if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
+ aURL += "/";
+
+ bool isCollection;
+ if ( Info.Type == WEBDAV_COLLECTION_TYPE )
+ {
+ aURL += "New_Collection";
+ isCollection = true;
+ }
+ else
+ {
+ aURL += "New_Content";
+ isCollection = false;
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId(
+ new ::ucbhelper::ContentIdentifier( aURL ) );
+
+ // create the local content
+ try
+ {
+ return new ::http_dav_ucp::Content( m_xContext,
+ m_pProvider,
+ xId,
+ m_xResAccess->getSessionFactory(),
+ isCollection );
+ }
+ catch ( ucb::ContentCreationException & )
+ {
+ return uno::Reference< ucb::XContent >();
+ }
+}
+
+
+// virtual
+OUString Content::getParentURL()
+{
+ // <scheme>:// -> ""
+ // <scheme>://foo -> ""
+ // <scheme>://foo/ -> ""
+ // <scheme>://foo/bar -> <scheme>://foo/
+ // <scheme>://foo/bar/ -> <scheme>://foo/
+ // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ sal_Int32 nPos = aURL.lastIndexOf( '/' );
+ if ( nPos == ( aURL.getLength() - 1 ) )
+ {
+ // Trailing slash found. Skip.
+ nPos = aURL.lastIndexOf( '/', nPos );
+ }
+
+ sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
+ if ( nPos1 != -1 )
+ nPos1 = aURL.lastIndexOf( '/', nPos1 );
+
+ if ( nPos1 == -1 )
+ return OUString();
+
+ return aURL.copy( 0, nPos + 1 );
+}
+
+
+// Non-interface methods.
+
+
+// static
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ const ContentProperties& rData,
+ const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider,
+ const OUString& rContentId )
+{
+ // Note: Empty sequence means "get values of all supported properties".
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ sal_Int32 nCount = rProperties.getLength();
+ if ( nCount )
+ {
+ uno::Reference< beans::XPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ const beans::Property* pProps = rProperties.getConstArray();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::Property& rProp = pProps[ n ];
+
+ // Process standard UCB, DAV and HTTP properties.
+ const uno::Any & rValue = rData.getValue( rProp.Name );
+ if ( rValue.hasValue() )
+ {
+ xRow->appendObject( rProp, rValue );
+ }
+ else
+ {
+ // Process local Additional Properties.
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet =
+ rProvider->getAdditionalPropertySet( rContentId,
+ false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( !xAdditionalPropSet.is() ||
+ !xRow->appendPropertySetValue(
+ xAdditionalPropSet, rProp ) )
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Append all standard UCB, DAV and HTTP properties.
+
+ const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
+
+ ContentProvider * pProvider
+ = static_cast< ContentProvider * >( rProvider.get() );
+ beans::Property aProp;
+
+ for ( const auto& rProp : *xProps )
+ {
+ if ( pProvider->getProperty( rProp.first, aProp ) )
+ xRow->appendObject( aProp, rProp.second.value() );
+ }
+
+ // Append all local Additional Properties.
+ uno::Reference< beans::XPropertySet > xSet =
+ rProvider->getAdditionalPropertySet( rContentId, false );
+ xRow->appendPropertySet( xSet );
+ }
+
+ return uno::Reference< sdbc::XRow >( xRow.get() );
+}
+
+
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< ContentProperties > xProps;
+ std::unique_ptr< ContentProperties > xCachedProps;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ OUString aUnescapedTitle;
+ bool bHasAll = false;
+ uno::Reference< uno::XComponentContext > xContext;
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ::ucbhelper::ContentProviderImplHelper > xProvider;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aUnescapedTitle = SerfUri::unescape( m_aEscapedTitle );
+ xContext.set( m_xContext );
+ xIdentifier.set( m_xIdentifier );
+ xProvider.set( m_xProvider.get() );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+
+ // First, ask cache...
+ if ( m_xCachedProps.get() )
+ {
+ xCachedProps.reset( new ContentProperties( *m_xCachedProps ) );
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset( new ContentProperties( *xCachedProps ) );
+ }
+ }
+
+ if ( !m_bTransient && !bHasAll )
+ {
+ // Obtain values from server...
+
+
+ // First, identify whether resource is DAV or not
+ bool bNetworkAccessAllowed = true;
+ const ResourceType & rType = getResourceType( xEnv, xResAccess, &bNetworkAccessAllowed );
+
+ if ( DAV == rType )
+ {
+ // cache lookup... getResourceType may fill the props cache via
+ // PROPFIND!
+ if ( m_xCachedProps.get() )
+ {
+ xCachedProps.reset(
+ new ContentProperties( *m_xCachedProps ) );
+
+ std::vector< OUString > aMissingProps;
+ if ( xCachedProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ {
+ // All properties are already in cache! No server access
+ // needed.
+ bHasAll = true;
+ }
+
+ // use the cached ContentProperties instance
+ xProps.reset( new ContentProperties( *xCachedProps ) );
+ }
+
+ if ( !bHasAll )
+ {
+ // Only DAV resources support PROPFIND
+ std::vector< OUString > aPropNames;
+
+ uno::Sequence< beans::Property > aProperties(
+ rProperties.getLength() );
+
+ if ( !m_aFailedPropNames.empty() )
+ {
+ sal_Int32 nProps = 0;
+ sal_Int32 nCount = rProperties.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const OUString & rName = rProperties[ n ].Name;
+
+ if ( std::none_of(m_aFailedPropNames.begin(), m_aFailedPropNames.end(),
+ [&rName](const OUString& rPropName) { return rPropName == rName; }) )
+ {
+ aProperties[ nProps ] = rProperties[ n ];
+ nProps++;
+ }
+ }
+
+ aProperties.realloc( nProps );
+ }
+ else
+ {
+ aProperties = rProperties;
+ }
+
+ if ( aProperties.getLength() > 0 )
+ ContentProperties::UCBNamesToDAVNames(
+ aProperties, aPropNames );
+
+ if ( !aPropNames.empty() )
+ {
+ std::vector< DAVResource > resources;
+ try
+ {
+ xResAccess->PROPFIND(
+ DAVZERO, aPropNames, resources, xEnv );
+
+ if ( 1 == resources.size() )
+ {
+ if ( xProps.get())
+ xProps->addProperties(
+ aPropNames,
+ ContentProperties( resources[ 0 ] ));
+ else
+ xProps.reset(
+ new ContentProperties( resources[ 0 ] ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ bNetworkAccessAllowed = bNetworkAccessAllowed &&
+ shouldAccessNetworkAfterException( e );
+
+ if ( !bNetworkAccessAllowed )
+ {
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+ }
+ }
+ }
+ }
+ }
+
+ if ( bNetworkAccessAllowed )
+ {
+ // All properties obtained already?
+ std::vector< OUString > aMissingProps;
+ if ( !( xProps.get()
+ && xProps->containsAllNames(
+ rProperties, aMissingProps ) )
+ || !m_bDidGetOrHead )
+ {
+ // Possibly the missing props can be obtained using a HEAD
+ // request.
+
+ std::vector< OUString > aHeaderNames;
+ ContentProperties::UCBNamesToHTTPNames(
+ rProperties,
+ aHeaderNames,
+ true /* bIncludeUnmatched */ );
+
+ if ( !aHeaderNames.empty() )
+ {
+ try
+ {
+ DAVResource resource;
+ xResAccess->HEAD( aHeaderNames, resource, xEnv );
+ m_bDidGetOrHead = true;
+
+ if ( xProps.get() )
+ 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 )
+ {
+ // 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;
+
+ // 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
+ // TODO SC_NOT_FOUND is only for google-code server
+ if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED ||
+ aLastException.getStatus() == SC_METHOD_NOT_ALLOWED ||
+ aLastException.getStatus() == SC_NOT_FOUND )
+ {
+ lcl_sendPartialGETRequest( bError,
+ aLastException,
+ aMissingProps,
+ aHeaderNames,
+ xResAccess,
+ xProps,
+ xEnv );
+ m_bDidGetOrHead = !bError;
+ }
+
+ if ( bError )
+ {
+ if ( !shouldAccessNetworkAfterException( aLastException ) )
+ {
+ cancelCommandExecution( aLastException, xEnv );
+ // unreachable
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // might trigger HTTP redirect.
+ // Therefore, title must be updated here.
+ SerfUri aUri( xResAccess->getURL() );
+ aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
+
+ if ( rType == UNKNOWN )
+ {
+ xProps.reset( new ContentProperties( aUnescapedTitle ) );
+ }
+
+ // For DAV resources we only know the Title, for non-DAV
+ // resources we additionally know that it is a document.
+
+ if ( rType == DAV )
+ {
+ //xProps.reset(
+ // new ContentProperties( aUnescapedTitle ) );
+ xProps->addProperty(
+ "Title",
+ uno::makeAny( aUnescapedTitle ),
+ true );
+ }
+ else
+ {
+ if ( !xProps.get() )
+ xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
+ else
+ xProps->addProperty(
+ "Title",
+ uno::makeAny( aUnescapedTitle ),
+ true );
+
+ xProps->addProperty(
+ "IsFolder",
+ uno::makeAny( false ),
+ true );
+ xProps->addProperty(
+ "IsDocument",
+ uno::makeAny( true ),
+ true );
+ xProps->addProperty(
+ "ContentType",
+ uno::makeAny( OUString(WEBDAV_CONTENT_TYPE) ),
+ true );
+ }
+ }
+ else
+ {
+ // No server access for just created (not yet committed) objects.
+ // Only a minimal set of properties supported at this stage.
+ if (m_bTransient)
+ xProps.reset( new ContentProperties( aUnescapedTitle,
+ m_bCollection ) );
+ }
+
+ sal_Int32 nCount = rProperties.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const OUString rName = rProperties[ n ].Name;
+ if ( rName == "BaseURI" )
+ {
+ // Add BaseURI property, if requested.
+ xProps->addProperty(
+ "BaseURI",
+ uno::makeAny( getBaseURI( xResAccess ) ),
+ true );
+ }
+ else if ( rName == "CreatableContentsInfo" )
+ {
+ // Add CreatableContentsInfo property, if requested.
+ bool bFolder = false;
+ xProps->getValue( "IsFolder" )
+ >>= bFolder;
+ xProps->addProperty(
+ "CreatableContentsInfo",
+ uno::makeAny( bFolder
+ ? queryCreatableContentsInfo()
+ : uno::Sequence< ucb::ContentInfo >() ),
+ true );
+ }
+ }
+
+ uno::Reference< sdbc::XRow > xResultRow
+ = getPropertyValues( xContext,
+ rProperties,
+ *xProps,
+ xProvider,
+ xIdentifier->getContentIdentifier() );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( !m_xCachedProps.get() )
+ m_xCachedProps.reset( new CachableContentProperties( *xProps ) );
+ else
+ m_xCachedProps->addProperties( *xProps );
+
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ m_aEscapedTitle = SerfUri::escapeSegment( aUnescapedTitle );
+ }
+
+ return xResultRow;
+}
+
+
+uno::Sequence< uno::Any > Content::setPropertyValues(
+ const uno::Sequence< beans::PropertyValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ rtl::Reference< ContentProvider > xProvider;
+ bool bTransient;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xProvider.set( m_pProvider );
+ xIdentifier.set( m_xIdentifier );
+ bTransient = m_bTransient;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ uno::Sequence< uno::Any > aRet( rValues.getLength() );
+ uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
+ sal_Int32 nChanged = 0;
+
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ // aEvent.PropertyName =
+ aEvent.PropertyHandle = -1;
+ // aEvent.OldValue =
+ // aEvent.NewValue =
+
+ std::vector< ProppatchValue > aProppatchValues;
+ std::vector< sal_Int32 > aProppatchPropsPositions;
+
+ uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ bool bExchange = false;
+ OUString aNewTitle;
+ OUString aOldTitle;
+ sal_Int32 nTitlePos = -1;
+
+ uno::Reference< beans::XPropertySetInfo > xInfo;
+
+ const beans::PropertyValue* pValues = rValues.getConstArray();
+ sal_Int32 nCount = rValues.getLength();
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::PropertyValue& rValue = pValues[ n ];
+ const OUString & rName = rValue.Name;
+
+ beans::Property aTmpProp;
+ xProvider->getProperty( rName, aTmpProp );
+
+ if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+
+ // Mandatory props.
+
+
+ if ( rName == "ContentType" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsDocument" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "IsFolder" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "Title" )
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty titles!
+ if ( aNewValue.getLength() > 0 )
+ {
+ try
+ {
+ SerfUri aURI( xIdentifier->getContentIdentifier() );
+ aOldTitle = aURI.GetPathBaseNameUnescaped();
+
+ if ( aNewValue != aOldTitle )
+ {
+ // modified title -> modified URL -> exchange !
+ if ( !bTransient )
+ bExchange = true;
+
+ // new value will be set later...
+ aNewTitle = aNewValue;
+
+ // remember position within sequence of values (for
+ // error handling).
+ nTitlePos = n;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ aRet[ n ] <<= lang::IllegalArgumentException(
+ "Invalid content identifier!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= lang::IllegalArgumentException(
+ "Empty title not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= beans::IllegalTypeException(
+ "Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else
+ {
+
+ // Optional props.
+
+
+ OUString aSpecialName;
+ bool bIsSpecial = DAVProperties::isUCBSpecialProperty( rName, aSpecialName );
+
+ if ( !xInfo.is() )
+ xInfo = getPropertySetInfo( xEnv,
+ false /* don't cache data */ );
+
+ if ( !xInfo->hasPropertyByName( bIsSpecial ? aSpecialName : rName ) )
+ {
+ // Check, whether property exists. Skip otherwise.
+ // PROPPATCH::set would add the property automatically, which
+ // is not allowed for "setPropertyValues" command!
+ aRet[ n ] <<= beans::UnknownPropertyException(
+ "Property is unknown!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ continue;
+ }
+
+ if ( rName == "Size" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateCreated" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "DateModified" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rName == "MediaType" )
+ {
+ // Read-only property!
+ // (but could be writable, if 'getcontenttype' would be)
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ if ( rName == "CreatableContentsInfo" )
+ {
+ // Read-only property!
+ aRet[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ if ( getResourceType( xEnv, xResAccess ) == DAV )
+ {
+ // Property value will be set on server.
+ ProppatchValue aValue( PROPSET, rName, rValue.Value );
+ aProppatchValues.push_back( aValue );
+
+ // remember position within sequence of values (for
+ // error handling).
+ aProppatchPropsPositions.push_back( n );
+ }
+ else
+ {
+ // Property value will be stored in local property store.
+ if ( !bTriedToGetAdditionalPropSet &&
+ !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet
+ = getAdditionalPropertySet( false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ try
+ {
+ uno::Any aOldValue
+ = xAdditionalPropSet->getPropertyValue( rName );
+ if ( aOldValue != rValue.Value )
+ {
+ xAdditionalPropSet->setPropertyValue(
+ rName, rValue.Value );
+
+ aEvent.PropertyName = rName;
+ aEvent.OldValue = aOldValue;
+ aEvent.NewValue = rValue.Value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( beans::UnknownPropertyException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( lang::WrappedTargetException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( beans::PropertyVetoException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ catch ( lang::IllegalArgumentException const & e )
+ {
+ aRet[ n ] <<= e;
+ }
+ }
+ else
+ {
+ aRet[ n ] <<= uno::Exception(
+ "No property set for storing the value!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ }
+ } // for
+
+ if ( !bTransient && (!aProppatchValues.empty()) )
+ {
+ try
+ {
+ // Set property values at server.
+ xResAccess->PROPPATCH( aProppatchValues, xEnv );
+
+ for ( const auto& rProppatchValue : aProppatchValues )
+ {
+ aEvent.PropertyName = rProppatchValue.name;
+ aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
+ aEvent.NewValue = rProppatchValue.value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( DAVException const & e )
+ {
+// SAL_WARN( "ucb.ucp.webdav",
+// "Content::setPropertyValues - PROPPATCH failed!" );
+
+#if 1
+ cancelCommandExecution( e, xEnv );
+ // unreachable
+#else
+ // Note: PROPPATCH either sets ALL property values OR NOTHING.
+
+ std::vector< sal_Int32 >::const_iterator it
+ = aProppatchPropsPositions.begin();
+ std::vector< sal_Int32 >::const_iterator end
+ = aProppatchPropsPositions.end();
+
+ while ( it != end )
+ {
+ // Set error.
+ aRet[ (*it) ] <<= MapDAVException( e, true );
+ ++it;
+ }
+#endif
+ }
+ }
+
+ if ( bExchange )
+ {
+ // Assemble new content identifier...
+
+ OUString aNewURL = getParentURL();
+ if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
+ aNewURL += "/";
+
+ aNewURL += SerfUri::escapeSegment( aNewTitle );
+
+ uno::Reference< ucb::XContentIdentifier > xNewId
+ = new ::ucbhelper::ContentIdentifier( aNewURL );
+ uno::Reference< ucb::XContentIdentifier > xOldId = xIdentifier;
+
+ try
+ {
+ SerfUri sourceURI( xOldId->getContentIdentifier() );
+ SerfUri targetURI( xNewId->getContentIdentifier() );
+ targetURI.SetScheme( sourceURI.GetScheme() );
+
+ xResAccess->MOVE(
+ sourceURI.GetPath(), targetURI.GetURI(), false, xEnv );
+ // @@@ Should check for resources that could not be moved
+ // (due to source access or target overwrite) and send
+ // this information through the interaction handler.
+
+ // @@@ Existing content should be checked to see if it needs
+ // to be deleted at the source
+
+ // @@@ Existing content should be checked to see if it has
+ // been overwritten at the target
+
+ if ( exchangeIdentity( xNewId ) )
+ {
+ xResAccess->setURL( aNewURL );
+
+// DAV resources store all additional props on server!
+// // Adapt Additional Core Properties.
+// renameAdditionalPropertySet( xOldId->getContentIdentifier(),
+// xNewId->getContentIdentifier(),
+// true );
+ }
+ else
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRet[ nTitlePos ] <<= uno::Exception(
+ "Exchange failed!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ // Do not set new title!
+ aNewTitle.clear();
+
+ // Set error .
+ aRet[ nTitlePos ] = MapDAVException( e, true );
+ }
+ }
+
+ if ( aNewTitle.getLength() )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ aEvent.PropertyName = "Title";
+ aEvent.OldValue <<= aOldTitle;
+ aEvent.NewValue <<= aNewTitle;
+
+ m_aEscapedTitle = SerfUri::escapeSegment( aNewTitle );
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+
+ if ( nChanged > 0 )
+ {
+ aChanges.realloc( nChanged );
+ notifyPropertiesChange( aChanges );
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+
+ return aRet;
+}
+
+
+uno::Any Content::open(
+ const ucb::OpenCommandArgument2 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Any aRet;
+
+ bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
+ ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
+ if ( bOpenFolder )
+ {
+ if ( isFolder( xEnv ) )
+ {
+ // Open collection.
+
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet( m_xContext, this, rArg, xEnv );
+ aRet <<= xSet;
+ }
+ else
+ {
+ // Error: Not a folder!
+
+ OUString aMsg( "Non-folder resource cannot be opened as folder! Wrong Open Mode!" );
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ lang::IllegalArgumentException(
+ aMsg,
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+
+ if ( rArg.Sink.is() )
+ {
+ // Open document.
+
+ if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
+ {
+ // Currently(?) unsupported.
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedOpenModeException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ sal_Int16( rArg.Mode ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
+ if ( xOut.is() )
+ {
+ // PUSH: write data
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ xResAccess->GET( xOut, aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if ( !m_xCachedProps.get())
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties( ContentProperties( aResource ) );
+
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xDataSink.is() )
+ {
+ // PULL: wait for client read
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ // fill inputstream sync; return if all data present
+ DAVResource aResource;
+ std::vector< OUString > aHeaders;
+
+ uno::Reference< io::XInputStream > xIn
+ = xResAccess->GET( aHeaders, aResource, xEnv );
+ m_bDidGetOrHead = true;
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // cache headers.
+ if ( !m_xCachedProps.get())
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( aResource ) ) );
+ else
+ m_xCachedProps->addProperties(
+ aResource.properties );
+
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ xDataSink->setInputStream( xIn );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv );
+ // Unreachable
+ }
+ }
+ else
+ {
+ // Note: aOpenCommand.Sink may contain an XStream
+ // implementation. Support for this type of
+ // sink is optional...
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+ }
+
+ return aRet;
+}
+
+
+void Content::post(
+ const ucb::PostCommandArgument2 & rArg,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
+ if ( xSink.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ uno::Reference< io::XInputStream > xResult
+ = xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ xSink->setInputStream( xResult );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
+ if ( xResult.is() )
+ {
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset(
+ new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ xResAccess->POST( rArg.MediaType,
+ rArg.Referer,
+ rArg.Source,
+ xResult,
+ xEnv );
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, xEnv, true );
+ // Unreachable
+ }
+ }
+ else
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+}
+
+
+void Content::queryChildren( ContentRefList& rChildren )
+{
+ // Obtain a list with a snapshot of all currently instantiated contents
+ // from provider and extract the contents which are direct children
+ // of this content.
+
+ ::ucbhelper::ContentRefList aAllContents;
+ m_xProvider->queryExistingContents( aAllContents );
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
+
+ if ( nURLPos != ( aURL.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aURL += "/";
+ }
+
+ sal_Int32 nLen = aURL.getLength();
+
+ for ( const auto& rChild : aAllContents )
+ {
+ ::ucbhelper::ContentImplHelperRef xChild = rChild;
+ OUString aChildURL
+ = xChild->getIdentifier()->getContentIdentifier();
+
+ // Is aURL a prefix of aChildURL?
+ if ( ( aChildURL.getLength() > nLen ) &&
+ ( aChildURL.startsWith( aURL ) ) )
+ {
+ sal_Int32 nPos = nLen;
+ nPos = aChildURL.indexOf( '/', nPos );
+
+ if ( ( nPos == -1 ) ||
+ ( nPos == ( aChildURL.getLength() - 1 ) ) )
+ {
+ // No further slashes / only a final slash. It's a child!
+ rChildren.push_back(
+ ::http_dav_ucp::Content::ContentRef(
+ static_cast< ::http_dav_ucp::Content * >(
+ xChild.get() ) ) );
+ }
+ }
+ }
+}
+
+
+void Content::insert(
+ const uno::Reference< io::XInputStream > & xInputStream,
+ bool bReplaceExisting,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ bool bTransient, bCollection;
+ OUString aEscapedTitle;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ bTransient = m_bTransient;
+ bCollection = m_bCollection;
+ aEscapedTitle = m_aEscapedTitle;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ // Check, if all required properties are present.
+
+ if ( aEscapedTitle.isEmpty() )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
+
+ uno::Sequence<OUString> aProps { "Title" };
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::MissingPropertiesException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ aProps ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !bReplaceExisting )
+ {
+ /* [RFC 2616] - HTTP
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server.
+ */
+
+ /* [RFC 2518] - WebDAV
+
+ MKCOL creates a new collection resource at the location specified by
+ the Request-URI. If the resource identified by the Request-URI is
+ non-null then the MKCOL MUST fail.
+ */
+
+ // ==> Complain on PUT, continue on MKCOL.
+ if ( !bTransient || !bCollection )
+ {
+#undef ERROR
+ ucb::UnsupportedNameClashException aEx(
+ "Unable to write without overwrite!",
+ static_cast< cppu::OWeakObject * >( this ),
+ ucb::NameClash::ERROR );
+
+ uno::Reference< task::XInteractionHandler > xIH;
+
+ if ( Environment.is() )
+ xIH = Environment->getInteractionHandler();
+
+ if ( xIH.is() )
+ {
+ uno::Any aExAsAny( uno::makeAny( aEx ) );
+
+ rtl::Reference< ucbhelper::SimpleInteractionRequest > xRequest
+ = new ucbhelper::SimpleInteractionRequest(
+ aExAsAny,
+ ContinuationFlags::Approve
+ | ContinuationFlags::Disapprove );
+ xIH->handle( xRequest.get() );
+
+ const ContinuationFlags nResp = xRequest->getResponse();
+
+ switch ( nResp )
+ {
+ case ContinuationFlags::NONE:
+ // Not handled; throw.
+ throw aEx;
+// break;
+
+ case ContinuationFlags::Approve:
+ // Continue -> Overwrite.
+ bReplaceExisting = true;
+ break;
+
+ case ContinuationFlags::Disapprove:
+ // Abort.
+ throw ucb::CommandFailedException(
+ OUString(),
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+
+ default:
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::insert - "
+ "Unknown interaction selection!" );
+ throw ucb::CommandFailedException(
+ "Unknown interaction selection!",
+ uno::Reference< uno::XInterface >(),
+ aExAsAny );
+// break;
+ }
+ }
+ else
+ {
+ // No IH; throw.
+ throw aEx;
+ }
+ }
+ }
+
+ if ( bTransient )
+ {
+ // Assemble new content identifier...
+ OUString aURL = getParentURL();
+ if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += aEscapedTitle;
+
+ try
+ {
+ xResAccess->setURL( aURL );
+
+ if ( bCollection )
+ xResAccess->MKCOL( Environment );
+ else
+ xResAccess->PUT( xInputStream, Environment );
+ }
+ catch ( DAVException const & except )
+ {
+ if ( bCollection )
+ {
+ if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // [RFC 2518] - WebDAV
+ // 405 (Method Not Allowed) - MKCOL can only be
+ // executed on a deleted/non-existent resource.
+
+ if ( bReplaceExisting )
+ {
+ // Destroy old resource.
+ try
+ {
+ xResAccess->DESTROY( Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ // Insert (recursion!).
+ insert( xInputStream, bReplaceExisting, Environment );
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset(
+ new DAVResourceAccess( *xResAccess ) );
+ }
+
+ // Success!
+ return;
+ }
+ else
+ {
+ OUString aTitle;
+ try
+ {
+ SerfUri aURI( aURL );
+ aTitle = aURI.GetPathBaseNameUnescaped();
+ }
+ catch ( DAVException const & )
+ {
+ }
+
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTitle ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( except, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xIdentifier
+ = new ::ucbhelper::ContentIdentifier( aURL );
+ }
+
+ inserted();
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_bTransient = false;
+ }
+ }
+ else
+ {
+ if ( !xInputStream.is() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::MissingInputStreamException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ try
+ {
+ xResAccess->PUT( xInputStream, Environment );
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+}
+
+
+void Content::transfer(
+ const ucb::TransferInfo & rArgs,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Reference< uno::XComponentContext > xContext;
+ uno::Reference< ucb::XContentIdentifier > xIdentifier;
+ uno::Reference< ucb::XContentProvider > xProvider;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ xContext.set( m_xContext );
+ xIdentifier.set( m_xIdentifier );
+ xProvider.set( m_xProvider.get() );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ OUString aTargetURI;
+ try
+ {
+ SerfUri sourceURI( rArgs.SourceURL );
+ SerfUri targetURI( xIdentifier->getContentIdentifier() );
+ aTargetURI = targetURI.GetPathBaseNameUnescaped();
+
+ // Check source's and target's URL scheme
+
+ OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if ( aScheme == DAV_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTP_URL_SCHEME );
+ }
+ else if ( aScheme == DAVS_URL_SCHEME )
+ {
+ sourceURI.SetScheme( HTTPS_URL_SCHEME );
+ }
+ else if (aScheme == WEBDAV_URL_SCHEME)
+ {
+ sourceURI.SetScheme(HTTP_URL_SCHEME);
+ }
+ else if (aScheme == WEBDAVS_URL_SCHEME)
+ {
+ sourceURI.SetScheme(HTTPS_URL_SCHEME);
+ }
+ else
+ {
+ if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::InteractiveBadTransferURLException(
+ "Unsupported URL scheme!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ aScheme = targetURI.GetScheme().toAsciiLowerCase();
+ if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if ( aScheme == DAV_URL_SCHEME )
+ targetURI.SetScheme( HTTP_URL_SCHEME );
+ else if ( aScheme == DAVS_URL_SCHEME )
+ targetURI.SetScheme( HTTPS_URL_SCHEME );
+ else if (aScheme == WEBDAV_URL_SCHEME)
+ targetURI.SetScheme(HTTP_URL_SCHEME);
+ else if (aScheme == WEBDAVS_URL_SCHEME)
+ targetURI.SetScheme(HTTPS_URL_SCHEME);
+
+ // @@@ This implementation of 'transfer' only works
+ // if the source and target are located at same host.
+ // (Neon does not support cross-server copy/move)
+
+ // Check for same host
+
+ if ( sourceURI.GetHost().getLength() &&
+ ( sourceURI.GetHost() != targetURI.GetHost() ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny( ucb::InteractiveBadTransferURLException(
+ "Different hosts!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ OUString aTitle = rArgs.NewTitle;
+
+ if ( aTitle.isEmpty() )
+ aTitle = sourceURI.GetPathBaseNameUnescaped();
+
+ if ( aTitle == "/" )
+ {
+ // kso: ???
+ aTitle.clear();
+ }
+
+ targetURI.AppendPath( aTitle );
+
+ OUString aTargetURL = xIdentifier->getContentIdentifier();
+ if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
+ != aTargetURL.getLength() )
+ aTargetURL += "/";
+
+ aTargetURL += aTitle;
+
+ uno::Reference< ucb::XContentIdentifier > xTargetId
+ = new ::ucbhelper::ContentIdentifier( aTargetURL );
+
+ DAVResourceAccess aSourceAccess( xContext,
+ xResAccess->getSessionFactory(),
+ sourceURI.GetURI() );
+
+ if ( rArgs.MoveData )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xSource
+ = static_cast< Content * >(
+ xProvider->queryContent( xId ).get() );
+
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the move the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aSourceAccess.MOVE( sourceURI.GetPath(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+ if ( xSource.is() )
+ {
+ // Propagate destruction to listeners.
+ xSource->destroy( true );
+ }
+
+// DAV resources store all additional props on server!
+// // Rename own and all children's Additional Core Properties.
+// renameAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// true );
+ }
+ else
+ {
+ // [RFC 2518] - WebDAV
+ // If a resource exists at the destination and the Overwrite
+ // header is "T" then prior to performing the copy the server
+ // MUST perform a DELETE with "Depth: infinity" on the
+ // destination resource. If the Overwrite header is set to
+ // "F" then the operation will fail.
+
+ aSourceAccess.COPY( sourceURI.GetPath(),
+ targetURI.GetURI(),
+ rArgs.NameClash
+ == ucb::NameClash::OVERWRITE,
+ Environment );
+
+// DAV resources store all additional props on server!
+// // Copy own and all children's Additional Core Properties.
+// copyAdditionalPropertySet( xId->getContentIdentifier(),
+// xTargetId->getContentIdentifier(),
+// true );
+ }
+
+ // Note: The static cast is okay here, because its sure that
+ // xProvider is always the WebDAVContentProvider.
+ rtl::Reference< Content > xTarget
+ = static_cast< Content * >(
+ xProvider->queryContent( xTargetId ).get() );
+
+ // Announce transferred content in its new folder.
+ xTarget->inserted();
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+ catch ( DAVException const & e )
+ {
+ // [RFC 2518] - WebDAV
+ // 412 (Precondition Failed) - The server was unable to maintain
+ // the liveness of the properties listed in the propertybehavior
+ // XML element or the Overwrite header is "F" and the state of
+ // the destination resource is non-null.
+
+ if ( e.getStatus() == SC_PRECONDITION_FAILED )
+ {
+ switch ( rArgs.NameClash )
+ {
+ case 0/*ucb::NameClash::ERROR*/:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aTargetURI ) ),
+ Environment );
+ // Unreachable
+ }
+ [[fallthrough]];
+
+ case ucb::NameClash::OVERWRITE:
+ break;
+
+ case ucb::NameClash::KEEP: // deprecated
+ case ucb::NameClash::RENAME:
+ case ucb::NameClash::ASK:
+ default:
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::makeAny(
+ ucb::UnsupportedNameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArgs.NameClash ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+ }
+
+ cancelCommandExecution( e, Environment, true );
+ // Unreachable
+ }
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+}
+
+
+void Content::destroy( bool bDeletePhysical )
+{
+ // @@@ take care about bDeletePhysical -> trashcan support
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ deleted();
+
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Process instantiated children...
+
+ ::http_dav_ucp::Content::ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( auto& rChild : aChildren )
+ {
+ rChild->destroy( bDeletePhysical );
+ }
+}
+
+
+bool Content::supportsExclusiveWriteLock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ if ( getResourceType( Environment ) == DAV )
+ {
+ if ( m_xCachedProps.get() )
+ {
+ uno::Sequence< ucb::LockEntry > aSupportedLocks;
+ if ( m_xCachedProps->getValue( DAVProperties::SUPPORTEDLOCK )
+ >>= aSupportedLocks )
+ {
+ for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
+ {
+ if ( aSupportedLocks[ n ].Scope
+ == ucb::LockScope_EXCLUSIVE &&
+ aSupportedLocks[ n ].Type
+ == ucb::LockType_WRITE )
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+void Content::lock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ uno::Any aOwnerAny;
+ aOwnerAny <<= OUString( "http://ucb.openoffice.org" );
+
+ ucb::Lock aLock(
+ ucb::LockScope_EXCLUSIVE,
+ ucb::LockType_WRITE,
+ ucb::LockDepth_ZERO,
+ aOwnerAny,
+ 180, // lock timeout in secs
+ //-1, // infinite lock
+ uno::Sequence< OUString >() );
+
+ xResAccess->LOCK( aLock, Environment );
+ m_bLocked = true;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, false );
+ // Unreachable
+ }
+}
+
+
+void Content::unlock(
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ try
+ {
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+
+ xResAccess->UNLOCK( Environment );
+ m_bLocked = false;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ }
+ catch ( DAVException const & e )
+ {
+ cancelCommandExecution( e, Environment, false );
+ // Unreachable
+ }
+}
+
+
+bool Content::exchangeIdentity(
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ if ( !xNewId.is() )
+ return false;
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Already persistent?
+ if ( m_bTransient )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
+ return false;
+ }
+
+ // Exchange own identitity.
+
+ // Fail, if a content with given id already exists.
+// if ( !hasData( xNewId ) )
+ {
+ OUString aOldURL = m_xIdentifier->getContentIdentifier();
+
+ aGuard.clear();
+ if ( exchange( xNewId ) )
+ {
+ // Process instantiated children...
+
+ ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( const auto& rChild : aChildren )
+ {
+ ContentRef xChild = rChild;
+
+ // Create new content identifier for the child...
+ uno::Reference< ucb::XContentIdentifier >
+ xOldChildId = xChild->getIdentifier();
+ OUString aOldChildURL
+ = xOldChildId->getContentIdentifier();
+ OUString aNewChildURL
+ = aOldChildURL.replaceAt(
+ 0,
+ aOldURL.getLength(),
+ xNewId->getContentIdentifier() );
+ uno::Reference< ucb::XContentIdentifier > xNewChildId
+ = new ::ucbhelper::ContentIdentifier( aNewChildURL );
+
+ if ( !xChild->exchangeIdentity( xNewChildId ) )
+ return false;
+ }
+ return true;
+ }
+ }
+
+ SAL_WARN( "ucb.ucp.webdav",
+ "Content::exchangeIdentity - "
+ "Panic! Cannot exchange identity!" );
+ return false;
+}
+
+
+bool Content::isFolder(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_bTransient )
+ return m_bCollection;
+ }
+
+ uno::Sequence< beans::Property > aProperties( 1 );
+ aProperties[ 0 ].Name = "IsFolder";
+ aProperties[ 0 ].Handle = -1;
+ uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
+ if ( xRow.is() )
+ {
+ try
+ {
+ return xRow->getBoolean( 1 );
+ }
+ catch ( sdbc::SQLException const & )
+ {
+ }
+ }
+
+ return false;
+}
+
+
+uno::Any Content::MapDAVException( const DAVException & e, bool bWrite )
+{
+ // Map DAVException...
+ uno::Any aException;
+
+ OUString aURL;
+ if ( m_bTransient )
+ {
+ aURL = getParentURL();
+ if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
+ aURL += "/";
+
+ aURL += m_aEscapedTitle;
+ }
+ else
+ {
+ aURL = m_xIdentifier->getContentIdentifier();
+ }
+
+ switch ( e.getStatus() )
+ {
+ case SC_NOT_FOUND:
+ {
+ uno::Sequence< uno::Any > aArgs( 1 );
+ aArgs[ 0 ] <<= beans::PropertyValue(
+ "Uri", -1,
+ uno::makeAny(aURL),
+ beans::PropertyState_DIRECT_VALUE);
+
+ aException <<=
+ ucb::InteractiveAugmentedIOException(
+ "Not found!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ ucb::IOErrorCode_NOT_EXISTING,
+ aArgs );
+ return aException;
+ }
+ default:
+ break;
+ }
+
+ switch ( e.getError() )
+ {
+ case DAVException::DAV_HTTP_ERROR:
+ {
+ if ( bWrite )
+ aException <<=
+ ucb::InteractiveNetworkWriteException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ else
+ aException <<=
+ ucb::InteractiveNetworkReadException(
+ e.getData(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+ }
+
+ case DAVException::DAV_HTTP_LOOKUP:
+ aException <<=
+ ucb::InteractiveNetworkResolveNameException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTH:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_AUTHPROXY:
+// break;
+
+ case DAVException::DAV_HTTP_CONNECT:
+ aException <<=
+ ucb::InteractiveNetworkConnectException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ e.getData() );
+ break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_TIMEOUT:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_HTTP_REDIRECT:
+// break;
+
+// @@@ No matching InteractiveNetwork*Exception
+// case DAVException::DAV_SESSION_CREATE:
+// break;
+
+ case DAVException::DAV_INVALID_ARG:
+ aException <<=
+ lang::IllegalArgumentException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ break;
+
+ case DAVException::DAV_LOCKED:
+#if 1
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ false ); // not SelfOwned
+#else
+ {
+ uno::Sequence< uno::Any > aArgs( 1 );
+ aArgs[ 0 ] <<= beans::PropertyValue(
+ OUString("Uri"), -1,
+ uno::makeAny(aURL),
+ beans::PropertyState_DIRECT_VALUE);
+
+ aException <<=
+ ucb::InteractiveAugmentedIOException(
+ OUString( "Locked!" ),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ ucb::IOErrorCode_LOCKING_VIOLATION,
+ aArgs );
+ }
+#endif
+ break;
+
+ case DAVException::DAV_LOCKED_SELF:
+ aException <<=
+ ucb::InteractiveLockingLockedException(
+ "Locked (self)!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL,
+ true ); // SelfOwned
+ break;
+
+ case DAVException::DAV_NOT_LOCKED:
+ aException <<=
+ ucb::InteractiveLockingNotLockedException(
+ "Not locked!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ case DAVException::DAV_LOCK_EXPIRED:
+ aException <<=
+ ucb::InteractiveLockingLockExpiredException(
+ "Lock expired!",
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ aURL );
+ break;
+
+ default:
+ aException <<=
+ ucb::InteractiveNetworkGeneralException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR );
+ break;
+ }
+
+ return aException;
+}
+
+
+// static
+bool Content::shouldAccessNetworkAfterException( const DAVException & e )
+{
+ if ( ( e.getStatus() == SC_NOT_FOUND ) ||
+ ( e.getError() == DAVException::DAV_HTTP_LOOKUP ) ||
+ ( e.getError() == DAVException::DAV_HTTP_CONNECT ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTH ) ||
+ ( e.getError() == DAVException::DAV_HTTP_AUTHPROXY ) )
+ return false;
+
+ return true;
+}
+
+
+void Content::cancelCommandExecution(
+ const DAVException & e,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv,
+ bool bWrite /* = false */ )
+{
+ ucbhelper::cancelCommandExecution( MapDAVException( e, bWrite ), xEnv );
+ // Unreachable
+}
+
+
+OUString
+Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // First, try to obtain value of response header "Content-Location".
+ if ( m_xCachedProps.get() )
+ {
+ OUString aLocation;
+ m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
+ if ( aLocation.getLength() )
+ {
+ try
+ {
+ // Do not use m_xIdentifier->getContentIdentifier() because it
+ // for example does not reflect redirects applied to requests
+ // done using the original URI but m_xResAccess' URI does.
+ return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
+ aLocation );
+ }
+ catch ( rtl::MalformedUriException const & )
+ {
+ }
+ }
+ }
+
+ return rResAccess->getURL();
+}
+
+
+Content::ResourceType Content::getResourceType(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed )
+{
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType != UNKNOWN) {
+ return m_eResourceType;
+ }
+ }
+
+ ResourceType eResourceType = UNKNOWN;
+
+ try
+ {
+ // Try to fetch some frequently used property value, e.g. those
+ // used when loading documents... along with identifying whether
+ // this is a DAV resource.
+ std::vector< DAVResource > resources;
+ std::vector< OUString > aPropNames;
+ uno::Sequence< beans::Property > aProperties( 5 );
+ aProperties[ 0 ].Name = "IsFolder";
+ aProperties[ 1 ].Name = "IsDocument";
+ aProperties[ 2 ].Name = "IsReadOnly";
+ aProperties[ 3 ].Name = "MediaType";
+ aProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
+
+ ContentProperties::UCBNamesToDAVNames(
+ aProperties, aPropNames );
+
+ rResAccess->PROPFIND(
+ DAVZERO, aPropNames, resources, xEnv );
+
+ // TODO - is this really only one?
+ if ( resources.size() == 1 )
+ {
+ osl::MutexGuard g(m_aMutex);
+ m_xCachedProps.reset(
+ new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
+ m_xCachedProps->containsAllNames(
+ aProperties, m_aFailedPropNames );
+ }
+
+ eResourceType = DAV;
+ }
+ catch ( DAVException const & e )
+ {
+ rResAccess->resetUri();
+
+ if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
+ {
+ // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
+ // resource is NON_DAV
+ eResourceType = NON_DAV;
+ }
+ else if (networkAccessAllowed != nullptr)
+ {
+ *networkAccessAllowed = *networkAccessAllowed
+ && shouldAccessNetworkAfterException(e);
+ }
+
+ // cancel command execution is case that no user authentication data has been provided.
+ if ( e.getError() == DAVException::DAV_HTTP_NOAUTH )
+ {
+ cancelCommandExecution( e, uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ }
+
+ osl::MutexGuard g(m_aMutex);
+ if (m_eResourceType == UNKNOWN) {
+ m_eResourceType = eResourceType;
+ } else {
+ SAL_WARN_IF(
+ eResourceType != m_eResourceType, "ucb.ucp.webdav",
+ "different resource types for <" << rResAccess->getURL() << ">: "
+ << +eResourceType << " vs. " << +m_eResourceType);
+ }
+ return m_eResourceType;
+}
+
+
+Content::ResourceType Content::getResourceType(
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ }
+ const Content::ResourceType & ret = getResourceType( xEnv, xResAccess );
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
+ }
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavcontent.hxx b/ucb/source/ucp/webdav/webdavcontent.hxx
new file mode 100644
index 000000000..ad4e93229
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavcontent.hxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVCONTENT_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVCONTENT_HXX
+
+#include <memory>
+#include <list>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include <ucbhelper/contenthelper.hxx>
+#include "DAVResourceAccess.hxx"
+#include "PropertyMap.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+ struct PropertyValue;
+}
+
+namespace com::sun::star::io {
+ class XInputStream;
+}
+
+namespace com::sun::star::sdbc {
+ class XRow;
+}
+
+namespace com::sun::star::ucb {
+ struct OpenCommandArgument2;
+ struct PropertyCommandArgument;
+ struct PostCommandArgument2;
+ struct TransferInfo;
+}
+
+namespace http_dav_ucp
+{
+
+
+// UNO service name for the content.
+#define WEBDAV_CONTENT_SERVICE_NAME "com.sun.star.ucb.WebDAVContent"
+
+
+class ContentProvider;
+class ContentProperties;
+class CachableContentProperties;
+
+class Content : public ::ucbhelper::ContentImplHelper,
+ public css::ucb::XContentCreator
+{
+ enum ResourceType
+ {
+ UNKNOWN,
+ NON_DAV,
+ DAV
+ };
+
+ std::unique_ptr< DAVResourceAccess > m_xResAccess;
+ std::unique_ptr< CachableContentProperties > m_xCachedProps; // locally cached props
+ OUString m_aEscapedTitle;
+ ResourceType m_eResourceType;
+ ContentProvider* m_pProvider; // No need for a ref, base class holds object
+ bool m_bTransient;
+ bool m_bLocked;
+ bool m_bCollection;
+ bool m_bDidGetOrHead;
+ std::vector< OUString > m_aFailedPropNames;
+
+private:
+ virtual css::uno::Sequence< css::beans::Property >
+ getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual css::uno::Sequence< css::ucb::CommandInfo >
+ getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual OUString getParentURL() override;
+
+ /// @throws css::uno::Exception
+ bool isFolder( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ css::uno::Sequence< css::uno::Any >
+ setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ typedef rtl::Reference< Content > ContentRef;
+ typedef std::vector< ContentRef > ContentRefList;
+ void queryChildren( ContentRefList& rChildren);
+
+ bool
+ exchangeIdentity( const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+
+ OUString
+ getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess );
+
+ /// @throws css::uno::Exception
+ ResourceType
+ getResourceType( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ ResourceType
+ getResourceType( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ const std::unique_ptr< DAVResourceAccess > & rResAccess,
+ bool * networkAccessAllowed = nullptr );
+
+ // Command "open"
+ /// @throws css::uno::Exception
+ css::uno::Any open(
+ const css::ucb::OpenCommandArgument2 & rArg,
+ const css::uno::Reference<
+ css::ucb::XCommandEnvironment > & xEnv );
+
+ // Command "post"
+ /// @throws css::uno::Exception
+ void post( const css::ucb::PostCommandArgument2 & rArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ // Command "insert"
+ /// @throws css::uno::Exception
+ void insert( const css::uno::Reference< css::io::XInputStream > & xInputStream,
+ bool bReplaceExisting,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "transfer"
+ /// @throws css::uno::Exception
+ void transfer( const css::ucb::TransferInfo & rArgs,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "delete"
+ /// @throws css::uno::Exception
+ void destroy( bool bDeletePhysical );
+
+ // Command "lock"
+ /// @throws css::uno::Exception
+ void lock( const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // Command "unlock"
+ /// @throws css::uno::Exception
+ void unlock( const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ css::uno::Any MapDAVException( const DAVException & e,
+ bool bWrite );
+ /// @throws css::uno::Exception
+ void cancelCommandExecution(
+ const DAVException & e,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv,
+ bool bWrite = false );
+
+ static bool shouldAccessNetworkAfterException( const DAVException & e );
+
+ bool supportsExclusiveWriteLock(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ // XPropertyContainer replacement
+ /// @throws css::beans::PropertyExistException
+ /// @throws css::beans::IllegalTypeException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+
+ /// @throws css::beans::PropertyExistException
+ /// @throws css::beans::NotRemoveableException
+ /// @throws css::uno::RuntimeException
+ void removeProperty( const OUString& Name,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment );
+public:
+ /// @throws css::ucb::ContentCreationException
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory );
+ /// @throws css::ucb::ContentCreationException
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ rtl::Reference< DAVSessionFactory > const & rSessionFactory,
+ bool isCollection );
+ virtual ~Content() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ throw() override;
+ virtual void SAL_CALL release()
+ throw() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XContent
+ virtual OUString SAL_CALL
+ getContentType() override;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL
+ execute( const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override;
+ virtual void SAL_CALL
+ abort( sal_Int32 CommandId ) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL
+ addProperty( const OUString& Name,
+ sal_Int16 Attributes,
+ const css::uno::Any& DefaultValue ) override;
+
+ virtual void SAL_CALL
+ removeProperty( const OUString& Name ) override;
+
+
+ // Additional interfaces
+
+
+ // XContentCreator
+ virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL
+ queryCreatableContentsInfo() override;
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createNewContent( const css::ucb::ContentInfo& Info ) override;
+
+
+ // Non-interface methods.
+
+
+ DAVResourceAccess & getResourceAccess() { return *m_xResAccess; }
+
+ // Called from resultset data supplier.
+ static css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const ContentProperties& rData,
+ const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider,
+ const OUString& rContentId );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavcontentcaps.cxx b/ucb/source/ucp/webdav/webdavcontentcaps.cxx
new file mode 100644
index 000000000..14050e2a1
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavcontentcaps.cxx
@@ -0,0 +1,602 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <set>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/ucb/LockEntry.hpp>
+#include "webdavcontent.hxx"
+#include "webdavprovider.hxx"
+#include "DAVProperties.hxx"
+#include "ContentProperties.hxx"
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+
+// ContentProvider implementation.
+
+
+bool ContentProvider::getProperty(
+ const OUString & rPropName, beans::Property & rProp, bool bStrict )
+{
+ if ( !m_pProps )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_pProps )
+ {
+ m_pProps = std::make_unique<PropertyMap>();
+
+
+ // Fill map of known properties...
+
+
+ // Mandatory UCB properties.
+ m_pProps->insert(
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+
+ // Optional UCB properties.
+
+ m_pProps->insert(
+ beans::Property(
+ "DateCreated",
+ -1,
+ cppu::UnoType<util::DateTime>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "DateModified",
+ -1,
+ cppu::UnoType<util::DateTime>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "Size",
+ -1,
+ cppu::UnoType<sal_Int64>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "BaseURI",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ // Standard DAV properties.
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::CREATIONDATE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::DISPLAYNAME,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTLANGUAGE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTLENGTH,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETCONTENTTYPE ,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETETAG,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::GETLASTMODIFIED,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::LOCKDISCOVERY,
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::Lock >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::RESOURCETYPE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::SUPPORTEDLOCK,
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::LockEntry >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ) );
+
+ m_pProps->insert(
+ beans::Property(
+ DAVProperties::EXECUTABLE,
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ) );
+ }
+ }
+
+
+ // Lookup property.
+
+
+ beans::Property aProp;
+ aProp.Name = rPropName;
+ const PropertyMap::const_iterator it = m_pProps->find( aProp );
+ if ( it != m_pProps->end() )
+ {
+ rProp = *it;
+ }
+ else
+ {
+ if ( bStrict )
+ return false;
+
+ // All unknown props are treated as:
+ rProp = beans::Property(
+ rPropName,
+ - 1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+ }
+
+ return true;
+}
+
+
+// Content implementation.
+
+
+// virtual
+uno::Sequence< beans::Property > Content::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ bool bTransient;
+ std::unique_ptr< DAVResourceAccess > xResAccess;
+ std::unique_ptr< ContentProperties > xCachedProps;
+ rtl::Reference< ContentProvider > xProvider;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ bTransient = m_bTransient;
+ xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
+ if ( m_xCachedProps.get() )
+ xCachedProps.reset(
+ new ContentProperties( *m_xCachedProps ) );
+ xProvider.set( m_pProvider );
+ }
+
+ std::set< OUString > aPropSet;
+
+ // No server access for just created (not yet committed) objects.
+ // Only a minimal set of properties supported at this stage.
+ if ( !bTransient )
+ {
+ // Obtain all properties supported for this resource from server.
+ try
+ {
+ std::vector< DAVResourceInfo > props;
+ xResAccess->PROPFIND( DAVZERO, props, xEnv );
+
+ // Note: vector always contains exactly one resource info, because
+ // we used a depth of DAVZERO for PROPFIND.
+ aPropSet.insert( (*props.begin()).properties.begin(),
+ (*props.begin()).properties.end() );
+ }
+ catch ( DAVException const & )
+ {
+ }
+ }
+
+ // Add DAV properties, map DAV properties to UCB properties.
+ bool bHasCreationDate = false; // creationdate <-> DateCreated
+ bool bHasGetLastModified = false; // getlastmodified <-> DateModified
+ bool bHasGetContentType = false; // getcontenttype <-> MediaType
+ bool bHasGetContentLength = false; // getcontentlength <-> Size
+
+ bool bHasContentType = false;
+ bool bHasIsDocument = false;
+ bool bHasIsFolder = false;
+ bool bHasTitle = false;
+ bool bHasBaseURI = false;
+ bool bHasDateCreated = false;
+ bool bHasDateModified = false;
+ bool bHasMediaType = false;
+ bool bHasSize = false;
+ bool bHasCreatableInfos = false;
+
+ {
+ for ( const auto& rProp : aPropSet )
+ {
+ if ( !bHasCreationDate &&
+ ( rProp == DAVProperties::CREATIONDATE ) )
+ {
+ bHasCreationDate = true;
+ }
+ else if ( !bHasGetLastModified &&
+ ( rProp == DAVProperties::GETLASTMODIFIED ) )
+ {
+ bHasGetLastModified = true;
+ }
+ else if ( !bHasGetContentType &&
+ ( rProp == DAVProperties::GETCONTENTTYPE ) )
+ {
+ bHasGetContentType = true;
+ }
+ else if ( !bHasGetContentLength &&
+ ( rProp == DAVProperties::GETCONTENTLENGTH ) )
+ {
+ bHasGetContentLength = true;
+ }
+ else if ( !bHasContentType && rProp == "ContentType" )
+ {
+ bHasContentType = true;
+ }
+ else if ( !bHasIsDocument && rProp == "IsDocument" )
+ {
+ bHasIsDocument = true;
+ }
+ else if ( !bHasIsFolder && rProp == "IsFolder" )
+ {
+ bHasIsFolder = true;
+ }
+ else if ( !bHasTitle && rProp == "Title" )
+ {
+ bHasTitle = true;
+ }
+ else if ( !bHasBaseURI && rProp == "BaseURI" )
+ {
+ bHasBaseURI = true;
+ }
+ else if ( !bHasDateCreated && rProp == "DateCreated" )
+ {
+ bHasDateCreated = true;
+ }
+ else if ( !bHasDateModified && rProp == "DateModified" )
+ {
+ bHasDateModified = true;
+ }
+ else if ( !bHasMediaType && rProp == "MediaType" )
+ {
+ bHasMediaType = true;
+ }
+ else if ( !bHasSize && rProp == "Size" )
+ {
+ bHasSize = true;
+ }
+ else if ( !bHasCreatableInfos && rProp == "CreatableContentsInfo" )
+ {
+ bHasCreatableInfos = true;
+ }
+ }
+ }
+
+ // Add mandatory properties.
+ if ( !bHasContentType )
+ aPropSet.insert(
+ OUString( "ContentType" ) );
+
+ if ( !bHasIsDocument )
+ aPropSet.insert(
+ OUString( "IsDocument" ) );
+
+ if ( !bHasIsFolder )
+ aPropSet.insert(
+ OUString( "IsFolder" ) );
+
+ if ( !bHasTitle )
+ {
+ // Always present since it can be calculated from content's URI.
+ aPropSet.insert(
+ OUString( "Title" ) );
+ }
+
+ // Add optional properties.
+
+ if ( !bHasBaseURI )
+ {
+ // Always present since it can be calculated from content's URI.
+ aPropSet.insert(
+ OUString( "BaseURI" ) );
+ }
+
+ if ( !bHasDateCreated && bHasCreationDate )
+ aPropSet.insert(
+ OUString( "DateCreated" ) );
+
+ if ( !bHasDateModified && bHasGetLastModified )
+ aPropSet.insert(
+ OUString( "DateModified" ) );
+
+ if ( !bHasMediaType && bHasGetContentType )
+ aPropSet.insert(
+ OUString( "MediaType" ) );
+
+ if ( !bHasSize && bHasGetContentLength )
+ aPropSet.insert(
+ OUString( "Size" ) );
+
+ if ( !bHasCreatableInfos )
+ aPropSet.insert(
+ OUString(
+ "CreatableContentsInfo" ) );
+
+ // Add cached properties, if present and still missing.
+ if ( xCachedProps.get() )
+ {
+ const std::unique_ptr< PropertyValueMap > & xProps
+ = xCachedProps->getProperties();
+
+ for ( const auto& rEntry : *xProps )
+ aPropSet.insert( rEntry.first );
+ }
+
+ // std::set -> uno::Sequence
+ sal_Int32 nCount = aPropSet.size();
+ uno::Sequence< beans::Property > aProperties( nCount );
+
+ beans::Property aProp;
+ sal_Int32 n = 0;
+
+ for ( const auto& rProp : aPropSet )
+ {
+ xProvider->getProperty( rProp, aProp );
+ aProperties[ n++ ] = aProp;
+ }
+
+ return aProperties;
+}
+
+
+// virtual
+uno::Sequence< ucb::CommandInfo > Content::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< ucb::CommandInfo > aCmdInfo( 10 );
+
+
+ // Mandatory commands
+
+
+ aCmdInfo[ 0 ] =
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get() );
+ aCmdInfo[ 1 ] =
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get() );
+ aCmdInfo[ 2 ] =
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get());
+ aCmdInfo[ 3 ] =
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get());
+
+
+ // Optional standard commands
+
+
+ aCmdInfo[ 4 ] =
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get() );
+ aCmdInfo[ 5 ] =
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<ucb::InsertCommandArgument>::get() );
+ aCmdInfo[ 6 ] =
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get() );
+
+
+ // New commands
+
+
+ aCmdInfo[ 7 ] =
+ ucb::CommandInfo(
+ "post",
+ -1,
+ cppu::UnoType<ucb::PostCommandArgument2>::get() );
+ aCmdInfo[ 8 ] =
+ ucb::CommandInfo(
+ "addProperty",
+ -1,
+ cppu::UnoType<ucb::PropertyCommandArgument>::get() );
+ aCmdInfo[ 9 ] =
+ ucb::CommandInfo(
+ "removeProperty",
+ -1,
+ cppu::UnoType<OUString>::get() );
+
+ bool bFolder = false;
+
+ try
+ {
+ bFolder = isFolder( xEnv );
+ }
+ catch ( uno::Exception const & )
+ {
+ return aCmdInfo;
+ }
+
+ bool bSupportsLocking = supportsExclusiveWriteLock( xEnv );
+
+ sal_Int32 nPos = aCmdInfo.getLength();
+ sal_Int32 nMoreCmds = ( bFolder ? 2 : 0 ) + ( bSupportsLocking ? 2 : 0 );
+ if ( nMoreCmds )
+ aCmdInfo.realloc( nPos + nMoreCmds );
+ else
+ return aCmdInfo;
+
+ if ( bFolder )
+ {
+
+ // Optional standard commands
+
+
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get() );
+ nPos++;
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get() );
+ nPos++;
+ }
+ else
+ {
+ // no document-only commands at the moment.
+ }
+
+ if ( bSupportsLocking )
+ {
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "lock",
+ -1,
+ cppu::UnoType<void>::get() );
+ nPos++;
+ aCmdInfo[ nPos ] =
+ ucb::CommandInfo(
+ "unlock",
+ -1,
+ cppu::UnoType<void>::get() );
+ nPos++;
+ }
+ return aCmdInfo;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavdatasupplier.cxx b/ucb/source/ucp/webdav/webdavdatasupplier.cxx
new file mode 100644
index 000000000..6001b8665
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavdatasupplier.cxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <utility>
+
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include "webdavdatasupplier.hxx"
+#include "webdavcontent.hxx"
+#include "ContentProperties.hxx"
+#include "DAVProperties.hxx"
+#include "SerfUri.hxx"
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/ResultSetException.hpp>
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+namespace http_dav_ucp
+{
+
+
+// struct ResultListEntry.
+
+namespace {
+
+struct ResultListEntry
+{
+ OUString aId;
+ uno::Reference< ucb::XContentIdentifier > xId;
+ uno::Reference< ucb::XContent > xContent;
+ uno::Reference< sdbc::XRow > xRow;
+ std::unique_ptr<ContentProperties> pData;
+
+ explicit ResultListEntry( std::unique_ptr<ContentProperties> && pEntry ) : pData( std::move(pEntry) ) {}
+};
+
+}
+
+// ResultList.
+
+
+typedef std::vector< ResultListEntry* > ResultList;
+
+
+// struct DataSupplier_Impl.
+
+
+struct DataSupplier_Impl
+{
+ osl::Mutex m_aMutex;
+ ResultList m_aResults;
+ rtl::Reference< Content > m_xContent;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ sal_Int32 m_nOpenMode;
+ bool m_bCountFinal;
+ bool m_bThrowException;
+
+ DataSupplier_Impl(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode )
+ : m_xContent( rContent ), m_xContext( rxContext ), m_nOpenMode( nOpenMode ),
+ m_bCountFinal( false ), m_bThrowException( false ) {}
+ ~DataSupplier_Impl();
+};
+
+
+DataSupplier_Impl::~DataSupplier_Impl()
+{
+ for ( auto& rResultPtr : m_aResults )
+ {
+ delete rResultPtr;
+ }
+}
+
+}
+
+
+// DataSupplier Implementation.
+
+
+DataSupplier::DataSupplier(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode )
+: m_pImpl(std::make_unique<DataSupplier_Impl>(rxContext, rContent, nOpenMode))
+{
+}
+
+
+// virtual
+DataSupplier::~DataSupplier()
+{}
+
+
+// virtual
+OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( nIndex < m_pImpl->m_aResults.size() )
+ {
+ OUString aId = m_pImpl->m_aResults[ nIndex ]->aId;
+ if ( aId.getLength() )
+ {
+ // Already cached.
+ return aId;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ OUString aId = m_pImpl->m_xContent->getResourceAccess().getURL();
+
+ const ContentProperties& props
+ = *( m_pImpl->m_aResults[ nIndex ]->pData );
+
+ if ( ( aId.lastIndexOf( '/' ) + 1 ) != aId.getLength() )
+ aId += "/";
+
+ aId += props.getEscapedTitle();
+
+ if ( props.isTrailingSlash() )
+ aId += "/";
+
+ m_pImpl->m_aResults[ nIndex ]->aId = aId;
+ return aId;
+ }
+ return OUString();
+}
+
+
+// virtual
+uno::Reference< ucb::XContentIdentifier >
+DataSupplier::queryContentIdentifier( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( nIndex < m_pImpl->m_aResults.size() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = m_pImpl->m_aResults[ nIndex ]->xId;
+ if ( xId.is() )
+ {
+ // Already cached.
+ return xId;
+ }
+ }
+
+ OUString aId = queryContentIdentifierString( nIndex );
+ if ( aId.getLength() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aId );
+ m_pImpl->m_aResults[ nIndex ]->xId = xId;
+ return xId;
+ }
+ return uno::Reference< ucb::XContentIdentifier >();
+}
+
+
+// virtual
+uno::Reference< ucb::XContent >
+DataSupplier::queryContent( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( nIndex < m_pImpl->m_aResults.size() )
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_pImpl->m_aResults[ nIndex ]->xContent;
+ if ( xContent.is() )
+ {
+ // Already cached.
+ return xContent;
+ }
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = queryContentIdentifier( nIndex );
+ if ( xId.is() )
+ {
+ try
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_pImpl->m_xContent->getProvider()->queryContent( xId );
+ m_pImpl->m_aResults[ nIndex ]->xContent = xContent;
+ return xContent;
+
+ }
+ catch ( ucb::IllegalIdentifierException& )
+ {
+ }
+ }
+ return uno::Reference< ucb::XContent >();
+}
+
+
+// virtual
+bool DataSupplier::getResult( sal_uInt32 nIndex )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( m_pImpl->m_aResults.size() > nIndex )
+ {
+ // Result already present.
+ return true;
+ }
+
+ // Obtain values...
+ if ( getData() )
+ {
+ if ( m_pImpl->m_aResults.size() > nIndex )
+ {
+ // Result already present.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// virtual
+sal_uInt32 DataSupplier::totalCount()
+{
+ // Obtain values...
+ getData();
+
+ return m_pImpl->m_aResults.size();
+}
+
+
+// virtual
+sal_uInt32 DataSupplier::currentCount()
+{
+ return m_pImpl->m_aResults.size();
+}
+
+
+// virtual
+bool DataSupplier::isCountFinal()
+{
+ return m_pImpl->m_bCountFinal;
+}
+
+
+// virtual
+uno::Reference< sdbc::XRow > DataSupplier::queryPropertyValues(
+ sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( nIndex < m_pImpl->m_aResults.size() )
+ {
+ uno::Reference< sdbc::XRow > xRow = m_pImpl->m_aResults[ nIndex ]->xRow;
+ if ( xRow.is() )
+ {
+ // Already cached.
+ return xRow;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ uno::Reference< sdbc::XRow > xRow
+ = Content::getPropertyValues(
+ m_pImpl->m_xContext,
+ getResultSet()->getProperties(),
+ *(m_pImpl->m_aResults[ nIndex ]->pData),
+ rtl::Reference< ::ucbhelper::ContentProviderImplHelper >(
+ m_pImpl->m_xContent->getProvider().get() ),
+ queryContentIdentifierString( nIndex ) );
+ m_pImpl->m_aResults[ nIndex ]->xRow = xRow;
+ return xRow;
+ }
+
+ return uno::Reference< sdbc::XRow >();
+}
+
+
+// virtual
+void DataSupplier::releasePropertyValues( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( nIndex < m_pImpl->m_aResults.size() )
+ m_pImpl->m_aResults[ nIndex ]->xRow.clear();
+}
+
+
+// virtual
+void DataSupplier::close()
+{
+}
+
+
+// virtual
+void DataSupplier::validate()
+{
+ if ( m_pImpl->m_bThrowException )
+ throw ucb::ResultSetException();
+}
+
+bool DataSupplier::getData()
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex );
+
+ if ( !m_pImpl->m_bCountFinal )
+ {
+ std::vector< OUString > propertyNames;
+ ContentProperties::UCBNamesToDAVNames(
+ getResultSet()->getProperties(), propertyNames );
+
+ // Append "resourcetype", if not already present. It's value is
+ // needed to get a valid ContentProperties::pIsFolder value, which
+ // is needed for OpenMode handling.
+
+ bool isNoResourceType = std::none_of(propertyNames.begin(), propertyNames.end(),
+ [](const OUString& rPropName) { return rPropName.equals(DAVProperties::RESOURCETYPE); });
+
+ if ( isNoResourceType )
+ propertyNames.push_back( DAVProperties::RESOURCETYPE );
+
+ std::vector< DAVResource > resources;
+ try
+ {
+ // propfind depth 1, get property values for parent AND for each
+ // child
+ m_pImpl->m_xContent->getResourceAccess()
+ .PROPFIND( DAVONE,
+ propertyNames,
+ resources,
+ getResultSet()->getEnvironment() );
+ }
+ catch ( DAVException & )
+ {
+ SAL_WARN( "ucb.ucp.webdav", "PROPFIND : DAVException" );
+ m_pImpl->m_bThrowException = true;
+ }
+
+ if ( !m_pImpl->m_bThrowException )
+ {
+ try
+ {
+ SerfUri aURI(
+ m_pImpl->m_xContent->getResourceAccess().getURL() );
+ OUString aPath = aURI.GetPath();
+
+ if ( aPath.endsWith("/") )
+ aPath = aPath.copy( 0, aPath.getLength() - 1 );
+
+ aPath = SerfUri::unescape( aPath );
+ bool bFoundParent = false;
+
+ for ( size_t n = 0; n < resources.size(); ++n )
+ {
+ const DAVResource & rRes = resources[ n ];
+
+ // Filter parent, which is contained somewhere(!) in
+ // the vector.
+ if ( !bFoundParent )
+ {
+ try
+ {
+ SerfUri aCurrURI( rRes.uri );
+ OUString aCurrPath = aCurrURI.GetPath();
+ if ( aCurrPath.endsWith("/") )
+ aCurrPath
+ = aCurrPath.copy(
+ 0,
+ aCurrPath.getLength() - 1 );
+
+ aCurrPath = SerfUri::unescape( aCurrPath );
+ if ( aPath == aCurrPath )
+ {
+ bFoundParent = true;
+ continue;
+ }
+ }
+ catch ( DAVException const & )
+ {
+ // do nothing, ignore error. continue.
+ }
+ }
+
+ std::unique_ptr<ContentProperties> pContentProperties
+ = std::make_unique<ContentProperties>( rRes );
+
+ // Check resource against open mode.
+ switch ( m_pImpl->m_nOpenMode )
+ {
+ case ucb::OpenMode::FOLDERS:
+ {
+ bool bFolder = false;
+
+ const uno::Any & rValue
+ = pContentProperties->getValue( "IsFolder" );
+ rValue >>= bFolder;
+
+ if ( !bFolder )
+ continue;
+
+ break;
+ }
+
+ case ucb::OpenMode::DOCUMENTS:
+ {
+ bool bDocument = false;
+
+ const uno::Any & rValue
+ = pContentProperties->getValue( "IsDocument" );
+ rValue >>= bDocument;
+
+ if ( !bDocument )
+ continue;
+
+ break;
+ }
+
+ case ucb::OpenMode::ALL:
+ default:
+ break;
+ }
+
+ m_pImpl->m_aResults.push_back(
+ new ResultListEntry( std::move(pContentProperties) ) );
+ }
+ }
+ catch ( DAVException const & )
+ {
+ }
+ }
+
+ m_pImpl->m_bCountFinal = true;
+
+ // Callback possible, because listeners may be informed!
+ aGuard.clear();
+ getResultSet()->rowCountFinal();
+ }
+ return !m_pImpl->m_bThrowException;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavdatasupplier.hxx b/ucb/source/ucp/webdav/webdavdatasupplier.hxx
new file mode 100644
index 000000000..ecd22000e
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavdatasupplier.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVDATASUPPLIER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVDATASUPPLIER_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+
+namespace http_dav_ucp {
+
+struct DataSupplier_Impl;
+class Content;
+struct DAVResource;
+class ContentProperties;
+
+class DataSupplier : public ucbhelper::ResultSetDataSupplier
+{
+ std::unique_ptr<DataSupplier_Impl> m_pImpl;
+
+private:
+ bool getData();
+
+public:
+ DataSupplier( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent,
+ sal_Int32 nOpenMode);
+
+ virtual ~DataSupplier() override;
+
+ virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier >
+ queryContentIdentifier( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContent >
+ queryContent( sal_uInt32 nIndex ) override;
+
+ virtual bool getResult( sal_uInt32 nIndex ) override;
+
+ virtual sal_uInt32 totalCount() override;
+ virtual sal_uInt32 currentCount() override;
+ virtual bool isCountFinal() override;
+
+ virtual css::uno::Reference< css::sdbc::XRow >
+ queryPropertyValues( sal_uInt32 nIndex ) override;
+ virtual void releasePropertyValues( sal_uInt32 nIndex ) override;
+
+ virtual void close() override;
+
+ virtual void validate() override;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavprovider.cxx b/ucb/source/ucp/webdav/webdavprovider.cxx
new file mode 100644
index 000000000..6c1d77b94
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavprovider.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/getcomponentcontext.hxx>
+#include <ucbhelper/macros.hxx>
+#include "webdavprovider.hxx"
+#include "webdavcontent.hxx"
+
+#include <cppuhelper/queryinterface.hxx>
+#include <osl/mutex.hxx>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+
+#include <tools/urlobj.hxx>
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+
+// ContentProvider Implementation.
+
+
+ContentProvider::ContentProvider(
+ const uno::Reference< uno::XComponentContext >& rContext )
+: ::ucbhelper::ContentProviderImplHelper( rContext ),
+ m_xDAVSessionFactory( new DAVSessionFactory )
+{
+}
+
+
+// virtual
+ContentProvider::~ContentProvider()
+{}
+
+
+// XInterface methods.
+void SAL_CALL ContentProvider::acquire()
+ throw()
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ContentProvider::release()
+ throw()
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XTypeProvider* >(this),
+ static_cast< lang::XServiceInfo* >(this),
+ static_cast< ucb::XContentProvider* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_IMPL_3( ContentProvider,
+ lang::XTypeProvider,
+ lang::XServiceInfo,
+ ucb::XContentProvider );
+
+
+// XServiceInfo methods.
+
+XSERVICEINFO_COMMOM_IMPL( ContentProvider,
+ "com.sun.star.comp.WebDAVContentProvider" )
+/// @throws css::uno::Exception
+static css::uno::Reference< css::uno::XInterface >
+ContentProvider_CreateInstance( const css::uno::Reference< css::lang::XMultiServiceFactory> & rSMgr )
+{
+ css::lang::XServiceInfo* pX =
+ static_cast<css::lang::XServiceInfo*>(new ContentProvider( ucbhelper::getComponentContext(rSMgr) ));
+ return css::uno::Reference< css::uno::XInterface >::query( pX );
+}
+
+css::uno::Sequence< OUString >
+ContentProvider::getSupportedServiceNames_Static()
+{
+ css::uno::Sequence< OUString > aSNS { WEBDAV_CONTENT_PROVIDER_SERVICE_NAME };
+ return aSNS;
+}
+
+// Service factory implementation.
+
+
+ONE_INSTANCE_SERVICE_FACTORY_IMPL( ContentProvider );
+
+
+// XContentProvider methods.
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+ContentProvider::queryContent(
+ const uno::Reference<
+ ucb::XContentIdentifier >& Identifier )
+{
+ // Check URL scheme...
+ INetURLObject aURL(Identifier->getContentIdentifier());
+
+ if (aURL.isSchemeEqualTo(INetProtocol::NotValid))
+ throw ucb::IllegalIdentifierException();
+
+ if (!aURL.isAnyKnownWebDAVScheme())
+ throw ucb::IllegalIdentifierException();
+
+ uno::Reference< ucb::XContentIdentifier > xCanonicId;
+
+ if (aURL.isSchemeEqualTo(INetProtocol::VndSunStarWebdav) ||
+ aURL.isSchemeEqualTo(DAV_URL_SCHEME) ||
+ aURL.isSchemeEqualTo(WEBDAV_URL_SCHEME))
+ {
+ aURL.changeScheme(INetProtocol::Http);
+ xCanonicId = new ::ucbhelper::ContentIdentifier( aURL.getExternalURL() );
+ }
+ else if (aURL.isSchemeEqualTo(VNDSUNSTARWEBDAVS_URL_SCHEME) ||
+ aURL.isSchemeEqualTo(DAVS_URL_SCHEME) ||
+ aURL.isSchemeEqualTo(WEBDAVS_URL_SCHEME))
+ {
+ aURL.changeScheme(INetProtocol::Https);
+ xCanonicId = new ::ucbhelper::ContentIdentifier( aURL.getExternalURL() );
+ }
+ else
+ {
+ xCanonicId = Identifier;
+ }
+
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xCanonicId ).get();
+ if ( xContent.is() )
+ return xContent;
+
+ // Create a new content.
+
+ try
+ {
+ xContent = new ::http_dav_ucp::Content(
+ m_xContext, this, xCanonicId, m_xDAVSessionFactory );
+ registerNewContent( xContent );
+ }
+ catch ( ucb::ContentCreationException const & )
+ {
+ throw ucb::IllegalIdentifierException();
+ }
+
+ if ( !xContent->getIdentifier().is() )
+ throw ucb::IllegalIdentifierException();
+
+ return xContent;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavprovider.hxx b/ucb/source/ucp/webdav/webdavprovider.hxx
new file mode 100644
index 000000000..e399178a9
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavprovider.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVPROVIDER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVPROVIDER_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <rtl/ref.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include "DAVSessionFactory.hxx"
+#include <ucbhelper/providerhelper.hxx>
+#include "PropertyMap.hxx"
+
+namespace com::sun::star::lang {
+class XSingleServiceFactory;
+}
+
+namespace http_dav_ucp {
+
+
+// UNO service name for the provider. This name will be used by the UCB to
+// create instances of the provider.
+#define WEBDAV_CONTENT_PROVIDER_SERVICE_NAME "com.sun.star.ucb.WebDAVContentProvider"
+
+// URL scheme. This is the scheme the provider will be able to create
+// contents for. The UCB will select the provider ( i.e. in order to create
+// contents ) according to this scheme.
+#define VNDSUNSTARWEBDAV_URL_SCHEME "vnd.sun.star.webdav"
+#define VNDSUNSTARWEBDAVS_URL_SCHEME u"vnd.sun.star.webdavs"
+#define HTTP_URL_SCHEME "http"
+#define HTTPS_URL_SCHEME "https"
+#define DAV_URL_SCHEME u"dav"
+#define DAVS_URL_SCHEME u"davs"
+#define WEBDAV_URL_SCHEME u"webdav"
+#define WEBDAVS_URL_SCHEME u"webdavs"
+
+#define HTTP_CONTENT_TYPE "application/" HTTP_URL_SCHEME "-content"
+
+#define WEBDAV_CONTENT_TYPE HTTP_CONTENT_TYPE
+#define WEBDAV_COLLECTION_TYPE "application/" VNDSUNSTARWEBDAV_URL_SCHEME "-collection"
+
+
+class ContentProvider : public ::ucbhelper::ContentProviderImplHelper
+{
+ rtl::Reference< DAVSessionFactory > m_xDAVSessionFactory;
+ std::unique_ptr<PropertyMap> m_pProps;
+
+public:
+ explicit ContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rContext );
+ virtual ~ContentProvider() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ throw() override;
+ virtual void SAL_CALL release()
+ throw() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ static OUString getImplementationName_Static();
+ static css::uno::Sequence< OUString > getSupportedServiceNames_Static();
+
+ static css::uno::Reference< css::lang::XSingleServiceFactory >
+ createServiceFactory( const css::uno::Reference<
+ css::lang::XMultiServiceFactory >& rxServiceMgr );
+
+ // XContentProvider
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override;
+
+
+ // Non-interface methods.
+
+ bool getProperty( const OUString & rPropName,
+ css::beans::Property & rProp,
+ bool bStrict = false );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavresponseparser.cxx b/ucb/source/ucp/webdav/webdavresponseparser.cxx
new file mode 100644
index 000000000..b03a392d5
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavresponseparser.cxx
@@ -0,0 +1,897 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "webdavresponseparser.hxx"
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/ucb/LockEntry.hpp>
+#include <com/sun/star/ucb/LockScope.hpp>
+#include <com/sun/star/ucb/LockType.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <map>
+#include <unordered_map>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+
+// WebDAVNamespace enum and StringToEnum converter
+namespace
+{
+ enum WebDAVNamespace
+ {
+ WebDAVNamespace_unknown = 0,
+ WebDAVNamespace_DAV,
+ WebDAVNamespace_ucb_openoffice_org_dav_props,
+
+ WebDAVNamespace_last
+ };
+
+ WebDAVNamespace StrToWebDAVNamespace(const OUString& rStr)
+ {
+ if(rStr == "DAV:")
+ {
+ return WebDAVNamespace_DAV;
+ }
+ else if(rStr == "http://ucb.openoffice.org/dav/props/")
+ {
+ return WebDAVNamespace_ucb_openoffice_org_dav_props;
+ }
+
+ return WebDAVNamespace_unknown;
+ }
+} // end of anonymous namespace
+
+// WebDAVName enum and StringToEnum converter using unordered_map
+namespace
+{
+ enum WebDAVName
+ {
+ WebDAVName_unknown = 0,
+ WebDAVName_activelock,
+ WebDAVName_multistatus,
+ WebDAVName_response,
+ WebDAVName_href,
+ WebDAVName_propstat,
+ WebDAVName_prop,
+ WebDAVName_resourcetype,
+ WebDAVName_collection,
+ WebDAVName_getcontenttype,
+ WebDAVName_supportedlock,
+ WebDAVName_lockentry,
+ WebDAVName_lockscope,
+ WebDAVName_locktoken,
+ WebDAVName_exclusive,
+ WebDAVName_locktype,
+ WebDAVName_owner,
+ WebDAVName_timeout,
+ WebDAVName_write,
+ WebDAVName_shared,
+ WebDAVName_status,
+ WebDAVName_getlastmodified,
+ WebDAVName_creationdate,
+ WebDAVName_getcontentlength,
+
+ WebDAVName_last
+ };
+
+ WebDAVName StrToWebDAVName(const OUString& rStr)
+ {
+ typedef std::unordered_map< OUString, WebDAVName > WebDAVNameMapper;
+ typedef std::pair< OUString, WebDAVName > WebDAVNameValueType;
+ static WebDAVNameMapper aWebDAVNameMapperList;
+
+ if(aWebDAVNameMapperList.empty())
+ {
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("activelock"), WebDAVName_activelock));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("multistatus"), WebDAVName_multistatus));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("response"), WebDAVName_response));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("href"), WebDAVName_href));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("propstat"), WebDAVName_propstat));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("prop"), WebDAVName_prop));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("resourcetype"), WebDAVName_resourcetype));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("collection"), WebDAVName_collection));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("getcontenttype"), WebDAVName_getcontenttype));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("supportedlock"), WebDAVName_supportedlock));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("lockentry"), WebDAVName_lockentry));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("lockscope"), WebDAVName_lockscope));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("locktoken"), WebDAVName_locktoken));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("exclusive"), WebDAVName_exclusive));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("locktype"), WebDAVName_locktype));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("owner"), WebDAVName_owner));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("timeout"), WebDAVName_timeout));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("write"), WebDAVName_write));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("shared"), WebDAVName_shared));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("status"), WebDAVName_status));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("getlastmodified"), WebDAVName_getlastmodified));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("creationdate"), WebDAVName_creationdate));
+ aWebDAVNameMapperList.insert(WebDAVNameValueType(OUString("getcontentlength"), WebDAVName_getcontentlength));
+ }
+
+ const WebDAVNameMapper::const_iterator aResult(aWebDAVNameMapperList.find(rStr));
+
+ if(aResult == aWebDAVNameMapperList.end())
+ {
+ return WebDAVName_unknown;
+ }
+ else
+ {
+ return aResult->second;
+ }
+ }
+} // end of anonymous namespace
+
+
+// WebDAVContext, holding information for each start/endElement pair
+
+namespace
+{
+ typedef std::map< OUString, OUString > NamespaceMap;
+
+ class WebDAVContext
+ {
+ private:
+ WebDAVContext* mpParent;
+ NamespaceMap maNamespaceMap;
+ OUString maWhiteSpace;
+
+ OUString maNamespace;
+ OUString maName;
+
+ WebDAVNamespace maWebDAVNamespace;
+ WebDAVName maWebDAVName;
+
+ // local helpers
+ void parseForNamespaceTokens(const uno::Reference< xml::sax::XAttributeList >& xAttribs);
+ OUString mapNamespaceToken(const OUString& rToken) const;
+ void splitName(const OUString& rSource);
+
+ public:
+ WebDAVContext(WebDAVContext* pParent, const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs);
+
+ WebDAVContext* getParent() const { return mpParent; }
+ OUString& getWhiteSpace() { return maWhiteSpace; }
+ void setWhiteSpace(const OUString& rNew) { maWhiteSpace = rNew; }
+
+ const OUString& getNamespace() const { return maNamespace; }
+ const OUString& getName() const { return maName; }
+ const WebDAVNamespace& getWebDAVNamespace() const { return maWebDAVNamespace; }
+ const WebDAVName& getWebDAVName() const { return maWebDAVName; }
+ };
+
+ void WebDAVContext::parseForNamespaceTokens(const uno::Reference< xml::sax::XAttributeList >& xAttribs)
+ {
+ const sal_Int16 nAttributes(xAttribs->getLength());
+
+ for(sal_Int16 a(0); a < nAttributes; a++)
+ {
+ const OUString aName(xAttribs->getNameByIndex(a));
+ const sal_Int32 nLen(aName.getLength());
+
+ if(nLen)
+ {
+ if(aName.startsWith("xmlns"))
+ {
+ const sal_Int32 nIndex(aName.indexOf(':', 0));
+
+ if(-1 != nIndex && nIndex + 1 < nLen)
+ {
+ const OUString aToken(aName.copy(nIndex + 1));
+
+ maNamespaceMap.emplace(aToken, xAttribs->getValueByIndex(a));
+ }
+ }
+ }
+ }
+ }
+
+ OUString WebDAVContext::mapNamespaceToken(const OUString& rToken) const
+ {
+ NamespaceMap::const_iterator iter = maNamespaceMap.find(rToken);
+
+ if(maNamespaceMap.end() == iter)
+ {
+ if(getParent())
+ {
+ return getParent()->mapNamespaceToken(rToken);
+ }
+ else
+ {
+ return rToken;
+ }
+ }
+ else
+ {
+ return (*iter).second;
+ }
+ }
+
+ void WebDAVContext::splitName(const OUString& rSource)
+ {
+ const sal_Int32 nLen(rSource.getLength());
+ maNamespace.clear();
+ maName = rSource;
+
+ if(nLen)
+ {
+ const sal_Int32 nIndex(rSource.indexOf(':', 0));
+
+ if(nIndex > 0 && ((nIndex + 1) < nLen))
+ {
+ maNamespace = mapNamespaceToken(rSource.copy(0, nIndex));
+ maName = rSource.copy(nIndex + 1);
+ }
+ }
+ }
+
+ WebDAVContext::WebDAVContext(WebDAVContext* pParent, const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs)
+ : mpParent(pParent),
+ maNamespaceMap(),
+ maWhiteSpace(),
+ maNamespace(),
+ maName(),
+ maWebDAVNamespace(WebDAVNamespace_unknown),
+ maWebDAVName(WebDAVName_unknown)
+ {
+ const sal_Int16 nAttributes(xAttribs->getLength());
+
+ if(nAttributes)
+ {
+ // parse evtl. namespace entries
+ parseForNamespaceTokens(xAttribs);
+ }
+
+ // split name to namespace and name
+ splitName(aName);
+
+ // evaluate enums for namespace and name
+ maWebDAVNamespace = StrToWebDAVNamespace(maNamespace);
+ maWebDAVName = StrToWebDAVName(maName);
+ }
+} // end of anonymous namespace
+
+
+// the Xml parser itself
+
+namespace
+{
+ enum WebDAVResponseParserMode
+ {
+ WebDAVResponseParserMode_PropFind = 0,
+ WebDAVResponseParserMode_PropName,
+ WebDAVResponseParserMode_Lock
+ };
+
+ class WebDAVResponseParser : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
+ {
+ private:
+ std::vector< ucb::Lock > maResult_Lock;
+ std::vector< http_dav_ucp::DAVResource > maResult_PropFind;
+ std::vector< http_dav_ucp::DAVResourceInfo > maResult_PropName;
+
+ WebDAVContext* mpContext;
+ OUString maHref;
+ OUString maStatus;
+ std::vector< http_dav_ucp::DAVPropertyValue > maResponseProperties;
+ std::vector< http_dav_ucp::DAVPropertyValue > maPropStatProperties;
+ std::vector< OUString > maResponseNames;
+ std::vector< OUString > maPropStatNames;
+ uno::Sequence< ucb::LockEntry > maLockEntries;
+ ucb::LockScope maLockScope;
+ ucb::LockType maLockType;
+ ucb::Lock maLock;
+ WebDAVResponseParserMode meWebDAVResponseParserMode;
+
+ bool mbResourceTypeCollection : 1;
+ bool mbLockScopeSet : 1;
+ bool mbLockTypeSet : 1;
+
+ // local helpers
+ bool whitespaceIsAvailable() const
+ {
+ return mpContext && mpContext->getWhiteSpace().getLength();
+ }
+ bool hasParent(WebDAVName aWebDAVName) const
+ {
+ return mpContext && mpContext->getParent() && aWebDAVName == mpContext->getParent()->getWebDAVName();
+ }
+ bool propertyIsReady() const
+ {
+ return hasParent(WebDAVName_prop) && whitespaceIsAvailable();
+ }
+ bool isCollectingProperties() const
+ {
+ return WebDAVResponseParserMode_PropFind == meWebDAVResponseParserMode;
+ }
+ bool isCollectingPropNames() const
+ {
+ return WebDAVResponseParserMode_PropName == meWebDAVResponseParserMode;
+ }
+ bool collectThisPropertyAsName() const
+ {
+ return isCollectingPropNames() && hasParent(WebDAVName_prop);
+ }
+ void pop_context()
+ {
+ if(mpContext)
+ {
+ WebDAVContext* pTemp = mpContext;
+ mpContext = mpContext->getParent();
+ delete pTemp;
+ }
+ else
+ {
+ SAL_WARN( "ucb.ucp.webdav", "Parser context pop without context (!)");
+ }
+ }
+
+ public:
+ explicit WebDAVResponseParser(WebDAVResponseParserMode eWebDAVResponseParserMode);
+ virtual ~WebDAVResponseParser() override;
+
+ // Methods XDocumentHandler
+ virtual void SAL_CALL startDocument( ) override;
+ virtual void SAL_CALL endDocument( ) override;
+ virtual void SAL_CALL startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs ) override;
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+ virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
+ virtual void SAL_CALL setDocumentLocator( const uno::Reference< xml::sax::XLocator >& xLocator ) override;
+
+ const std::vector< ucb::Lock >& getResult_Lock() const { return maResult_Lock; }
+ const std::vector< http_dav_ucp::DAVResource >& getResult_PropFind() const { return maResult_PropFind; }
+ const std::vector< http_dav_ucp::DAVResourceInfo >& getResult_PropName() const { return maResult_PropName; }
+ };
+
+ WebDAVResponseParser::WebDAVResponseParser(WebDAVResponseParserMode eWebDAVResponseParserMode)
+ : maResult_PropFind(),
+ maResult_PropName(),
+ mpContext(nullptr),
+ maHref(),
+ maStatus(),
+ maResponseProperties(),
+ maPropStatProperties(),
+ maResponseNames(),
+ maPropStatNames(),
+ maLockEntries(),
+ maLockScope(ucb::LockScope_EXCLUSIVE),
+ maLockType(ucb::LockType_WRITE),
+ meWebDAVResponseParserMode(eWebDAVResponseParserMode),
+ mbResourceTypeCollection(false),
+ mbLockScopeSet(false),
+ mbLockTypeSet(false)
+ {
+ }
+
+ WebDAVResponseParser::~WebDAVResponseParser()
+ {
+ SAL_WARN_IF(mpContext, "ucb.ucp.webdav", "Parser destructed with existing content (!)");
+ while(mpContext)
+ {
+ pop_context();
+ }
+ }
+
+ void SAL_CALL WebDAVResponseParser::startDocument( )
+ {
+ SAL_WARN_IF(mpContext, "ucb.ucp.webdav", "Parser start with existing content (!)");
+ }
+
+ void SAL_CALL WebDAVResponseParser::endDocument( )
+ {
+ SAL_WARN_IF(mpContext, "ucb.ucp.webdav", "Parser end with existing content (!)");
+ }
+
+ void SAL_CALL WebDAVResponseParser::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
+ {
+ const sal_Int32 nLen(aName.getLength());
+
+ if(nLen)
+ {
+ // create new context (push)
+ mpContext = new WebDAVContext(mpContext, aName, xAttribs);
+
+ if(collectThisPropertyAsName())
+ {
+ // When collecting property names and parent is prop there is no need
+ // to handle the content of this property deeper (evtl. preparations)
+ }
+ else
+ {
+ switch(mpContext->getWebDAVNamespace())
+ {
+ default: // WebDAVNamespace_unknown, WebDAVNamespace_last or unhandled
+ {
+ break;
+ }
+ case WebDAVNamespace_DAV:
+ {
+ switch(mpContext->getWebDAVName())
+ {
+ default: // WebDAVName_unknown, WebDAVName_last or unhandled
+ {
+ break;
+ }
+ case WebDAVName_propstat:
+ {
+ // propstat start
+ if(isCollectingProperties())
+ {
+ // reset maPropStatProperties
+ maPropStatProperties.clear();
+ }
+ else
+ {
+ // when collecting properties reset maPropStatNames
+ maPropStatNames.clear();
+ }
+ break;
+ }
+ case WebDAVName_response:
+ {
+ // response start, reset Href and status and maResponseProperties
+ maHref.clear();
+ maStatus.clear();
+
+ if(isCollectingProperties())
+ {
+ // reset maResponseProperties
+ maResponseProperties.clear();
+ }
+ else
+ {
+ // reset maResponseNames when collecting properties
+ maResponseNames.clear();
+ }
+ break;
+ }
+ case WebDAVName_resourcetype:
+ {
+ // resourcetype start, reset collection
+ mbResourceTypeCollection = false;
+ break;
+ }
+ case WebDAVName_supportedlock:
+ {
+ // supportedlock start, reset maLockEntries
+ maLockEntries.realloc(0);
+ break;
+ }
+ case WebDAVName_lockentry:
+ {
+ // lockentry start, reset maLockEntries
+ mbLockScopeSet = false;
+ mbLockTypeSet = false;
+ break;
+ }
+ case WebDAVName_activelock:
+ {
+ maLock = ucb::Lock();
+ break;
+ }
+ }
+ break;
+ }
+ case WebDAVNamespace_ucb_openoffice_org_dav_props:
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void SAL_CALL WebDAVResponseParser::endElement( const OUString& aName )
+ {
+ const sal_Int32 nLen(aName.getLength());
+ SAL_WARN_IF(!mpContext, "ucb.ucp.webdav", "Parser EndElement without content (!)");
+
+ if(mpContext && nLen)
+ {
+ if(collectThisPropertyAsName())
+ {
+ // When collecting property names and parent is prop, just append the prop name
+ // to the collection, no need to parse deeper
+ maPropStatNames.push_back(mpContext->getNamespace() + mpContext->getName());
+ }
+ else
+ {
+ switch(mpContext->getWebDAVNamespace())
+ {
+ default: // WebDAVNamespace_unknown, WebDAVNamespace_last or unhandled
+ {
+ break;
+ }
+ case WebDAVNamespace_DAV:
+ {
+ switch(mpContext->getWebDAVName())
+ {
+ default: // WebDAVName_unknown, WebDAVName_last or unhandled
+ {
+ break;
+ }
+ case WebDAVName_href:
+ {
+ // href end, save it if we have whitespace
+ if(whitespaceIsAvailable())
+ {
+ maHref = mpContext->getWhiteSpace();
+ }
+ break;
+ }
+ case WebDAVName_status:
+ {
+ // status end, save it if we have whitespace
+ if(whitespaceIsAvailable())
+ {
+ maStatus = mpContext->getWhiteSpace();
+ }
+ break;
+ }
+ case WebDAVName_getlastmodified:
+ {
+ // getlastmodified end, safe if content is correct
+ if(propertyIsReady())
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:getlastmodified";
+ aDAVPropertyValue.Value <<= mpContext->getWhiteSpace();
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_creationdate:
+ {
+ // creationdate end, safe if content is correct
+ if(propertyIsReady())
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:creationdate";
+ aDAVPropertyValue.Value <<= mpContext->getWhiteSpace();
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_collection:
+ {
+ // collection end, check and set
+ if(hasParent(WebDAVName_resourcetype))
+ {
+ mbResourceTypeCollection = true;
+ }
+ break;
+ }
+ case WebDAVName_resourcetype:
+ {
+ // resourcetype end, check for collection
+ if(hasParent(WebDAVName_prop))
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:resourcetype";
+ aDAVPropertyValue.Value <<= (mbResourceTypeCollection ? OUString("collection") : OUString());
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_getcontentlength:
+ {
+ // getcontentlength end, safe if content is correct
+ if(propertyIsReady())
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:getcontentlength";
+ aDAVPropertyValue.Value <<= mpContext->getWhiteSpace();
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_getcontenttype:
+ {
+ // getcontenttype end, safe if content is correct
+ if(propertyIsReady())
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:getcontenttype";
+ aDAVPropertyValue.Value <<= mpContext->getWhiteSpace();
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_supportedlock:
+ {
+ // supportedlock end
+ if(hasParent(WebDAVName_prop) && maLockEntries.hasElements())
+ {
+ http_dav_ucp::DAVPropertyValue aDAVPropertyValue;
+
+ aDAVPropertyValue.Name = "DAV:supportedlock";
+ aDAVPropertyValue.Value <<= maLockEntries;
+ maPropStatProperties.push_back(aDAVPropertyValue);
+ }
+ break;
+ }
+ case WebDAVName_lockentry:
+ {
+ // lockentry end
+ if(hasParent(WebDAVName_supportedlock) && (mbLockScopeSet && mbLockTypeSet))
+ {
+ const sal_Int32 nLength(maLockEntries.getLength());
+ ucb::LockEntry aEntry;
+
+ aEntry.Scope = maLockScope;
+ aEntry.Type = maLockType;
+ maLockEntries.realloc(nLength + 1);
+ maLockEntries[nLength] = aEntry;
+ }
+ break;
+ }
+ case WebDAVName_owner:
+ {
+ maLock.Owner <<= mpContext->getWhiteSpace();
+ break;
+ }
+ case WebDAVName_timeout:
+ {
+ const OUString sTimeout(mpContext->getWhiteSpace());
+ if (sTimeout == "Infinite")
+ maLock.Timeout = -1;
+ else if (sTimeout.startsWith("Second-"))
+ maLock.Timeout = sTimeout.copy(7).toInt64();
+ break;
+ }
+ case WebDAVName_locktoken:
+ {
+ const OUString sLockToken(maHref);
+ SAL_WARN_IF(!sLockToken.startsWith("opaquelocktoken:"), "ucb.ucp.webdav",
+ "Parser error: wrong 'locktoken' value.");
+ const sal_Int32 nLength(maLock.LockTokens.getLength());
+ maLock.LockTokens.realloc(nLength+1);
+ maLock.LockTokens[nLength] = sLockToken;
+ break;
+ }
+ case WebDAVName_exclusive:
+ {
+ // exclusive lockscope end
+ if(hasParent(WebDAVName_lockscope))
+ {
+ maLockScope = ucb::LockScope_EXCLUSIVE;
+ mbLockScopeSet = true;
+ }
+ break;
+ }
+ case WebDAVName_shared:
+ {
+ // shared lockscope end
+ if(hasParent(WebDAVName_lockscope))
+ {
+ maLockScope = ucb::LockScope_SHARED;
+ mbLockScopeSet = true;
+ }
+ break;
+ }
+ case WebDAVName_write:
+ {
+ // write locktype end
+ if(hasParent(WebDAVName_locktype))
+ {
+ maLockType = ucb::LockType_WRITE;
+ mbLockTypeSet = true;
+ }
+ break;
+ }
+ case WebDAVName_activelock:
+ {
+ maLock.Type = maLockType;
+ maLock.Scope = maLockScope;
+ maResult_Lock.push_back(maLock);
+ }
+ [[fallthrough]]; // I hope intentional?
+ case WebDAVName_propstat:
+ {
+ // propstat end, check status
+ if(maStatus.getLength())
+ {
+ if(maStatus == "HTTP/1.1 200 OK")
+ {
+ if(isCollectingProperties())
+ {
+ if(!maPropStatProperties.empty())
+ {
+ // append to maResponseProperties if okay
+ maResponseProperties.insert(maResponseProperties.end(), maPropStatProperties.begin(), maPropStatProperties.end());
+ }
+ }
+ else
+ {
+ if(!maPropStatNames.empty())
+ {
+ // when collecting properties append to
+ maResponseNames.insert(maResponseNames.end(), maPropStatNames.begin(), maPropStatNames.end());
+ }
+ }
+ }
+ }
+ break;
+ }
+ case WebDAVName_response:
+ {
+ // response end
+ if(maHref.getLength())
+ {
+ if(isCollectingProperties())
+ {
+ // create DAVResource when we have content
+ if(!maResponseProperties.empty())
+ {
+ http_dav_ucp::DAVResource aDAVResource;
+
+ aDAVResource.uri = maHref;
+ aDAVResource.properties = maResponseProperties;
+ maResult_PropFind.push_back(aDAVResource);
+ }
+ }
+ else
+ {
+ // when collecting properties add them to result when there are some
+ if(!maResponseNames.empty())
+ {
+ http_dav_ucp::DAVResourceInfo aDAVResourceInfo(maHref);
+
+ aDAVResourceInfo.properties = maResponseNames;
+ maResult_PropName.push_back(aDAVResourceInfo);
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case WebDAVNamespace_ucb_openoffice_org_dav_props:
+ {
+ break;
+ }
+ }
+ }
+
+ // destroy last context (pop)
+ pop_context();
+ }
+ }
+
+ void SAL_CALL WebDAVResponseParser::characters( const OUString& aChars )
+ {
+ // collect whitespace over evtl. several calls in mpContext
+ SAL_WARN_IF(!mpContext, "ucb.ucp.webdav", "Parser characters without content (!)");
+ const sal_Int32 nLen(aChars.getLength());
+
+ if(mpContext && nLen)
+ {
+ // remove leading/trailing blanks and CRLF
+ const OUString aTrimmedChars(aChars.trim());
+
+ if(aTrimmedChars.getLength())
+ {
+ OUString aNew(mpContext->getWhiteSpace());
+
+ if(aNew.getLength())
+ {
+ // add one char when appending (see html1.1 spec)
+ aNew += " ";
+ }
+
+ aNew += aTrimmedChars;
+ mpContext->setWhiteSpace(aNew);
+ }
+ }
+ }
+
+ void SAL_CALL WebDAVResponseParser::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
+ {
+ }
+
+ void SAL_CALL WebDAVResponseParser::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
+ {
+ }
+
+ void SAL_CALL WebDAVResponseParser::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& /*xLocator*/ )
+ {
+ }
+} // end of anonymous namespace
+
+
+// wrapper for various calls to the parser
+
+namespace
+{
+ template<typename T>
+ void parseWebDAVResponse(
+ const uno::Reference< io::XInputStream >& xInputStream,
+ std::vector< T >& rResult,
+ WebDAVResponseParserMode eWebDAVResponseParserMode,
+ std::vector<T> const & (WebDAVResponseParser::* fn)() const)
+ {
+ if(xInputStream.is())
+ {
+ try
+ {
+ // prepare ParserInputSrouce
+ xml::sax::InputSource myInputSource;
+ myInputSource.aInputStream = xInputStream;
+
+ // get parser
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(
+ comphelper::getProcessComponentContext() );
+
+ // create parser; connect parser and filter
+ WebDAVResponseParser* pWebDAVResponseParser = new WebDAVResponseParser(eWebDAVResponseParserMode);
+ uno::Reference< xml::sax::XDocumentHandler > xWebDAVHdl(pWebDAVResponseParser);
+ xParser->setDocumentHandler(xWebDAVHdl);
+
+ // finally, parse the stream
+ xParser->parseStream(myInputSource);
+
+ // get result
+ rResult = (pWebDAVResponseParser->*fn)();
+ }
+ catch(uno::Exception&)
+ {
+ SAL_WARN("ucb.ucp.webdav", "WebDAV Parse error (!)");
+ }
+ }
+ }
+} // end of anonymous namespace
+
+
+// helper to parse a XML WebDAV response
+
+namespace http_dav_ucp
+{
+ std::vector< ucb::Lock > parseWebDAVLockResponse(const uno::Reference< io::XInputStream >& xInputStream)
+ {
+ std::vector< ucb::Lock > aResult;
+ parseWebDAVResponse< ucb::Lock >(xInputStream, aResult, WebDAVResponseParserMode_Lock, &WebDAVResponseParser::getResult_Lock);
+ return aResult;
+ }
+
+ std::vector< DAVResource > parseWebDAVPropFindResponse(const uno::Reference< io::XInputStream >& xInputStream)
+ {
+ std::vector< DAVResource > aResult;
+ parseWebDAVResponse< DAVResource >(xInputStream, aResult, WebDAVResponseParserMode_PropFind, &WebDAVResponseParser::getResult_PropFind);
+ return aResult;
+ }
+
+ std::vector< DAVResourceInfo > parseWebDAVPropNameResponse(const uno::Reference< io::XInputStream >& xInputStream)
+ {
+ std::vector< DAVResourceInfo > aResult;
+ parseWebDAVResponse< DAVResourceInfo >(xInputStream, aResult, WebDAVResponseParserMode_PropName, &WebDAVResponseParser::getResult_PropName);
+ return aResult;
+ }
+} // namespace http_dav_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavresponseparser.hxx b/ucb/source/ucp/webdav/webdavresponseparser.hxx
new file mode 100644
index 000000000..546d44dfb
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavresponseparser.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVRESPONSEPARSER_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVRESPONSEPARSER_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include "DAVResource.hxx"
+#include <vector>
+
+
+namespace http_dav_ucp
+{
+ std::vector< css::ucb::Lock > parseWebDAVLockResponse(const css::uno::Reference< css::io::XInputStream >& xInputStream);
+ std::vector< DAVResource > parseWebDAVPropFindResponse(const css::uno::Reference< css::io::XInputStream >& xInputStream);
+ std::vector< DAVResourceInfo > parseWebDAVPropNameResponse(const css::uno::Reference< css::io::XInputStream >& xInputStream);
+} // namespace http_dav_ucp
+
+
+#endif // INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVRESPONSEPARSER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavresultset.cxx b/ucb/source/ucp/webdav/webdavresultset.cxx
new file mode 100644
index 000000000..e67dd1558
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavresultset.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - This implementation is not a dynamic result set!!! It only implements
+ the necessary interfaces, but never recognizes/notifies changes!!!
+
+ *************************************************************************/
+#include "webdavresultset.hxx"
+
+using namespace com::sun::star;
+using namespace http_dav_ucp;
+
+
+// DynamicResultSet Implementation.
+
+
+DynamicResultSet::DynamicResultSet(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const ucb::OpenCommandArgument2& rCommand,
+ const uno::Reference< ucb::XCommandEnvironment >& rxEnv )
+: ResultSetImplHelper( rxContext, rCommand ),
+ m_xContent( rxContent ),
+ m_xEnv( rxEnv )
+{
+}
+
+
+// Non-interface methods.
+
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet( m_xContext,
+ m_aCommand.Properties,
+ new DataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ),
+ m_xEnv );
+}
+
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet( m_xContext,
+ m_aCommand.Properties,
+ new DataSupplier( m_xContext,
+ m_xContent,
+ m_aCommand.Mode ),
+ m_xEnv );
+ m_xResultSet2 = m_xResultSet1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavresultset.hxx b/ucb/source/ucp/webdav/webdavresultset.hxx
new file mode 100644
index 000000000..cb1b7c0f5
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavresultset.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVRESULTSET_HXX
+#define INCLUDED_UCB_SOURCE_UCP_WEBDAV_WEBDAVRESULTSET_HXX
+
+#include <rtl/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#include "webdavcontent.hxx"
+#include "webdavdatasupplier.hxx"
+
+namespace http_dav_ucp {
+
+class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+{
+ rtl::Reference< Content > m_xContent;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv;
+
+private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+public:
+ DynamicResultSet( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const css::ucb::OpenCommandArgument2& rCommand,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& rxEnv );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/webdav/webdavservices.cxx b/ucb/source/ucp/webdav/webdavservices.cxx
new file mode 100644
index 000000000..419c9740d
--- /dev/null
+++ b/ucb/source/ucp/webdav/webdavservices.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include "webdavprovider.hxx"
+
+using namespace com::sun::star;
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * ucpdav1_component_getFactory(
+ const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
+{
+ void * pRet = nullptr;
+
+ uno::Reference< lang::XMultiServiceFactory > xSMgr(
+ static_cast< lang::XMultiServiceFactory * >( pServiceManager ) );
+ uno::Reference< lang::XSingleServiceFactory > xFactory;
+
+
+ // WebDAV Content Provider.
+
+
+ if ( ::http_dav_ucp::ContentProvider::getImplementationName_Static().
+ equalsAscii( pImplName ) )
+ {
+ xFactory = ::http_dav_ucp::ContentProvider::createServiceFactory( xSMgr );
+ }
+
+
+ if ( xFactory.is() )
+ {
+ xFactory->acquire();
+ pRet = xFactory.get();
+ }
+
+ return pRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */