diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucbhelper/source/client | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | ucbhelper/source/client/activedatasink.cxx | 45 | ||||
-rw-r--r-- | ucbhelper/source/client/activedatastreamer.cxx | 46 | ||||
-rw-r--r-- | ucbhelper/source/client/activedatastreamer.hxx | 48 | ||||
-rw-r--r-- | ucbhelper/source/client/commandenvironment.cxx | 84 | ||||
-rw-r--r-- | ucbhelper/source/client/content.cxx | 1359 | ||||
-rw-r--r-- | ucbhelper/source/client/interceptedinteraction.cxx | 138 | ||||
-rw-r--r-- | ucbhelper/source/client/proxydecider.cxx | 960 |
7 files changed, 2680 insertions, 0 deletions
diff --git a/ucbhelper/source/client/activedatasink.cxx b/ucbhelper/source/client/activedatasink.cxx new file mode 100644 index 000000000..fdbcbdccb --- /dev/null +++ b/ucbhelper/source/client/activedatasink.cxx @@ -0,0 +1,45 @@ +/* -*- 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 + ************************************************************************** + + *************************************************************************/ +#include <ucbhelper/activedatasink.hxx> + +using namespace com::sun::star; + +namespace ucbhelper +{ +// ActiveDataSink Implementation. +// XActiveDataSink methods. + +// virtual +void SAL_CALL ActiveDataSink::setInputStream(const uno::Reference<io::XInputStream>& aStream) +{ + m_xStream = aStream; +} + +// virtual +uno::Reference<io::XInputStream> SAL_CALL ActiveDataSink::getInputStream() { return m_xStream; } + +} // namespace ucbhelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/activedatastreamer.cxx b/ucbhelper/source/client/activedatastreamer.cxx new file mode 100644 index 000000000..8438cae49 --- /dev/null +++ b/ucbhelper/source/client/activedatastreamer.cxx @@ -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 . + */ + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ +#include "activedatastreamer.hxx" + +using namespace com::sun::star; + +namespace ucbhelper +{ +// ActiveDataStreamer Implementation. + +// XActiveDataStreamer methods. + +// virtual +void SAL_CALL ActiveDataStreamer::setStream(const uno::Reference<io::XStream>& xStream) +{ + m_xStream = xStream; +} + +// virtual +uno::Reference<io::XStream> SAL_CALL ActiveDataStreamer::getStream() { return m_xStream; } + +} // namespace ucbhelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/activedatastreamer.hxx b/ucbhelper/source/client/activedatastreamer.hxx new file mode 100644 index 000000000..84ee10ac9 --- /dev/null +++ b/ucbhelper/source/client/activedatastreamer.hxx @@ -0,0 +1,48 @@ +/* -*- 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 UCBHELPER_SOURCE_CLIENT_ACTIVEDATASTREAMER_HXX +#define UCBHELPER_SOURCE_CLIENT_ACTIVEDATASTREAMER_HXX + +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <cppuhelper/implbase.hxx> + +namespace ucbhelper +{ +/** + * This class implements the interface css::io::XActiveDataStreamer. + * Instances of this class can be passed with the parameters of an + * "open" command. + */ + +class ActiveDataStreamer final : public cppu::WeakImplHelper<css::io::XActiveDataStreamer> +{ + css::uno::Reference<css::io::XStream> m_xStream; + +public: + // XActiveDataStreamer methods. + virtual void SAL_CALL setStream(const css::uno::Reference<css::io::XStream>& xStream) override; + virtual css::uno::Reference<css::io::XStream> SAL_CALL getStream() override; +}; + +} /* namespace ucbhelper */ + +#endif /* ! UCBHELPER_SOURCE_CLIENT_ACTIVEDATASTREAMER_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/commandenvironment.cxx b/ucbhelper/source/client/commandenvironment.cxx new file mode 100644 index 000000000..13bc254e0 --- /dev/null +++ b/ucbhelper/source/client/commandenvironment.cxx @@ -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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ + +#include <ucbhelper/commandenvironment.hxx> +#include <com/sun/star/uno/Reference.hxx> + +using namespace com::sun::star::lang; +using namespace com::sun::star::task; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; + +namespace com::sun::star::task { class XInteractionHandler; } +namespace com::sun::star::ucb { class XProgressHandler; } + +namespace ucbhelper +{ +// struct CommandEnvironment_Impl. + +struct CommandEnvironment_Impl +{ + Reference< XInteractionHandler > m_xInteractionHandler; + Reference< XProgressHandler > m_xProgressHandler; + + CommandEnvironment_Impl( const Reference< XInteractionHandler >& rxInteractionHandler, + const Reference< XProgressHandler >& rxProgressHandler ) + : m_xInteractionHandler( rxInteractionHandler ) + , m_xProgressHandler( rxProgressHandler ) {} +}; + +// CommandEnvironment Implementation. + +CommandEnvironment::CommandEnvironment( + const Reference< XInteractionHandler >& rxInteractionHandler, + const Reference< XProgressHandler >& rxProgressHandler ) + : m_pImpl( new CommandEnvironment_Impl( rxInteractionHandler, + rxProgressHandler ) ) +{ +} + +// virtual +CommandEnvironment::~CommandEnvironment() +{ +} + +// XCommandEnvironment methods. + +// virtual +Reference< XInteractionHandler > SAL_CALL CommandEnvironment::getInteractionHandler() +{ + return m_pImpl->m_xInteractionHandler; +} + +// virtual +Reference< XProgressHandler > SAL_CALL CommandEnvironment::getProgressHandler() +{ + return m_pImpl->m_xProgressHandler; +} + +} /* namespace ucbhelper */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/content.cxx b/ucbhelper/source/client/content.cxx new file mode 100644 index 000000000..4eeeab200 --- /dev/null +++ b/ucbhelper/source/client/content.cxx @@ -0,0 +1,1359 @@ +/* -*- 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 <cassert> + +#include <o3tl/unreachable.hxx> +#include <osl/diagnose.h> +#include <mutex> +#include <sal/log.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/ucb/CheckinArgument.hpp> +#include <com/sun/star/ucb/ContentCreationError.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/ucb/Command.hpp> +#include <com/sun/star/ucb/ContentAction.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/InsertCommandArgument.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> +#include <com/sun/star/ucb/XContentEventListener.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp> +#include <com/sun/star/ucb/UniversalContentBroker.hpp> +#include <com/sun/star/ucb/XUniversalContentBroker.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <ucbhelper/content.hxx> +#include <ucbhelper/activedatasink.hxx> +#include "activedatastreamer.hxx" +#include <ucbhelper/cancelcommandexecution.hxx> + +namespace com::sun::star::ucb { class XCommandEnvironment; } +namespace com::sun::star::ucb { class XContentProvider; } +namespace com::sun::star::sdbc { class XResultSet; } + +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::io; +using namespace com::sun::star::lang; +using namespace com::sun::star::sdbc; +using namespace com::sun::star::task; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; + +namespace ucbhelper +{ + +namespace { + +class EmptyInputStream : public ::cppu::WeakImplHelper< XInputStream > +{ +public: + virtual sal_Int32 SAL_CALL readBytes( + Sequence< sal_Int8 > & data, sal_Int32 nBytesToRead ) override; + virtual sal_Int32 SAL_CALL readSomeBytes( + Sequence< sal_Int8 > & data, 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; +}; + +} + +sal_Int32 EmptyInputStream::readBytes( + Sequence< sal_Int8 > & data, sal_Int32 ) +{ + data.realloc( 0 ); + return 0; +} + +sal_Int32 EmptyInputStream::readSomeBytes( + Sequence< sal_Int8 > & data, sal_Int32 ) +{ + data.realloc( 0 ); + return 0; +} + +void EmptyInputStream::skipBytes( sal_Int32 ) +{ +} + +sal_Int32 EmptyInputStream::available() +{ + return 0; +} + +void EmptyInputStream::closeInput() +{ +} + + + +namespace { + +class ContentEventListener_Impl : public cppu::OWeakObject, + public XContentEventListener +{ + Content_Impl& m_rContent; + +public: + explicit ContentEventListener_Impl( Content_Impl& rContent ) + : m_rContent( rContent ) {} + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() + noexcept override; + virtual void SAL_CALL release() + noexcept override; + + // XContentEventListener + virtual void SAL_CALL contentEvent( const ContentEvent& evt ) override; + + // XEventListener ( base of XContentEventListener ) + virtual void SAL_CALL disposing( const EventObject& Source ) override; +}; + +} + + + +class Content_Impl : public salhelper::SimpleReferenceObject +{ +friend ContentEventListener_Impl; + + mutable OUString m_aURL; + Reference< XComponentContext > m_xCtx; + Reference< XContent > m_xContent; + Reference< XCommandProcessor > m_xCommandProcessor; + Reference< XCommandEnvironment > m_xEnv; + Reference< XContentEventListener > m_xContentEventListener; + mutable std::mutex m_aMutex; + +private: + void reinit( const Reference< XContent >& xContent ); + void disposing(const EventObject& Source); + Reference< XContent > getContent_NoLock(); + const OUString& getURL_NoLock() const; + +public: + Content_Impl() {}; + Content_Impl( const Reference< XComponentContext >& rCtx, + const Reference< XContent >& rContent, + const Reference< XCommandEnvironment >& rEnv ); + + virtual ~Content_Impl() override; + + const OUString& getURL() const; + Reference< XContent > getContent(); + Reference< XCommandProcessor > getCommandProcessor(); + Reference< XComponentContext > const & getComponentContext() const + { assert(m_xCtx.is()); return m_xCtx; } + + Any executeCommand( const Command& rCommand ); + + inline const Reference< XCommandEnvironment >& getEnvironment() const; + inline void setEnvironment( + const Reference< XCommandEnvironment >& xNewEnv ); + + void inserted(); +}; + + +// Helpers. + +/// @throws ContentCreationException +/// @throws RuntimeException +static void ensureContentProviderForURL( const Reference< XUniversalContentBroker >& rBroker, + const OUString & rURL ) +{ + Reference< XContentProvider > xProv + = rBroker->queryContentProvider( rURL ); + if ( !xProv.is() ) + { + throw ContentCreationException( + "No Content Provider available for URL: " + rURL, + Reference< XInterface >(), + ContentCreationError_NO_CONTENT_PROVIDER ); + } +} + +/// @throws ContentCreationException +/// @throws RuntimeException +static Reference< XContentIdentifier > getContentIdentifierThrow( + const Reference< XUniversalContentBroker > & rBroker, + const OUString & rURL) +{ + Reference< XContentIdentifier > xId + = rBroker->createContentIdentifier( rURL ); + + if (!xId.is()) + { + ensureContentProviderForURL( rBroker, rURL ); + + throw ContentCreationException( + "Unable to create Content Identifier!", + Reference< XInterface >(), + ContentCreationError_IDENTIFIER_CREATION_FAILED ); + } + + return xId; +} + +/// @throws RuntimeException +static Reference< XContentIdentifier > getContentIdentifierNoThrow( + const Reference< XUniversalContentBroker > & rBroker, + const OUString & rURL) +{ + return rBroker->createContentIdentifier(rURL); +} + +/// @throws ContentCreationException +/// @throws RuntimeException +static Reference< XContent > getContentThrow( + const Reference< XUniversalContentBroker > & rBroker, + const Reference< XContentIdentifier > & xId) +{ + Reference< XContent > xContent; + OUString msg; + try + { + xContent = rBroker->queryContent( xId ); + } + catch ( IllegalIdentifierException const & e ) + { + msg = e.Message; + // handled below. + } + + if ( !xContent.is() ) + { + ensureContentProviderForURL( rBroker, xId->getContentIdentifier() ); + + throw ContentCreationException( + "Unable to create Content for <" + xId->getContentIdentifier() + ">: " + msg, + Reference< XInterface >(), + ContentCreationError_CONTENT_CREATION_FAILED ); + } + + return xContent; +} + +/// @throws RuntimeException +static Reference< XContent > getContentNoThrow( + const Reference< XUniversalContentBroker > & rBroker, + const Reference< XContentIdentifier > & xId) +{ + Reference< XContent > xContent; + try + { + xContent = rBroker->queryContent( xId ); + } + catch ( IllegalIdentifierException const & e ) + { + SAL_WARN("ucbhelper", "getContentNoThrow: " << e); + } + + return xContent; +} + + +// Content Implementation. + + +Content::Content() +: m_xImpl( new Content_Impl ) +{ +} + + +Content::Content( const OUString& rURL, + const Reference< XCommandEnvironment >& rEnv, + const Reference< XComponentContext >& rCtx ) +{ + Reference< XUniversalContentBroker > pBroker( + UniversalContentBroker::create( rCtx ) ); + + Reference< XContentIdentifier > xId + = getContentIdentifierThrow(pBroker, rURL); + + Reference< XContent > xContent = getContentThrow(pBroker, xId); + + m_xImpl = new Content_Impl( rCtx, xContent, rEnv ); +} + + +Content::Content( const Reference< XContent >& rContent, + const Reference< XCommandEnvironment >& rEnv, + const Reference< XComponentContext >& rCtx ) +{ + m_xImpl = new Content_Impl( rCtx, rContent, rEnv ); +} + + +Content::Content( const Content& rOther ) +{ + m_xImpl = rOther.m_xImpl; +} + +Content::Content( Content&& rOther ) noexcept +{ + m_xImpl = std::move(rOther.m_xImpl); +} + +// static +bool Content::create( const OUString& rURL, + const Reference< XCommandEnvironment >& rEnv, + const Reference< XComponentContext >& rCtx, + Content& rContent ) +{ + Reference< XUniversalContentBroker > pBroker( + UniversalContentBroker::create( rCtx ) ); + + Reference< XContentIdentifier > xId + = getContentIdentifierNoThrow(pBroker, rURL); + if ( !xId.is() ) + return false; + + Reference< XContent > xContent = getContentNoThrow(pBroker, xId); + if ( !xContent.is() ) + return false; + + rContent.m_xImpl + = new Content_Impl( rCtx, xContent, rEnv ); + + return true; +} + + +Content::~Content() +{ +} + + +Content& Content::operator=( const Content& rOther ) +{ + m_xImpl = rOther.m_xImpl; + return *this; +} + +Content& Content::operator=( Content&& rOther ) noexcept +{ + m_xImpl = std::move(rOther.m_xImpl); + return *this; +} + +Reference< XContent > Content::get() const +{ + return m_xImpl->getContent(); +} + + +const OUString& Content::getURL() const +{ + return m_xImpl->getURL(); +} + + +const Reference< XCommandEnvironment >& Content::getCommandEnvironment() const +{ + return m_xImpl->getEnvironment(); +} + + +void Content::setCommandEnvironment( + const Reference< XCommandEnvironment >& xNewEnv ) +{ + m_xImpl->setEnvironment( xNewEnv ); +} + + +Reference< XCommandInfo > Content::getCommands() +{ + Command aCommand; + aCommand.Name = "getCommandInfo"; + aCommand.Handle = -1; // n/a + aCommand.Argument = Any(); + + Any aResult = m_xImpl->executeCommand( aCommand ); + + Reference< XCommandInfo > xInfo; + aResult >>= xInfo; + return xInfo; +} + + +Reference< XPropertySetInfo > Content::getProperties() +{ + static constexpr OUStringLiteral sgetPropertySetInfo = u"getPropertySetInfo"; + Command aCommand; + aCommand.Name = sgetPropertySetInfo; + aCommand.Handle = -1; // n/a + aCommand.Argument = Any(); + + Any aResult = m_xImpl->executeCommand( aCommand ); + + Reference< XPropertySetInfo > xInfo; + aResult >>= xInfo; + return xInfo; +} + + +Any Content::getPropertyValue( const OUString& rPropertyName ) +{ + Sequence<OUString> aNames { rPropertyName }; + + Sequence< Any > aRet = getPropertyValues( aNames ); + return aRet.getConstArray()[ 0 ]; +} + + +Any Content::setPropertyValue( const OUString& rName, + const Any& rValue ) +{ + Sequence<OUString> aNames { rName }; + + Sequence< Any > aValues( 1 ); + aValues.getArray()[ 0 ] = rValue; + + Sequence< Any > aErrors = setPropertyValues( aNames, aValues ); + return aErrors.getConstArray()[ 0 ]; +} + + +Sequence< Any > Content::getPropertyValues( + const Sequence< OUString >& rPropertyNames ) +{ + Reference< XRow > xRow = getPropertyValuesInterface( rPropertyNames ); + + sal_Int32 nCount = rPropertyNames.getLength(); + Sequence< Any > aValues( nCount ); + + if ( xRow.is() ) + { + Any* pValues = aValues.getArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + pValues[ n ] = xRow->getObject( n + 1, Reference< XNameAccess >() ); + } + + return aValues; +} + + +Reference< XRow > Content::getPropertyValuesInterface( + const Sequence< OUString >& rPropertyNames ) +{ + sal_Int32 nCount = rPropertyNames.getLength(); + Sequence< Property > aProps( nCount ); + Property* pProps = aProps.getArray(); + + const OUString* pNames = rPropertyNames.getConstArray(); + + for ( sal_Int32 n = 0; n< nCount; ++n ) + { + Property& rProp = pProps[ n ]; + + rProp.Name = pNames[ n ]; + rProp.Handle = -1; // n/a +// rProp.Type = +// rProp.Attributes = ; + } + + static constexpr OUStringLiteral sgetPropertyValues = u"getPropertyValues"; + Command aCommand; + aCommand.Name = sgetPropertyValues; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aProps; + + Any aResult = m_xImpl->executeCommand( aCommand ); + + Reference< XRow > xRow; + aResult >>= xRow; + return xRow; +} + + +Sequence< Any > Content::setPropertyValues( + const Sequence< OUString >& rPropertyNames, + const Sequence< Any >& rValues ) +{ + if ( rPropertyNames.getLength() != rValues.getLength() ) + { + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + "Length of property names sequence and value " + "sequence are unequal!", + get(), + -1 ) ), + m_xImpl->getEnvironment() ); + // Unreachable + } + + sal_Int32 nCount = rValues.getLength(); + Sequence< PropertyValue > aProps( nCount ); + PropertyValue* pProps = aProps.getArray(); + + const OUString* pNames = rPropertyNames.getConstArray(); + const Any* pValues = rValues.getConstArray(); + + for ( sal_Int32 n = 0; n< nCount; ++n ) + { + PropertyValue& rProp = pProps[ n ]; + + rProp.Name = pNames[ n ]; + rProp.Handle = -1; // n/a + rProp.Value = pValues[ n ]; +// rProp.State = ; + } + + Command aCommand; + aCommand.Name = "setPropertyValues"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aProps; + + Any aResult = m_xImpl->executeCommand( aCommand ); + + Sequence< Any > aErrors; + aResult >>= aErrors; + return aErrors; +} + + +Any Content::executeCommand( const OUString& rCommandName, + const Any& rCommandArgument ) +{ + Command aCommand; + aCommand.Name = rCommandName; + aCommand.Handle = -1; // n/a + aCommand.Argument = rCommandArgument; + + return m_xImpl->executeCommand( aCommand ); +} + + +Any Content::createCursorAny( const Sequence< OUString >& rPropertyNames, + ResultSetInclude eMode ) +{ + sal_Int32 nCount = rPropertyNames.getLength(); + Sequence< Property > aProps( nCount ); + Property* pProps = aProps.getArray(); + const OUString* pNames = rPropertyNames.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + Property& rProp = pProps[ n ]; + rProp.Name = pNames[ n ]; + rProp.Handle = -1; // n/a + } + + OpenCommandArgument2 aArg; + aArg.Mode = ( eMode == INCLUDE_FOLDERS_ONLY ) + ? OpenMode::FOLDERS + : ( eMode == INCLUDE_DOCUMENTS_ONLY ) + ? OpenMode::DOCUMENTS : OpenMode::ALL; + aArg.Priority = 0; // unused + aArg.Sink.clear(); // unused + aArg.Properties = aProps; + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + return m_xImpl->executeCommand( aCommand ); +} + + +Reference< XResultSet > Content::createCursor( + const Sequence< OUString >& rPropertyNames, + ResultSetInclude eMode ) +{ + Any aCursorAny = createCursorAny( rPropertyNames, eMode ); + + Reference< XDynamicResultSet > xDynSet; + Reference< XResultSet > aResult; + + aCursorAny >>= xDynSet; + if ( xDynSet.is() ) + aResult = xDynSet->getStaticResultSet(); + + OSL_ENSURE( aResult.is(), "Content::createCursor - no cursor!" ); + + if ( !aResult.is() ) + { + // Former, the open command directly returned a XResultSet. + aCursorAny >>= aResult; + + OSL_ENSURE( !aResult.is(), + "Content::createCursor - open-Command must " + "return a Reference< XDynnamicResultSet >!" ); + } + + return aResult; +} + + +Reference< XDynamicResultSet > Content::createDynamicCursor( + const Sequence< OUString >& rPropertyNames, + ResultSetInclude eMode ) +{ + Reference< XDynamicResultSet > aResult; + createCursorAny( rPropertyNames, eMode ) >>= aResult; + + OSL_ENSURE( aResult.is(), "Content::createDynamicCursor - no cursor!" ); + + return aResult; +} + + +Reference< XResultSet > Content::createSortedCursor( + const Sequence< OUString >& rPropertyNames, + const Sequence< NumberedSortingInfo >& rSortInfo, + const Reference< XAnyCompareFactory >& rAnyCompareFactory, + ResultSetInclude eMode ) +{ + Reference< XResultSet > aResult; + Reference< XDynamicResultSet > aDynSet; + + Any aCursorAny = createCursorAny( rPropertyNames, eMode ); + + aCursorAny >>= aDynSet; + + if( aDynSet.is() ) + { + Reference< XDynamicResultSet > aDynResult; + + if( m_xImpl->getComponentContext().is() ) + { + Reference< XSortedDynamicResultSetFactory > aSortFactory = + SortedDynamicResultSetFactory::create( m_xImpl->getComponentContext()); + + aDynResult = aSortFactory->createSortedDynamicResultSet( aDynSet, + rSortInfo, + rAnyCompareFactory ); + } + + OSL_ENSURE( aDynResult.is(), "Content::createSortedCursor - no sorted cursor!" ); + + if( aDynResult.is() ) + aResult = aDynResult->getStaticResultSet(); + else + aResult = aDynSet->getStaticResultSet(); + } + + OSL_ENSURE( aResult.is(), "Content::createSortedCursor - no cursor!" ); + + if ( !aResult.is() ) + { + // Former, the open command directly returned a XResultSet. + aCursorAny >>= aResult; + + OSL_ENSURE( !aResult.is(), + "Content::createCursor - open-Command must " + "return a Reference< XDynnamicResultSet >!" ); + } + + return aResult; +} + + +Reference< XInputStream > Content::openStream() +{ + if ( !isDocument() ) + return Reference< XInputStream >(); + + Reference< XActiveDataSink > xSink = new ActiveDataSink; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return xSink->getInputStream(); +} + + +Reference< XInputStream > Content::openStreamNoLock() +{ + if ( !isDocument() ) + return Reference< XInputStream >(); + + Reference< XActiveDataSink > xSink = new ActiveDataSink; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT_SHARE_DENY_NONE; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return xSink->getInputStream(); +} + + +Reference< XStream > Content::openWriteableStream() +{ + if ( !isDocument() ) + return Reference< XStream >(); + + Reference< XActiveDataStreamer > xStreamer = new ActiveDataStreamer; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xStreamer; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return xStreamer->getStream(); +} + + +Reference< XStream > Content::openWriteableStreamNoLock() +{ + if ( !isDocument() ) + return Reference< XStream >(); + + Reference< XActiveDataStreamer > xStreamer = new ActiveDataStreamer; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT_SHARE_DENY_NONE; + aArg.Priority = 0; // unused + aArg.Sink = xStreamer; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return xStreamer->getStream(); +} + + +bool Content::openStream( const Reference< XActiveDataSink >& rSink ) +{ + if ( !isDocument() ) + return false; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = rSink; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return true; +} + + +bool Content::openStream( const Reference< XOutputStream >& rStream ) +{ + if ( !isDocument() ) + return false; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = rStream; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Command aCommand; + aCommand.Name = "open"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + return true; +} + + +void Content::writeStream( const Reference< XInputStream >& rStream, + bool bReplaceExisting ) +{ + InsertCommandArgument aArg; + aArg.Data = rStream.is() ? rStream : new EmptyInputStream; + aArg.ReplaceExisting = bReplaceExisting; + + Command aCommand; + aCommand.Name = "insert"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aArg; + + m_xImpl->executeCommand( aCommand ); + + m_xImpl->inserted(); +} + + +Sequence< ContentInfo > Content::queryCreatableContentsInfo() +{ + // First, try it using "CreatableContentsInfo" property -> the "new" way. + Sequence< ContentInfo > aInfo; + if ( getPropertyValue( + "CreatableContentsInfo" ) + >>= aInfo ) + return aInfo; + + // Second, try it using XContentCreator interface -> the "old" way (not + // providing the chance to supply an XCommandEnvironment. + Reference< XContentCreator > xCreator( m_xImpl->getContent(), UNO_QUERY ); + if ( xCreator.is() ) + aInfo = xCreator->queryCreatableContentsInfo(); + + return aInfo; +} + + +bool Content::insertNewContent( const OUString& rContentType, + const Sequence< OUString >& + rPropertyNames, + const Sequence< Any >& rPropertyValues, + Content& rNewContent ) +{ + return insertNewContent( rContentType, + rPropertyNames, + rPropertyValues, + new EmptyInputStream, + rNewContent ); +} + + +bool Content::insertNewContent( const OUString& rContentType, + const Sequence< OUString >& + rPropertyNames, + const Sequence< Any >& rPropertyValues, + const Reference< XInputStream >& rData, + Content& rNewContent ) +{ + if ( rContentType.isEmpty() ) + return false; + + // First, try it using "createNewContent" command -> the "new" way. + ContentInfo aInfo; + aInfo.Type = rContentType; + aInfo.Attributes = 0; + + Command aCommand; + aCommand.Name = "createNewContent"; + aCommand.Handle = -1; // n/a + aCommand.Argument <<= aInfo; + + Reference< XContent > xNew; + try + { + m_xImpl->executeCommand( aCommand ) >>= xNew; + } + catch ( RuntimeException const & ) + { + throw; + } + catch ( Exception const & ) + { + } + + if ( !xNew.is() ) + { + // Second, try it using XContentCreator interface -> the "old" + // way (not providing the chance to supply an XCommandEnvironment. + Reference< XContentCreator > xCreator( m_xImpl->getContent(), UNO_QUERY ); + + if ( !xCreator.is() ) + return false; + + xNew = xCreator->createNewContent( aInfo ); + + if ( !xNew.is() ) + return false; + } + + Content aNewContent( + xNew, m_xImpl->getEnvironment(), m_xImpl->getComponentContext() ); + aNewContent.setPropertyValues( rPropertyNames, rPropertyValues ); + aNewContent.executeCommand( "insert", + Any( + InsertCommandArgument( + rData.is() ? rData : new EmptyInputStream, + false /* ReplaceExisting */ ) ) ); + aNewContent.m_xImpl->inserted(); + + rNewContent = aNewContent; + return true; +} + + +void Content::transferContent( const Content& rSourceContent, + InsertOperation eOperation, + const OUString & rTitle, + const sal_Int32 nNameClashAction, + const OUString & rMimeType, + bool bMajorVersion, + const OUString & rVersionComment, + OUString* pResultURL, + const OUString & rDocumentId ) const +{ + Reference< XUniversalContentBroker > pBroker( + UniversalContentBroker::create( m_xImpl->getComponentContext() ) ); + + // Execute command "globalTransfer" at UCB. + + TransferCommandOperation eTransOp = TransferCommandOperation(); + OUString sCommand( "globalTransfer" ); + bool bCheckIn = false; + switch ( eOperation ) + { + case InsertOperation::Copy: + eTransOp = TransferCommandOperation_COPY; + break; + + case InsertOperation::Move: + eTransOp = TransferCommandOperation_MOVE; + break; + + case InsertOperation::Checkin: + eTransOp = TransferCommandOperation_COPY; + sCommand = "checkin"; + bCheckIn = true; + break; + } + Command aCommand; + aCommand.Name = sCommand; + aCommand.Handle = -1; // n/a + + if ( !bCheckIn ) + { + GlobalTransferCommandArgument2 aTransferArg( + eTransOp, + rSourceContent.getURL(), // SourceURL + getURL(), // TargetFolderURL, + rTitle, + nNameClashAction, + rMimeType, + rDocumentId ); + aCommand.Argument <<= aTransferArg; + } + else + { + CheckinArgument aCheckinArg( bMajorVersion, rVersionComment, + rSourceContent.getURL(), getURL(), rTitle, rMimeType ); + aCommand.Argument <<= aCheckinArg; + } + + Any aRet = pBroker->execute( aCommand, 0, m_xImpl->getEnvironment() ); + if ( pResultURL != nullptr ) + aRet >>= *pResultURL; +} + + +bool Content::isFolder() +{ + bool bFolder = false; + if ( getPropertyValue("IsFolder") + >>= bFolder ) + return bFolder; + + ucbhelper::cancelCommandExecution( + Any( UnknownPropertyException( + "Unable to retrieve value of property 'IsFolder'!", + get() ) ), + m_xImpl->getEnvironment() ); + + O3TL_UNREACHABLE; +} + + +SAL_WNOUNREACHABLE_CODE_PUSH + +bool Content::isDocument() +{ + bool bDoc = false; + if ( getPropertyValue("IsDocument") + >>= bDoc ) + return bDoc; + + ucbhelper::cancelCommandExecution( + Any( UnknownPropertyException( + "Unable to retrieve value of property 'IsDocument'!", + get() ) ), + m_xImpl->getEnvironment() ); + + // Unreachable - cancelCommandExecution always throws an exception, + // But some compilers complain... + return false; +} + +SAL_WNOUNREACHABLE_CODE_POP + +void Content::lock() +{ + Command aCommand; + aCommand.Name = "lock"; + aCommand.Handle = -1; // n/a + + m_xImpl->executeCommand( aCommand ); + +} + +void Content::unlock() +{ + + Command aCommand; + aCommand.Name = "unlock"; + aCommand.Handle = -1; // n/a + + m_xImpl->executeCommand( aCommand ); + +} + + +// Content_Impl Implementation. + + +Content_Impl::Content_Impl( const Reference< XComponentContext >& rCtx, + const Reference< XContent >& rContent, + const Reference< XCommandEnvironment >& rEnv ) +: m_xCtx( rCtx ), + m_xContent( rContent ), + m_xEnv( rEnv ) +{ + assert(rCtx.is()); + if ( m_xContent.is() ) + { + m_xContentEventListener = new ContentEventListener_Impl( *this ); + m_xContent->addContentEventListener( m_xContentEventListener ); + +#if OSL_DEBUG_LEVEL > 0 + // Only done on demand in product version for performance reasons, + // but a nice debug helper. + getURL(); +#endif + } +} + + +void Content_Impl::reinit( const Reference< XContent >& xContent ) +{ + std::unique_lock aGuard( m_aMutex ); + + m_xCommandProcessor = nullptr; + + // #92581# - Don't reset m_aURL!!! + + if ( m_xContent.is() ) + { + try + { + m_xContent->removeContentEventListener( m_xContentEventListener ); + } + catch ( RuntimeException const & ) + { + } + } + + if ( xContent.is() ) + { + m_xContent = xContent; + m_xContent->addContentEventListener( m_xContentEventListener ); + +#if OSL_DEBUG_LEVEL > 0 + // Only done on demand in product version for performance reasons, + // but a nice debug helper. + getURL_NoLock(); +#endif + } + else + { + // We need m_xContent's URL in order to be able to create the + // content object again if demanded ( --> Content_Impl::getContent() ) + getURL_NoLock(); + + m_xContent = nullptr; + } +} + + +// virtual +Content_Impl::~Content_Impl() +{ + if ( m_xContent.is() ) + { + try + { + m_xContent->removeContentEventListener( m_xContentEventListener ); + } + catch ( RuntimeException const & ) + { + } + } +} + + +void Content_Impl::disposing( const EventObject& Source ) +{ + Reference<XContent> xContent; + + { + std::unique_lock aGuard( m_aMutex ); + if(Source.Source != m_xContent) + return; + + xContent = m_xContent; + + m_aURL.clear(); + m_xCommandProcessor = nullptr; + m_xContent = nullptr; + } + + if ( xContent.is() ) + { + try + { + xContent->removeContentEventListener( m_xContentEventListener ); + } + catch ( RuntimeException const & ) + { + } + } +} + + +const OUString& Content_Impl::getURL() const +{ + if ( m_aURL.isEmpty() && m_xContent.is() ) + { + std::unique_lock aGuard( m_aMutex ); + + return getURL_NoLock(); + } + + return m_aURL; +} + +const OUString& Content_Impl::getURL_NoLock() const +{ + if ( m_aURL.isEmpty() && m_xContent.is() ) + { + Reference< XContentIdentifier > xId = m_xContent->getIdentifier(); + if ( xId.is() ) + m_aURL = xId->getContentIdentifier(); + } + + return m_aURL; +} + +Reference< XContent > Content_Impl::getContent() +{ + if ( !m_xContent.is() && !m_aURL.isEmpty() ) + { + std::unique_lock aGuard( m_aMutex ); + return getContent_NoLock(); + } + return m_xContent; +} + +Reference< XContent > Content_Impl::getContent_NoLock() +{ + if ( !m_xContent.is() && !m_aURL.isEmpty() ) + { + Reference< XUniversalContentBroker > pBroker( + UniversalContentBroker::create( getComponentContext() ) ); + + OSL_ENSURE( pBroker->queryContentProviders().hasElements(), + "Content Broker not configured (no providers)!" ); + + Reference< XContentIdentifier > xId + = pBroker->createContentIdentifier( m_aURL ); + + OSL_ENSURE( xId.is(), "No Content Identifier!" ); + + if ( xId.is() ) + { + try + { + m_xContent = pBroker->queryContent( xId ); + } + catch ( IllegalIdentifierException const & ) + { + } + + if ( m_xContent.is() ) + m_xContent->addContentEventListener( + m_xContentEventListener ); + } + } + + return m_xContent; +} + + +Reference< XCommandProcessor > Content_Impl::getCommandProcessor() +{ + if ( !m_xCommandProcessor.is() ) + { + std::unique_lock aGuard( m_aMutex ); + + if ( !m_xCommandProcessor.is() ) + m_xCommandProcessor.set( getContent_NoLock(), UNO_QUERY ); + } + + return m_xCommandProcessor; +} + + +Any Content_Impl::executeCommand( const Command& rCommand ) +{ + Reference< XCommandProcessor > xProc = getCommandProcessor(); + if ( !xProc.is() ) + return Any(); + + // Execute command + return xProc->execute( rCommand, 0, m_xEnv ); +} + + +inline const Reference< XCommandEnvironment >& + Content_Impl::getEnvironment() const +{ + return m_xEnv; +} + + +inline void Content_Impl::setEnvironment( + const Reference< XCommandEnvironment >& xNewEnv ) +{ + std::unique_lock aGuard( m_aMutex ); + m_xEnv = xNewEnv; +} + + +void Content_Impl::inserted() +{ + // URL might have changed during 'insert' => recalculate in next getURL() + std::unique_lock aGuard( m_aMutex ); + m_aURL.clear(); +} + + +// ContentEventListener_Impl Implementation. + + +// XInterface methods. + +void SAL_CALL ContentEventListener_Impl::acquire() + noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ContentEventListener_Impl::release() + noexcept +{ + OWeakObject::release(); +} + +css::uno::Any SAL_CALL ContentEventListener_Impl::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = cppu::queryInterface( rType, + static_cast< XContentEventListener* >(this), + static_cast< XEventListener* >(this) + ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + +// XContentEventListener methods. + + +// virtual +void SAL_CALL ContentEventListener_Impl::contentEvent( const ContentEvent& evt ) +{ + if ( evt.Source != m_rContent.m_xContent ) + return; + + switch ( evt.Action ) + { + case ContentAction::DELETED: + m_rContent.reinit( Reference< XContent >() ); + break; + + case ContentAction::EXCHANGED: + m_rContent.reinit( evt.Content ); + break; + + default: + break; + } +} + + +// XEventListenr methods. + + +// virtual +void SAL_CALL ContentEventListener_Impl::disposing( const EventObject& Source ) +{ + m_rContent.disposing(Source); +} + +} /* namespace ucbhelper */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/interceptedinteraction.cxx b/ucbhelper/source/client/interceptedinteraction.cxx new file mode 100644 index 000000000..96b3fd32c --- /dev/null +++ b/ucbhelper/source/client/interceptedinteraction.cxx @@ -0,0 +1,138 @@ +/* -*- 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/interceptedinteraction.hxx> + +#include <osl/diagnose.h> + +namespace ucbhelper{ + +InterceptedInteraction::InterceptedInteraction() +{ +} + +void InterceptedInteraction::setInterceptedHandler(const css::uno::Reference< css::task::XInteractionHandler >& xInterceptedHandler) +{ + m_xInterceptedHandler = xInterceptedHandler; +} + +void InterceptedInteraction::setInterceptions(::std::vector< InterceptedRequest >&& lInterceptions) +{ + m_lInterceptions = std::move(lInterceptions); +} + +InterceptedInteraction::EInterceptionState InterceptedInteraction::intercepted( + const InterceptedRequest&, + const css::uno::Reference< css::task::XInteractionRequest >&) +{ + // default behaviour! see impl_interceptRequest() for further information ... + return E_NOT_INTERCEPTED; +} + +css::uno::Reference< css::task::XInteractionContinuation > InterceptedInteraction::extractContinuation(const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > >& lContinuations, + const css::uno::Type& aType ) +{ + const css::uno::Reference< css::task::XInteractionContinuation >* pContinuations = std::find_if(lContinuations.begin(), lContinuations.end(), + [&aType](const css::uno::Reference< css::task::XInteractionContinuation >& rContinuation) { + css::uno::Reference< css::uno::XInterface > xCheck(rContinuation, css::uno::UNO_QUERY); + return xCheck->queryInterface(aType).hasValue(); + }); + if (pContinuations != lContinuations.end()) + return *pContinuations; + + return css::uno::Reference< css::task::XInteractionContinuation >(); +} + +void SAL_CALL InterceptedInteraction::handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest) +{ + impl_handleDefault(xRequest); +} + +void InterceptedInteraction::impl_handleDefault(const css::uno::Reference< css::task::XInteractionRequest >& xRequest) +{ + EInterceptionState eState = impl_interceptRequest(xRequest); + + switch(eState) + { + case E_NOT_INTERCEPTED: + { + // Non of the intercepted requests match to the given one. + // => forward request to the internal wrapped handler - if there is one. + if (m_xInterceptedHandler.is()) + m_xInterceptedHandler->handle(xRequest); + } + break; + + case E_NO_CONTINUATION_FOUND: + { + // Runtime error! The defined continuation could not be located + // inside the set of available continuations of the incoming request. + // What's wrong - the interception list or the request? + OSL_FAIL("InterceptedInteraction::handle()\nCould intercept this interaction request - but can't locate the right continuation!"); + } + break; + + case E_INTERCEPTED: + break; + } +} + +InterceptedInteraction::EInterceptionState InterceptedInteraction::impl_interceptRequest(const css::uno::Reference< css::task::XInteractionRequest >& xRequest) +{ + css::uno::Any aRequest = xRequest->getRequest(); + const css::uno::Type& aRequestType = aRequest.getValueType(); + css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + + // check against the list of static requests + auto pIt = std::find_if(m_lInterceptions.begin(), m_lInterceptions.end(), + [&aRequestType](const InterceptedRequest& rInterception) { + // check the request + // don't change intercepted and request type here -> it will check the wrong direction! + return rInterception.Request.getValueType().isAssignableFrom(aRequestType); + }); + + if (pIt != m_lInterceptions.end()) // intercepted ... + { + const InterceptedRequest& rInterception = *pIt; + + // Call they might existing derived class, so they can handle that by its own. + // If it's not interested on that (maybe it's not overwritten and the default implementation + // returns E_NOT_INTERCEPTED as default) -> search required continuation + EInterceptionState eState = intercepted(rInterception, xRequest); + if (eState != E_NOT_INTERCEPTED) + return eState; + + css::uno::Reference< css::task::XInteractionContinuation > xContinuation = InterceptedInteraction::extractContinuation(lContinuations, rInterception.Continuation); + if (xContinuation.is()) + { + xContinuation->select(); + return E_INTERCEPTED; + } + + // Can be reached only, if the request does not support the given continuation! + // => RuntimeError!? + return E_NO_CONTINUATION_FOUND; + } + + return E_NOT_INTERCEPTED; +} + +} // namespace ucbhelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucbhelper/source/client/proxydecider.cxx b/ucbhelper/source/client/proxydecider.cxx new file mode 100644 index 000000000..10228b72a --- /dev/null +++ b/ucbhelper/source/client/proxydecider.cxx @@ -0,0 +1,960 @@ +/* -*- 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 <string_view> +#include <utility> +#include <vector> +#include <deque> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> +#include <osl/socket.hxx> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/XChangesListener.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> +#include <cppuhelper/implbase.hxx> +#include <ucbhelper/proxydecider.hxx> +#include <o3tl/string_view.hxx> + +#ifdef _WIN32 +#include <o3tl/char16_t2wchar_t.hxx> +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#include <Winhttp.h> +#endif + +using namespace com::sun::star; +using namespace ucbhelper; + +constexpr OUStringLiteral CONFIG_ROOT_KEY = u"org.openoffice.Inet/Settings"; +constexpr OUStringLiteral PROXY_TYPE_KEY = u"ooInetProxyType"; +constexpr OUStringLiteral NO_PROXY_LIST_KEY = u"ooInetNoProxy"; +constexpr OUStringLiteral HTTP_PROXY_NAME_KEY = u"ooInetHTTPProxyName"; +constexpr OUStringLiteral HTTP_PROXY_PORT_KEY = u"ooInetHTTPProxyPort"; +constexpr OUStringLiteral HTTPS_PROXY_NAME_KEY = u"ooInetHTTPSProxyName"; +constexpr OUStringLiteral HTTPS_PROXY_PORT_KEY = u"ooInetHTTPSProxyPort"; +constexpr OUStringLiteral FTP_PROXY_NAME_KEY = u"ooInetFTPProxyName"; +constexpr OUStringLiteral FTP_PROXY_PORT_KEY = u"ooInetFTPProxyPort"; + + +namespace ucbhelper +{ + + +namespace proxydecider_impl +{ + +namespace { + +// A simple case ignoring wildcard matcher. +class WildCard +{ +private: + OString m_aWildString; + +public: + explicit WildCard( std::u16string_view rWildCard ) + : m_aWildString( + OUStringToOString( + rWildCard, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() ) {} + + bool Matches( std::u16string_view rStr ) const; +}; + +} + +namespace { + +class HostnameCache +{ + typedef std::pair< OUString, OUString > HostListEntry; + + std::deque< HostListEntry > m_aHostList; + +public: + bool get( std::u16string_view rKey, OUString & rValue ) const + { + for (auto const& host : m_aHostList) + { + if ( host.first == rKey ) + { + rValue = host.second; + return true; + } + } + return false; + } + + void put( const OUString & rKey, const OUString & rValue ) + { + static constexpr sal_uInt32 nCapacity = 256; + + if ( m_aHostList.size() == nCapacity ) + m_aHostList.resize( nCapacity / 2 ); + + m_aHostList.push_front( HostListEntry( rKey, rValue ) ); + } +}; + +} + +class InternetProxyDecider_Impl : + public cppu::WeakImplHelper< util::XChangesListener > +{ + // see officecfg/registry/schema/org/openoffice/Inet.xcs for the definition of these values + enum class ProxyType { NoProxy, Automatic, Manual }; + mutable osl::Mutex m_aMutex; + InternetProxyServer m_aHttpProxy; + InternetProxyServer m_aHttpsProxy; + InternetProxyServer m_aFtpProxy; + const InternetProxyServer m_aEmptyProxy; + ProxyType m_nProxyType; + uno::Reference< util::XChangesNotifier > m_xNotifier; + typedef std::pair< WildCard, WildCard > NoProxyListEntry; + std::vector< NoProxyListEntry > m_aNoProxyList; + mutable HostnameCache m_aHostnames; + +private: + bool shouldUseProxy( std::u16string_view rHost, + sal_Int32 nPort, + bool bUseFullyQualified ) const; +public: + explicit InternetProxyDecider_Impl( + const uno::Reference< uno::XComponentContext >& rxContext ); + + void dispose(); + + InternetProxyServer getProxy(const OUString& rProtocol, + const OUString & rHost, + sal_Int32 nPort ) const; + + // XChangesListener + virtual void SAL_CALL changesOccurred( const util::ChangesEvent& Event ) override; + + // XEventListener ( base of XChangesLisetenr ) + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + +private: + void setNoProxyList( const OUString & rNoProxyList ); +}; + + +// WildCard Implementation. + + +bool WildCard::Matches( std::u16string_view rString ) const +{ + OString aString + = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase(); + const char * pStr = aString.getStr(); + const char * pWild = m_aWildString.getStr(); + + int pos = 0; + int flag = 0; + + while ( *pWild || flag ) + { + switch ( *pWild ) + { + case '?': + if ( *pStr == '\0' ) + return false; + break; + + default: + if ( ( *pWild == '\\' ) && ( ( *( pWild + 1 ) == '?' ) + || ( *( pWild + 1 ) == '*') ) ) + pWild++; + if ( *pWild != *pStr ) + if ( !pos ) + return false; + else + pWild += pos; + else + break; + + [[fallthrough]]; + + case '*': + while ( *pWild == '*' ) + pWild++; + if ( *pWild == '\0' ) + return true; + flag = 1; + pos = 0; + if ( *pStr == '\0' ) + return ( *pWild == '\0' ); + while ( *pStr && *pStr != *pWild ) + { + if ( *pWild == '?' ) { + pWild++; + while ( *pWild == '*' ) + pWild++; + } + pStr++; + if ( *pStr == '\0' ) + return ( *pWild == '\0' ); + } + break; + } + if ( *pWild != '\0' ) + pWild++; + if ( *pStr != '\0' ) + pStr++; + else + flag = 0; + if ( flag ) + pos--; + } + return ( *pStr == '\0' ) && ( *pWild == '\0' ); +} + + +static bool getConfigStringValue( + const uno::Reference< container::XNameAccess > & xNameAccess, + const OUString& key, + OUString & value ) +{ + try + { + if ( !( xNameAccess->getByName( key ) >>= value ) ) + { + OSL_FAIL( "InternetProxyDecider - " + "Error getting config item value!" ); + return false; + } + } + catch ( lang::WrappedTargetException const & ) + { + return false; + } + catch ( container::NoSuchElementException const & ) + { + return false; + } + return true; +} + + +static bool getConfigInt32Value( + const uno::Reference< container::XNameAccess > & xNameAccess, + const OUString& key, + sal_Int32 & value ) +{ + try + { + uno::Any aValue = xNameAccess->getByName( key ); + if ( aValue.hasValue() && !( aValue >>= value ) ) + { + OSL_FAIL( "InternetProxyDecider - " + "Error getting config item value!" ); + return false; + } + } + catch ( lang::WrappedTargetException const & ) + { + return false; + } + catch ( container::NoSuchElementException const & ) + { + return false; + } + return true; +} + + +// InternetProxyDecider_Impl Implementation. + + +InternetProxyDecider_Impl::InternetProxyDecider_Impl( + const uno::Reference< uno::XComponentContext >& rxContext ) + : m_nProxyType( ProxyType::NoProxy ), + m_aHostnames() +{ + try + { + + // Read proxy configuration from config db. + + + uno::Reference< lang::XMultiServiceFactory > xConfigProv = + configuration::theDefaultProvider::get( rxContext ); + + uno::Sequence< uno::Any > aArguments{ uno::Any(OUString( CONFIG_ROOT_KEY )) }; + uno::Reference< uno::XInterface > xInterface( + xConfigProv->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments ) ); + + OSL_ENSURE( xInterface.is(), + "InternetProxyDecider - No config access!" ); + + if ( xInterface.is() ) + { + uno::Reference< container::XNameAccess > xNameAccess( + xInterface, uno::UNO_QUERY ); + OSL_ENSURE( xNameAccess.is(), + "InternetProxyDecider - No name access!" ); + + if ( xNameAccess.is() ) + { + // *** Proxy type *** + sal_Int32 tmp = 0; + getConfigInt32Value( + xNameAccess, PROXY_TYPE_KEY, tmp ); + m_nProxyType = static_cast<ProxyType>(tmp); + + // *** No proxy list *** + OUString aNoProxyList; + getConfigStringValue( + xNameAccess, NO_PROXY_LIST_KEY, aNoProxyList ); + setNoProxyList( aNoProxyList ); + + // *** HTTP *** + getConfigStringValue( + xNameAccess, HTTP_PROXY_NAME_KEY, m_aHttpProxy.aName ); + + m_aHttpProxy.nPort = -1; + getConfigInt32Value( + xNameAccess, HTTP_PROXY_PORT_KEY, m_aHttpProxy.nPort ); + if ( m_aHttpProxy.nPort == -1 ) + m_aHttpProxy.nPort = 80; // standard HTTP port. + + // *** HTTPS *** + getConfigStringValue( + xNameAccess, HTTPS_PROXY_NAME_KEY, m_aHttpsProxy.aName ); + + m_aHttpsProxy.nPort = -1; + getConfigInt32Value( + xNameAccess, HTTPS_PROXY_PORT_KEY, m_aHttpsProxy.nPort ); + if ( m_aHttpsProxy.nPort == -1 ) + m_aHttpsProxy.nPort = 443; // standard HTTPS port. + + // *** FTP *** + getConfigStringValue( + xNameAccess, FTP_PROXY_NAME_KEY, m_aFtpProxy.aName ); + + m_aFtpProxy.nPort = -1; + getConfigInt32Value( + xNameAccess, FTP_PROXY_PORT_KEY, m_aFtpProxy.nPort ); + } + + // Register as listener for config changes. + + m_xNotifier.set( xInterface, uno::UNO_QUERY ); + + OSL_ENSURE( m_xNotifier.is(), + "InternetProxyDecider - No notifier!" ); + + if ( m_xNotifier.is() ) + m_xNotifier->addChangesListener( this ); + } + } + catch ( uno::Exception const & ) + { + // createInstance, createInstanceWithArguments + OSL_FAIL( "InternetProxyDecider - Exception!" ); + } +} + +void InternetProxyDecider_Impl::dispose() +{ + uno::Reference< util::XChangesNotifier > xNotifier; + + if ( m_xNotifier.is() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( m_xNotifier.is() ) + { + xNotifier = m_xNotifier; + m_xNotifier.clear(); + } + } + + // Do this unguarded! + if ( xNotifier.is() ) + xNotifier->removeChangesListener( this ); +} + + +bool InternetProxyDecider_Impl::shouldUseProxy( std::u16string_view rHost, + sal_Int32 nPort, + bool bUseFullyQualified ) const +{ + OUStringBuffer aBuffer; + + if ( ( rHost.find( ':' ) != std::u16string_view::npos ) && + ( rHost[ 0 ] != '[' ) ) + { + // host is given as numeric IPv6 address + aBuffer.append( "[" ); + aBuffer.append( rHost ); + aBuffer.append( "]" ); + } + else + { + // host is given either as numeric IPv4 address or non-numeric hostname + aBuffer.append( rHost ); + } + + aBuffer.append( ':' ); + aBuffer.append( nPort ); + const OUString aHostAndPort( aBuffer.makeStringAndClear() ); + + for (auto const& noProxy : m_aNoProxyList) + { + if ( bUseFullyQualified ) + { + if ( noProxy.second.Matches( aHostAndPort ) ) + return false; + } + else + { + if ( noProxy.first.Matches( aHostAndPort ) ) + return false; + } + } + + return true; +} + +namespace +{ +#ifdef _WIN32 +struct GetPACProxyData +{ + const OUString& m_rProtocol; + const OUString& m_rHost; + sal_Int32 m_nPort; + bool m_bAutoDetect = false; + OUString m_sAutoConfigUrl; + InternetProxyServer m_ProxyServer; + + GetPACProxyData(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort) + : m_rProtocol(rProtocol) + , m_rHost(rHost) + , m_nPort(nPort) + { + } +}; + +// Tries to get proxy configuration using WinHttpGetProxyForUrl, which supports Web Proxy Auto-Discovery +// (WPAD) protocol and manually configured address to get Proxy Auto-Configuration (PAC) file. +// The WinINet/WinHTTP functions cannot correctly run in a STA COM thread, so use a dedicated thread +DWORD WINAPI GetPACProxyThread(_In_ LPVOID lpParameter) +{ + assert(lpParameter); + GetPACProxyData* pData = static_cast<GetPACProxyData*>(lpParameter); + + OUString url(pData->m_rProtocol + "://" + pData->m_rHost + ":" + + OUString::number(pData->m_nPort)); + + HINTERNET hInternet = WinHttpOpen(L"Mozilla 5.0", WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + DWORD nError = GetLastError(); + if (!hInternet) + return nError; + + WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions{}; + if (pData->m_bAutoDetect) + { + AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + AutoProxyOptions.dwAutoDetectFlags + = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + if (!pData->m_sAutoConfigUrl.isEmpty()) + { + AutoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + AutoProxyOptions.lpszAutoConfigUrl = o3tl::toW(pData->m_sAutoConfigUrl.getStr()); + } + // First, try without autologon. According to + // https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/web/winhttp/WinhttpProxySample/GetProxy.cpp + // autologon prevents caching, and so causes repetitive network traffic. + AutoProxyOptions.fAutoLogonIfChallenged = FALSE; + WINHTTP_PROXY_INFO ProxyInfo{}; + bool bResult + = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()), &AutoProxyOptions, &ProxyInfo); + nError = GetLastError(); + if (!bResult && nError == ERROR_WINHTTP_LOGIN_FAILURE) + { + AutoProxyOptions.fAutoLogonIfChallenged = TRUE; + bResult = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()), + &AutoProxyOptions, &ProxyInfo); + nError = GetLastError(); + } + WinHttpCloseHandle(hInternet); + if (bResult) + { + if (ProxyInfo.lpszProxyBypass) + GlobalFree(ProxyInfo.lpszProxyBypass); + if (ProxyInfo.lpszProxy) + { + OUString sProxyResult(o3tl::toU(ProxyInfo.lpszProxy)); + GlobalFree(ProxyInfo.lpszProxy); + // Get the first of possibly multiple results + sProxyResult = sProxyResult.getToken(0, ';'); + sal_Int32 nPortSepPos = sProxyResult.indexOf(':'); + if (nPortSepPos != -1) + { + pData->m_ProxyServer.nPort = o3tl::toInt32(sProxyResult.subView(nPortSepPos + 1)); + sProxyResult = sProxyResult.copy(0, nPortSepPos); + } + else + { + pData->m_ProxyServer.nPort = 0; + } + pData->m_ProxyServer.aName = sProxyResult; + } + } + + return nError; +} + +InternetProxyServer GetPACProxy(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort) +{ + GetPACProxyData aData(rProtocol, rHost, nPort); + + // WinHTTP only supports http(s), so don't try for other protocols + if (!(rProtocol.equalsIgnoreAsciiCase("http") || rProtocol.equalsIgnoreAsciiCase("https"))) + return aData.m_ProxyServer; + + // Only try to get configuration from PAC (with all the overhead, including new thread) + // if configured to do so + { + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG aProxyConfig{}; + bool bResult = WinHttpGetIEProxyConfigForCurrentUser(&aProxyConfig); + if (aProxyConfig.lpszProxy) + GlobalFree(aProxyConfig.lpszProxy); + if (aProxyConfig.lpszProxyBypass) + GlobalFree(aProxyConfig.lpszProxyBypass); + // Don't try WPAD if AutoDetection or AutoConfig script URL are not configured + if (!bResult || !(aProxyConfig.fAutoDetect || aProxyConfig.lpszAutoConfigUrl)) + return aData.m_ProxyServer; + aData.m_bAutoDetect = aProxyConfig.fAutoDetect; + if (aProxyConfig.lpszAutoConfigUrl) + { + aData.m_sAutoConfigUrl = o3tl::toU(aProxyConfig.lpszAutoConfigUrl); + GlobalFree(aProxyConfig.lpszAutoConfigUrl); + } + } + + HANDLE hThread = CreateThread(nullptr, 0, GetPACProxyThread, &aData, 0, nullptr); + if (hThread) + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + return aData.m_ProxyServer; +} + +#else // .. _WIN32 + +// Read the settings from the OS which are stored in env vars +// +InternetProxyServer GetUnixSystemProxy(const OUString & rProtocol) +{ + // TODO this could be improved to read the "no_proxy" env variable + InternetProxyServer aProxy; + OUString protocolLower = rProtocol.toAsciiLowerCase() + "_proxy"; + OString protocolLowerStr = OUStringToOString( protocolLower, RTL_TEXTENCODING_ASCII_US ); + const char* pEnvProxy = getenv(protocolLowerStr.getStr()); + if (!pEnvProxy) + return aProxy; + // expecting something like "https://example.ct:80" + OUString tmp = OUString::createFromAscii(pEnvProxy); + if (tmp.getLength() < (rProtocol.getLength() + 3)) + return aProxy; + tmp = tmp.copy(rProtocol.getLength() + 3); + sal_Int32 x = tmp.indexOf(':'); + if (x == -1) + return aProxy; + int nPort = o3tl::toInt32(tmp.subView(x + 1)); + if (nPort == 0) + return aProxy; + aProxy.aName = tmp.copy(0, x); + aProxy.nPort = nPort; + return aProxy; +} + +#endif // else .. _WIN32 +} + +InternetProxyServer InternetProxyDecider_Impl::getProxy( + const OUString & rProtocol, + const OUString & rHost, + sal_Int32 nPort ) const +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( m_nProxyType == ProxyType::NoProxy ) + { + // Never use proxy. + return m_aEmptyProxy; + } + + // If get from system + if (m_nProxyType == ProxyType::Automatic && !rHost.isEmpty()) + { +#ifdef _WIN32 + InternetProxyServer aProxy(GetPACProxy(rProtocol, rHost, nPort)); +#else + InternetProxyServer aProxy(GetUnixSystemProxy(rProtocol)); +#endif // _WIN32 + if (!aProxy.aName.isEmpty()) + return aProxy; + } + + if ( !rHost.isEmpty() && !m_aNoProxyList.empty() ) + { + + // First, try direct hostname match - #110515# + + + if ( !shouldUseProxy( rHost, nPort, false ) ) + return m_aEmptyProxy; + + + // Second, try match against full qualified hostname - #104401# + + + OUString aHost; + + if ( ( rHost.getLength() > 1 ) && + ( rHost[ 0 ] == '[' )) + { + // host is given as numeric IPv6 address. name resolution + // functions need hostname without square brackets. + aHost = rHost.copy( 1, rHost.getLength() - 2 ); + } + else + { + aHost = rHost; + } + + OUString aFullyQualifiedHost; + if ( !m_aHostnames.get( aHost, aFullyQualifiedHost ) ) + { + // This might be quite expensive (DNS lookup). + const osl::SocketAddr aAddr( aHost, nPort ); + aFullyQualifiedHost = aAddr.getHostname().toAsciiLowerCase(); + m_aHostnames.put( aHost, aFullyQualifiedHost ); + } + + // Error resolving name? -> fallback. + if ( aFullyQualifiedHost.isEmpty() ) + aFullyQualifiedHost = aHost; + + if ( aFullyQualifiedHost != aHost ) + { + if ( !shouldUseProxy( aFullyQualifiedHost, nPort, false ) ) + return m_aEmptyProxy; + } + + + // Third, try match of fully qualified entries in no-proxy list + // against full qualified hostname + + // Example: + // list: staroffice-doc -> full: xyz.germany.sun.com + // in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com + + + if ( !shouldUseProxy( aFullyQualifiedHost, nPort, true ) ) + return m_aEmptyProxy; + } + + if ( rProtocol.toAsciiLowerCase() == "ftp" ) + { + if ( !m_aFtpProxy.aName.isEmpty() && m_aFtpProxy.nPort >= 0 ) + return m_aFtpProxy; + } + else if ( rProtocol.toAsciiLowerCase() == "https" ) + { + if ( !m_aHttpsProxy.aName.isEmpty() ) + return m_aHttpsProxy; + } + else if ( !m_aHttpProxy.aName.isEmpty() ) + { + // All other protocols use the HTTP proxy. + return m_aHttpProxy; + } + return m_aEmptyProxy; +} + +// virtual +void SAL_CALL InternetProxyDecider_Impl::changesOccurred( + const util::ChangesEvent& Event ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + for ( const util::ElementChange& rElem : Event.Changes ) + { + OUString aKey; + if ( ( rElem.Accessor >>= aKey ) && !aKey.isEmpty() ) + { + if ( aKey == PROXY_TYPE_KEY ) + { + sal_Int32 tmp; + if ( !( rElem.Element >>= tmp ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + else + m_nProxyType = static_cast<ProxyType>(tmp); + } + else if ( aKey == NO_PROXY_LIST_KEY ) + { + OUString aNoProxyList; + if ( !( rElem.Element >>= aNoProxyList ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + + setNoProxyList( aNoProxyList ); + } + else if ( aKey == HTTP_PROXY_NAME_KEY ) + { + if ( !( rElem.Element >>= m_aHttpProxy.aName ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + } + else if ( aKey == HTTP_PROXY_PORT_KEY ) + { + if ( !( rElem.Element >>= m_aHttpProxy.nPort ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + + if ( m_aHttpProxy.nPort == -1 ) + m_aHttpProxy.nPort = 80; // standard HTTP port. + } + else if ( aKey == HTTPS_PROXY_NAME_KEY ) + { + if ( !( rElem.Element >>= m_aHttpsProxy.aName ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + } + else if ( aKey == HTTPS_PROXY_PORT_KEY ) + { + if ( !( rElem.Element >>= m_aHttpsProxy.nPort ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + + if ( m_aHttpsProxy.nPort == -1 ) + m_aHttpsProxy.nPort = 443; // standard HTTPS port. + } + else if ( aKey == FTP_PROXY_NAME_KEY ) + { + if ( !( rElem.Element >>= m_aFtpProxy.aName ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + } + else if ( aKey == FTP_PROXY_PORT_KEY ) + { + if ( !( rElem.Element >>= m_aFtpProxy.nPort ) ) + { + OSL_FAIL( "InternetProxyDecider - changesOccurred - " + "Error getting config item value!" ); + } + } + } + } +} + + +// virtual +void SAL_CALL InternetProxyDecider_Impl::disposing(const lang::EventObject&) +{ + if ( m_xNotifier.is() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( m_xNotifier.is() ) + m_xNotifier.clear(); + } +} + + +void InternetProxyDecider_Impl::setNoProxyList( + const OUString & rNoProxyList ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + m_aNoProxyList.clear(); + + if ( rNoProxyList.isEmpty() ) + return; + + // List of connection endpoints hostname[:port], + // separated by semicolon. Wildcards allowed. + + sal_Int32 nPos = 0; + sal_Int32 nEnd = rNoProxyList.indexOf( ';' ); + sal_Int32 nLen = rNoProxyList.getLength(); + + do + { + if ( nEnd == -1 ) + nEnd = nLen; + + OUString aToken = rNoProxyList.copy( nPos, nEnd - nPos ); + + if ( !aToken.isEmpty() ) + { + OUString aServer; + OUString aPort; + + // numerical IPv6 address? + bool bIPv6Address = false; + sal_Int32 nClosedBracketPos = aToken.indexOf( ']' ); + if ( nClosedBracketPos == -1 ) + nClosedBracketPos = 0; + else + bIPv6Address = true; + + sal_Int32 nColonPos = aToken.indexOf( ':', nClosedBracketPos ); + if ( nColonPos == -1 ) + { + // No port given, server pattern equals current token + aPort = "*"; + if ( aToken.indexOf( '*' ) == -1 ) + { + // pattern describes exactly one server + aServer = aToken; + } + + aToken += ":*"; + } + else + { + // Port given, extract server pattern + sal_Int32 nAsteriskPos = aToken.indexOf( '*' ); + aPort = aToken.copy( nColonPos + 1 ); + if ( nAsteriskPos < nColonPos ) + { + // pattern describes exactly one server + aServer = aToken.copy( 0, nColonPos ); + } + } + + OUStringBuffer aFullyQualifiedHost; + if ( !aServer.isEmpty() ) + { + // Remember fully qualified server name if current list + // entry specifies exactly one non-fully qualified server + // name. + + // remove square brackets from host name in case it's + // a numerical IPv6 address. + if ( bIPv6Address ) + aServer = aServer.copy( 1, aServer.getLength() - 2 ); + + // This might be quite expensive (DNS lookup). + const osl::SocketAddr aAddr( aServer, 0 ); + OUString aTmp = aAddr.getHostname().toAsciiLowerCase(); + if ( aTmp != aServer.toAsciiLowerCase() ) + { + if ( bIPv6Address ) + { + aFullyQualifiedHost.append( "[" ); + aFullyQualifiedHost.append( aTmp ); + aFullyQualifiedHost.append( "]" ); + } + else + { + aFullyQualifiedHost.append( aTmp ); + } + aFullyQualifiedHost.append( ":" ); + aFullyQualifiedHost.append( aPort ); + } + } + + m_aNoProxyList.emplace_back( WildCard( aToken ), + WildCard( aFullyQualifiedHost ) ); + } + + if ( nEnd != nLen ) + { + nPos = nEnd + 1; + nEnd = rNoProxyList.indexOf( ';', nPos ); + } + } + while ( nEnd != nLen ); +} + +} // namespace proxydecider_impl + + +// InternetProxyDecider Implementation. + + +InternetProxyDecider::InternetProxyDecider( + const uno::Reference< uno::XComponentContext>& rxContext ) +: m_xImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxContext ) ) +{ +} + + +InternetProxyDecider::~InternetProxyDecider() +{ + // Break circular reference between config listener and notifier. + m_xImpl->dispose(); +} + + +bool InternetProxyDecider::shouldUseProxy( const OUString & rProtocol, + const OUString & rHost, + sal_Int32 nPort ) const +{ + const InternetProxyServer & rData = m_xImpl->getProxy( rProtocol, + rHost, + nPort ); + return !rData.aName.isEmpty(); +} + + +InternetProxyServer InternetProxyDecider::getProxy( + const OUString & rProtocol, + const OUString & rHost, + sal_Int32 nPort ) const +{ + return m_xImpl->getProxy( rProtocol, rHost, nPort ); +} + +} // namespace ucbhelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |