1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
24 #include <osl/diagnose.h>
25 #include <osl/mutex.hxx>
26 #include <rtl/ref.hxx>
27 #include <osl/socket.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <com/sun/star/container/XNameAccess.hpp>
30 #include <com/sun/star/configuration/theDefaultProvider.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/util/XChangesListener.hpp>
33 #include <com/sun/star/util/XChangesNotifier.hpp>
34 #include <cppuhelper/implbase.hxx>
35 #include "ucbhelper/proxydecider.hxx"
37 using namespace com::sun::star
;
38 using namespace ucbhelper
;
40 #define CONFIG_ROOT_KEY "org.openoffice.Inet/Settings"
41 #define PROXY_TYPE_KEY "ooInetProxyType"
42 #define NO_PROXY_LIST_KEY "ooInetNoProxy"
43 #define HTTP_PROXY_NAME_KEY "ooInetHTTPProxyName"
44 #define HTTP_PROXY_PORT_KEY "ooInetHTTPProxyPort"
45 #define HTTPS_PROXY_NAME_KEY "ooInetHTTPSProxyName"
46 #define HTTPS_PROXY_PORT_KEY "ooInetHTTPSProxyPort"
47 #define FTP_PROXY_NAME_KEY "ooInetFTPProxyName"
48 #define FTP_PROXY_PORT_KEY "ooInetFTPProxyPort"
55 namespace proxydecider_impl
58 // A simple case ignoring wildcard matcher.
62 OString m_aWildString
;
65 explicit WildCard( const OUString
& rWildCard
)
68 rWildCard
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase() ) {}
70 bool Matches( const OUString
& rStr
) const;
74 typedef std::pair
< WildCard
, WildCard
> NoProxyListEntry
;
79 typedef std::pair
< OUString
, OUString
> HostListEntry
;
81 std::list
< HostListEntry
> m_aHostList
;
82 sal_uInt32 m_nCapacity
;
85 explicit HostnameCache()
86 : m_nCapacity( 256 ) {}
88 bool get( const OUString
& rKey
, OUString
& rValue
) const
90 std::list
< HostListEntry
>::const_iterator it
91 = m_aHostList
.begin();
92 const std::list
< HostListEntry
>::const_iterator end
97 if ( (*it
).first
== rKey
)
99 rValue
= (*it
).second
;
107 void put( const OUString
& rKey
, const OUString
& rValue
)
109 if ( m_aHostList
.size() == m_nCapacity
)
110 m_aHostList
.resize( m_nCapacity
/ 2 );
112 m_aHostList
.push_front( HostListEntry( rKey
, rValue
) );
117 class InternetProxyDecider_Impl
:
118 public cppu::WeakImplHelper
< util::XChangesListener
>
120 mutable osl::Mutex m_aMutex
;
121 InternetProxyServer m_aHttpProxy
;
122 InternetProxyServer m_aHttpsProxy
;
123 InternetProxyServer m_aFtpProxy
;
124 const InternetProxyServer m_aEmptyProxy
;
125 sal_Int32 m_nProxyType
;
126 uno::Reference
< util::XChangesNotifier
> m_xNotifier
;
127 std::vector
< NoProxyListEntry
> m_aNoProxyList
;
128 mutable HostnameCache m_aHostnames
;
131 bool shouldUseProxy( const OUString
& rHost
,
133 bool bUseFullyQualified
) const;
135 explicit InternetProxyDecider_Impl(
136 const uno::Reference
< uno::XComponentContext
>& rxContext
);
137 virtual ~InternetProxyDecider_Impl() override
;
141 const InternetProxyServer
& getProxy( const OUString
& rProtocol
,
142 const OUString
& rHost
,
143 sal_Int32 nPort
) const;
146 virtual void SAL_CALL
changesOccurred( const util::ChangesEvent
& Event
)
147 throw( uno::RuntimeException
, std::exception
) override
;
149 // XEventListener ( base of XChangesLisetenr )
150 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
)
151 throw( uno::RuntimeException
, std::exception
) override
;
154 void setNoProxyList( const OUString
& rNoProxyList
);
158 // WildCard Implementation.
161 bool WildCard::Matches( const OUString
& rString
) const
165 rString
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase();
166 const char * pStr
= aString
.getStr();
167 const char * pWild
= m_aWildString
.getStr();
172 while ( *pWild
|| flag
)
182 if ( ( *pWild
== '\\' ) && ( ( *( pWild
+ 1 ) == '?' )
183 || ( *( pWild
+ 1 ) == '*') ) )
185 if ( *pWild
!= *pStr
)
196 while ( *pWild
== '*' )
198 if ( *pWild
== '\0' )
203 return ( *pWild
== '\0' );
204 while ( *pStr
&& *pStr
!= *pWild
)
206 if ( *pWild
== '?' ) {
208 while ( *pWild
== '*' )
213 return ( *pWild
== '\0' );
217 if ( *pWild
!= '\0' )
226 return ( *pStr
== '\0' ) && ( *pWild
== '\0' );
230 bool getConfigStringValue(
231 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
237 if ( !( xNameAccess
->getByName( OUString::createFromAscii( key
) )
240 OSL_FAIL( "InternetProxyDecider - "
241 "Error getting config item value!" );
245 catch ( lang::WrappedTargetException
const & )
249 catch ( container::NoSuchElementException
const & )
257 bool getConfigInt32Value(
258 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
264 uno::Any aValue
= xNameAccess
->getByName(
265 OUString::createFromAscii( key
) );
266 if ( aValue
.hasValue() && !( aValue
>>= value
) )
268 OSL_FAIL( "InternetProxyDecider - "
269 "Error getting config item value!" );
273 catch ( lang::WrappedTargetException
const & )
277 catch ( container::NoSuchElementException
const & )
285 // InternetProxyDecider_Impl Implementation.
288 InternetProxyDecider_Impl::InternetProxyDecider_Impl(
289 const uno::Reference
< uno::XComponentContext
>& rxContext
)
296 // Read proxy configuration from config db.
299 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
300 configuration::theDefaultProvider::get( rxContext
);
302 uno::Sequence
< uno::Any
> aArguments( 1 );
303 aArguments
[ 0 ] <<= OUString( CONFIG_ROOT_KEY
);
305 uno::Reference
< uno::XInterface
> xInterface(
306 xConfigProv
->createInstanceWithArguments(
307 "com.sun.star.configuration.ConfigurationAccess",
310 OSL_ENSURE( xInterface
.is(),
311 "InternetProxyDecider - No config access!" );
313 if ( xInterface
.is() )
315 uno::Reference
< container::XNameAccess
> xNameAccess(
316 xInterface
, uno::UNO_QUERY
);
317 OSL_ENSURE( xNameAccess
.is(),
318 "InternetProxyDecider - No name access!" );
320 if ( xNameAccess
.is() )
322 // *** Proxy type ***
324 xNameAccess
, PROXY_TYPE_KEY
, m_nProxyType
);
326 // *** No proxy list ***
327 OUString aNoProxyList
;
328 getConfigStringValue(
329 xNameAccess
, NO_PROXY_LIST_KEY
, aNoProxyList
);
330 setNoProxyList( aNoProxyList
);
333 getConfigStringValue(
334 xNameAccess
, HTTP_PROXY_NAME_KEY
, m_aHttpProxy
.aName
);
336 m_aHttpProxy
.nPort
= -1;
338 xNameAccess
, HTTP_PROXY_PORT_KEY
, m_aHttpProxy
.nPort
);
339 if ( m_aHttpProxy
.nPort
== -1 )
340 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
343 getConfigStringValue(
344 xNameAccess
, HTTPS_PROXY_NAME_KEY
, m_aHttpsProxy
.aName
);
346 m_aHttpsProxy
.nPort
= -1;
348 xNameAccess
, HTTPS_PROXY_PORT_KEY
, m_aHttpsProxy
.nPort
);
349 if ( m_aHttpsProxy
.nPort
== -1 )
350 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
353 getConfigStringValue(
354 xNameAccess
, FTP_PROXY_NAME_KEY
, m_aFtpProxy
.aName
);
356 m_aFtpProxy
.nPort
= -1;
358 xNameAccess
, FTP_PROXY_PORT_KEY
, m_aFtpProxy
.nPort
);
361 // Register as listener for config changes.
363 m_xNotifier
.set( xInterface
, uno::UNO_QUERY
);
365 OSL_ENSURE( m_xNotifier
.is(),
366 "InternetProxyDecider - No notifier!" );
368 if ( m_xNotifier
.is() )
369 m_xNotifier
->addChangesListener( this );
372 catch ( uno::Exception
const & )
374 // createInstance, createInstanceWithArguments
375 OSL_FAIL( "InternetProxyDecider - Exception!" );
381 InternetProxyDecider_Impl::~InternetProxyDecider_Impl()
386 void InternetProxyDecider_Impl::dispose()
388 uno::Reference
< util::XChangesNotifier
> xNotifier
;
390 if ( m_xNotifier
.is() )
392 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
394 if ( m_xNotifier
.is() )
396 xNotifier
= m_xNotifier
;
401 // Do this unguarded!
402 if ( xNotifier
.is() )
403 xNotifier
->removeChangesListener( this );
407 bool InternetProxyDecider_Impl::shouldUseProxy( const OUString
& rHost
,
409 bool bUseFullyQualified
) const
411 OUStringBuffer aBuffer
;
413 if ( ( rHost
.indexOf( ':' ) != -1 ) &&
414 ( rHost
[ 0 ] != '[' ) )
416 // host is given as numeric IPv6 address
417 aBuffer
.append( "[" );
418 aBuffer
.append( rHost
);
419 aBuffer
.append( "]" );
423 // host is given either as numeric IPv4 address or non-numeric hostname
424 aBuffer
.append( rHost
);
427 aBuffer
.append( ':' );
428 aBuffer
.append( OUString::number( nPort
) );
429 const OUString
aHostAndPort( aBuffer
.makeStringAndClear() );
431 std::vector
< NoProxyListEntry
>::const_iterator it
432 = m_aNoProxyList
.begin();
433 const std::vector
< NoProxyListEntry
>::const_iterator end
434 = m_aNoProxyList
.end();
438 if ( bUseFullyQualified
)
440 if ( (*it
).second
.Matches( aHostAndPort
) )
445 if ( (*it
).first
.Matches( aHostAndPort
) )
455 const InternetProxyServer
& InternetProxyDecider_Impl::getProxy(
456 const OUString
& rProtocol
,
457 const OUString
& rHost
,
458 sal_Int32 nPort
) const
460 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
462 if ( m_nProxyType
== 0 )
465 return m_aEmptyProxy
;
468 if ( !rHost
.isEmpty() && !m_aNoProxyList
.empty() )
471 // First, try direct hostname match - #110515#
474 if ( !shouldUseProxy( rHost
, nPort
, false ) )
475 return m_aEmptyProxy
;
478 // Second, try match against full qualified hostname - #104401#
483 if ( ( rHost
.getLength() > 1 ) &&
484 ( rHost
[ 0 ] == '[' ))
486 // host is given as numeric IPv6 address. name resolution
487 // functions need hostname without square brackets.
488 aHost
= rHost
.copy( 1, rHost
.getLength() - 2 );
495 OUString aFullyQualifiedHost
;
496 if ( !m_aHostnames
.get( aHost
, aFullyQualifiedHost
) )
498 // This might be quite expensive (DNS lookup).
499 const osl::SocketAddr
aAddr( aHost
, nPort
);
500 aFullyQualifiedHost
= aAddr
.getHostname().toAsciiLowerCase();
501 m_aHostnames
.put( aHost
, aFullyQualifiedHost
);
504 // Error resolving name? -> fallback.
505 if ( aFullyQualifiedHost
.isEmpty() )
506 aFullyQualifiedHost
= aHost
;
508 if ( aFullyQualifiedHost
!= aHost
)
510 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, false ) )
511 return m_aEmptyProxy
;
515 // Third, try match of fully qualified entries in no-proxy list
516 // against full qualified hostname
519 // list: staroffice-doc -> full: xyz.germany.sun.com
520 // in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com
523 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, true ) )
524 return m_aEmptyProxy
;
527 if ( rProtocol
.toAsciiLowerCase() == "ftp" )
529 if ( !m_aFtpProxy
.aName
.isEmpty() && m_aFtpProxy
.nPort
>= 0 )
532 else if ( rProtocol
.toAsciiLowerCase() == "https" )
534 if ( !m_aHttpsProxy
.aName
.isEmpty() )
535 return m_aHttpsProxy
;
537 else if ( !m_aHttpProxy
.aName
.isEmpty() )
539 // All other protocols use the HTTP proxy.
542 return m_aEmptyProxy
;
547 void SAL_CALL
InternetProxyDecider_Impl::changesOccurred(
548 const util::ChangesEvent
& Event
)
549 throw( uno::RuntimeException
, std::exception
)
551 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
553 sal_Int32 nCount
= Event
.Changes
.getLength();
556 const util::ElementChange
* pElementChanges
557 = Event
.Changes
.getConstArray();
558 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
560 const util::ElementChange
& rElem
= pElementChanges
[ n
];
562 if ( ( rElem
.Accessor
>>= aKey
) && !aKey
.isEmpty() )
564 if ( aKey
== PROXY_TYPE_KEY
)
566 if ( !( rElem
.Element
>>= m_nProxyType
) )
568 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
569 "Error getting config item value!" );
572 else if ( aKey
== NO_PROXY_LIST_KEY
)
574 OUString aNoProxyList
;
575 if ( !( rElem
.Element
>>= aNoProxyList
) )
577 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
578 "Error getting config item value!" );
581 setNoProxyList( aNoProxyList
);
583 else if ( aKey
== HTTP_PROXY_NAME_KEY
)
585 if ( !( rElem
.Element
>>= m_aHttpProxy
.aName
) )
587 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
588 "Error getting config item value!" );
591 else if ( aKey
== HTTP_PROXY_PORT_KEY
)
593 if ( !( rElem
.Element
>>= m_aHttpProxy
.nPort
) )
595 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
596 "Error getting config item value!" );
599 if ( m_aHttpProxy
.nPort
== -1 )
600 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
602 else if ( aKey
== HTTPS_PROXY_NAME_KEY
)
604 if ( !( rElem
.Element
>>= m_aHttpsProxy
.aName
) )
606 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
607 "Error getting config item value!" );
610 else if ( aKey
== HTTPS_PROXY_PORT_KEY
)
612 if ( !( rElem
.Element
>>= m_aHttpsProxy
.nPort
) )
614 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
615 "Error getting config item value!" );
618 if ( m_aHttpsProxy
.nPort
== -1 )
619 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
621 else if ( aKey
== FTP_PROXY_NAME_KEY
)
623 if ( !( rElem
.Element
>>= m_aFtpProxy
.aName
) )
625 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
626 "Error getting config item value!" );
629 else if ( aKey
== FTP_PROXY_PORT_KEY
)
631 if ( !( rElem
.Element
>>= m_aFtpProxy
.nPort
) )
633 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
634 "Error getting config item value!" );
644 void SAL_CALL
InternetProxyDecider_Impl::disposing(const lang::EventObject
&)
645 throw( uno::RuntimeException
, std::exception
)
647 if ( m_xNotifier
.is() )
649 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
651 if ( m_xNotifier
.is() )
657 void InternetProxyDecider_Impl::setNoProxyList(
658 const OUString
& rNoProxyList
)
660 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
662 m_aNoProxyList
.clear();
664 if ( !rNoProxyList
.isEmpty() )
666 // List of connection endpoints hostname[:port],
667 // separated by semicolon. Wildcards allowed.
670 sal_Int32 nEnd
= rNoProxyList
.indexOf( ';' );
671 sal_Int32 nLen
= rNoProxyList
.getLength();
678 OUString aToken
= rNoProxyList
.copy( nPos
, nEnd
- nPos
);
680 if ( !aToken
.isEmpty() )
685 // numerical IPv6 address?
686 bool bIPv6Address
= false;
687 sal_Int32 nClosedBracketPos
= aToken
.indexOf( ']' );
688 if ( nClosedBracketPos
== -1 )
689 nClosedBracketPos
= 0;
693 sal_Int32 nColonPos
= aToken
.indexOf( ':', nClosedBracketPos
);
694 if ( nColonPos
== -1 )
696 // No port given, server pattern equals current token
698 if ( aToken
.indexOf( '*' ) == -1 )
700 // pattern describes exactly one server
708 // Port given, extract server pattern
709 sal_Int32 nAsteriskPos
= aToken
.indexOf( '*' );
710 aPort
= aToken
.copy( nColonPos
+ 1 );
711 if ( nAsteriskPos
< nColonPos
)
713 // pattern describes exactly one server
714 aServer
= aToken
.copy( 0, nColonPos
);
718 OUStringBuffer aFullyQualifiedHost
;
719 if ( !aServer
.isEmpty() )
721 // Remember fully qualified server name if current list
722 // entry specifies exactly one non-fully qualified server
725 // remove square brackets from host name in case it's
726 // a numerical IPv6 address.
728 aServer
= aServer
.copy( 1, aServer
.getLength() - 2 );
730 // This might be quite expensive (DNS lookup).
731 const osl::SocketAddr
aAddr( aServer
, 0 );
732 OUString aTmp
= aAddr
.getHostname().toAsciiLowerCase();
733 if ( aTmp
!= aServer
.toAsciiLowerCase() )
737 aFullyQualifiedHost
.append( "[" );
738 aFullyQualifiedHost
.append( aTmp
);
739 aFullyQualifiedHost
.append( "]" );
743 aFullyQualifiedHost
.append( aTmp
);
745 aFullyQualifiedHost
.append( ":" );
746 aFullyQualifiedHost
.append( aPort
);
750 m_aNoProxyList
.push_back(
751 NoProxyListEntry( WildCard( aToken
),
754 .makeStringAndClear() ) ) );
760 nEnd
= rNoProxyList
.indexOf( ';', nPos
);
763 while ( nEnd
!= nLen
);
767 } // namespace proxydecider_impl
770 // InternetProxyDecider Implementation.
773 InternetProxyDecider::InternetProxyDecider(
774 const uno::Reference
< uno::XComponentContext
>& rxContext
)
775 : m_xImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxContext
) )
780 InternetProxyDecider::~InternetProxyDecider()
782 // Break circular reference between config listener and notifier.
787 bool InternetProxyDecider::shouldUseProxy( const OUString
& rProtocol
,
788 const OUString
& rHost
,
789 sal_Int32 nPort
) const
791 const InternetProxyServer
& rData
= m_xImpl
->getProxy( rProtocol
,
794 return !rData
.aName
.isEmpty();
798 const InternetProxyServer
& InternetProxyDecider::getProxy(
799 const OUString
& rProtocol
,
800 const OUString
& rHost
,
801 sal_Int32 nPort
) const
803 return m_xImpl
->getProxy( rProtocol
, rHost
, nPort
);
806 } // namespace ucbhelper
808 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */