diff options
Diffstat (limited to 'ucb/source/ucp/webdav')
73 files changed, 16536 insertions, 0 deletions
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 & + < + > 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<z + // - UCBDeadPropertyValue::toXML result: + // <ucbprop><type>string</type><value>x<z</value></ucbprop> + // PROPFIND: + // - parser replaces < 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: */ |