office-gobmx/ucbhelper/source/client/proxydecider.cxx
Noel Grandin f4c7ffc8ed loplugin:ostr in ucbhelper
Change-Id: Ic0513708b208ec4cba54eedc15e308410eef8d98
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167731
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Tested-by: Jenkins
2024-05-16 14:49:15 +02:00

916 lines
28 KiB
C++

/* -*- 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( std::u16string_view 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(
u"com.sun.star.configuration.ConfigurationAccess"_ustr,
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(
std::u16string_view rNoProxyList )
{
osl::Guard< osl::Mutex > aGuard( m_aMutex );
m_aNoProxyList.clear();
if ( rNoProxyList.empty() )
return;
// List of connection endpoints hostname[:port],
// separated by semicolon. Wildcards allowed.
size_t nPos = 0;
size_t nEnd = rNoProxyList.find( ';' );
size_t nLen = rNoProxyList.size();
do
{
if ( nEnd == std::u16string_view::npos )
nEnd = nLen;
OUString aToken( rNoProxyList.substr( 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.find( ';', 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: */