f4c7ffc8ed
Change-Id: Ic0513708b208ec4cba54eedc15e308410eef8d98 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167731 Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> Tested-by: Jenkins
916 lines
28 KiB
C++
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: */
|