summaryrefslogtreecommitdiffstats
path: root/ucbhelper/source/client
diff options
context:
space:
mode:
Diffstat (limited to 'ucbhelper/source/client')
-rw-r--r--ucbhelper/source/client/activedatasink.cxx40
-rw-r--r--ucbhelper/source/client/activedatastreamer.cxx41
-rw-r--r--ucbhelper/source/client/activedatastreamer.hxx48
-rw-r--r--ucbhelper/source/client/commandenvironment.cxx77
-rw-r--r--ucbhelper/source/client/content.cxx1353
-rw-r--r--ucbhelper/source/client/interceptedinteraction.cxx138
-rw-r--r--ucbhelper/source/client/proxydecider.cxx916
7 files changed, 2613 insertions, 0 deletions
diff --git a/ucbhelper/source/client/activedatasink.cxx b/ucbhelper/source/client/activedatasink.cxx
new file mode 100644
index 0000000000..96e75f565b
--- /dev/null
+++ b/ucbhelper/source/client/activedatasink.cxx
@@ -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 .
+ */
+
+#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 0000000000..fe6be13536
--- /dev/null
+++ b/ucbhelper/source/client/activedatastreamer.cxx
@@ -0,0 +1,41 @@
+/* -*- 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 "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 0000000000..84ee10ac96
--- /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 0000000000..37956b2c46
--- /dev/null
+++ b/ucbhelper/source/client/commandenvironment.cxx
@@ -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 .
+ */
+
+#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 0000000000..8feab846ba
--- /dev/null
+++ b/ucbhelper/source/client/content.cxx
@@ -0,0 +1,1353 @@
+/* -*- 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);
+ const 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;
+}
+
+
+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() );
+
+ O3TL_UNREACHABLE;
+}
+
+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;
+}
+
+const 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 0000000000..96b3fd32cb
--- /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 0000000000..8acc15716a
--- /dev/null
+++ b/ucbhelper/source/client/proxydecider.cxx
@@ -0,0 +1,916 @@
+/* -*- 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>
+#include <process.h>
+#endif
+
+using namespace com::sun::star;
+using namespace ucbhelper;
+
+constexpr OUString CONFIG_ROOT_KEY = u"org.openoffice.Inet/Settings"_ustr;
+constexpr OUString PROXY_TYPE_KEY = u"ooInetProxyType"_ustr;
+constexpr OUString NO_PROXY_LIST_KEY = u"ooInetNoProxy"_ustr;
+constexpr OUString HTTP_PROXY_NAME_KEY = u"ooInetHTTPProxyName"_ustr;
+constexpr OUString HTTP_PROXY_PORT_KEY = u"ooInetHTTPProxyPort"_ustr;
+constexpr OUString HTTPS_PROXY_NAME_KEY = u"ooInetHTTPSProxyName"_ustr;
+constexpr OUString HTTPS_PROXY_PORT_KEY = u"ooInetHTTPSProxyPort"_ustr;
+
+
+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;
+ 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(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.
+ }
+
+ // 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( OUString::Concat("[") + rHost + "]" );
+ }
+ else
+ {
+ // host is given either as numeric IPv4 address or non-numeric hostname
+ aBuffer.append( rHost );
+ }
+
+ aBuffer.append( ":" + OUString::number( nPort ) );
+
+ for (auto const& noProxy : m_aNoProxyList)
+ {
+ if ( bUseFullyQualified )
+ {
+ if ( noProxy.second.Matches( aBuffer ) )
+ return false;
+ }
+ else
+ {
+ if ( noProxy.first.Matches( aBuffer ) )
+ 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
+unsigned __stdcall GetPACProxyThread(void* 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 = reinterpret_cast<HANDLE>(
+ _beginthreadex(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;
+ sal_Int32 x = tmp.indexOf("://");
+ sal_Int32 at = tmp.indexOf('@', x == -1 ? 0 : x + 3);
+ x = tmp.indexOf(':', at == -1 ? x == -1 ? 0 : x + 3 : at + 1);
+ 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() == "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.
+ }
+ }
+ }
+}
+
+
+// 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( "[" + aTmp + "]" );
+ else
+ aFullyQualifiedHost.append( aTmp );
+ 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();
+}
+
+
+OUString InternetProxyDecider::getProxy(
+ const OUString & rProtocol,
+ const OUString & rHost,
+ sal_Int32 nPort ) const
+{
+ InternetProxyServer ret(m_xImpl->getProxy(rProtocol, rHost, nPort));
+
+ if (ret.aName.isEmpty() || ret.nPort == -1)
+ {
+ return ret.aName;
+ }
+
+ return ret.aName + ":" + OUString::number(ret.nPort);
+}
+
+} // namespace ucbhelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */