summaryrefslogtreecommitdiffstats
path: root/ucbhelper/source/client/proxydecider.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucbhelper/source/client/proxydecider.cxx
parentInitial commit. (diff)
downloadlibreoffice-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/proxydecider.cxx960
1 files changed, 960 insertions, 0 deletions
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: */