    /* -*- 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 <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>

    #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;

    #define CONFIG_ROOT_KEY      "org.openoffice.Inet/Settings"
    #define PROXY_TYPE_KEY       "ooInetProxyType"
    #define NO_PROXY_LIST_KEY    "ooInetNoProxy"
    #define HTTP_PROXY_NAME_KEY  "ooInetHTTPProxyName"
    #define HTTP_PROXY_PORT_KEY  "ooInetHTTPProxyPort"
    #define HTTPS_PROXY_NAME_KEY "ooInetHTTPSProxyName"
    #define HTTPS_PROXY_PORT_KEY "ooInetHTTPSProxyPort"
    #define FTP_PROXY_NAME_KEY   "ooInetFTPProxyName"
    #define FTP_PROXY_PORT_KEY   "ooInetFTPProxyPort"

    namespace ucbhelper
    {

    namespace proxydecider_impl
    {

    // A simple case ignoring wildcard matcher.
    class WildCard
    {
    private:
        OString m_aWildString;

    public:
        explicit WildCard( const OUString& rWildCard )
        : m_aWildString(
            OUStringToOString(
                rWildCard, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() ) {}

        bool Matches( const OUString & rStr ) const;
    };

    typedef std::pair< WildCard, WildCard > NoProxyListEntry;

    class HostnameCache
    {
        typedef std::pair< OUString, OUString > HostListEntry;

        std::deque< HostListEntry >    m_aHostList;
        sal_uInt32                     m_nCapacity;

    public:
        explicit HostnameCache()
            : m_nCapacity( 256 ) {}

        bool get( const OUString & 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 )
        {
            if ( m_aHostList.size() == m_nCapacity )
                m_aHostList.resize( m_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;
        std::vector< NoProxyListEntry >          m_aNoProxyList;
        mutable HostnameCache                    m_aHostnames;

    private:
        bool shouldUseProxy( const OUString & 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( const OUString& 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;

                    SAL_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' );
    }

    bool getConfigStringValue(
        const uno::Reference< container::XNameAccess > & xNameAccess,
        const char * key,
        OUString & value )
    {
        try
        {
            if ( !( xNameAccess->getByName( OUString::createFromAscii( 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;
    }

    bool getConfigInt32Value(
        const uno::Reference< container::XNameAccess > & xNameAccess,
        const char * key,
        sal_Int32 & value )
    {
        try
        {
            uno::Any aValue = xNameAccess->getByName(
                OUString::createFromAscii( 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( 1 );
            aArguments[ 0 ] <<= 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( const OUString & rHost,
                                                    sal_Int32 nPort,
                                                    bool bUseFullyQualified ) const
    {
        OUStringBuffer aBuffer;

        if ( ( rHost.indexOf( ':' ) != -1 ) &&
             ( 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( OUString::number( 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 = sProxyResult.copy(nPortSepPos + 1).toInt32();
                    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,
                                                const OUString & /*rHost*/,
                                                sal_Int32 /*nPort*/)
    {
        // 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 = tmp.copy(x + 1).toInt32();
        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, rHost, nPort));
    #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 );

        sal_Int32 nCount = Event.Changes.getLength();
        if ( nCount )
        {
            const util::ElementChange* pElementChanges
                = Event.Changes.getConstArray();
            for ( sal_Int32 n = 0; n < nCount; ++n )
            {
                const util::ElementChange& rElem = pElementChanges[ n ];
                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() )
        {
            // 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.makeStringAndClear() ) );
                }

                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: */

↑ V1019 Compound assignment expression 'aValue >>= value' is used inside condition.