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 .
21 /**************************************************************************
23 **************************************************************************
25 *************************************************************************/
30 #include <osl/mutex.hxx>
31 #include <rtl/ref.hxx>
32 #include <osl/socket.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <com/sun/star/container/XNameAccess.hpp>
35 #include <com/sun/star/configuration/theDefaultProvider.hpp>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37 #include <com/sun/star/util/XChangesListener.hpp>
38 #include <com/sun/star/util/XChangesNotifier.hpp>
39 #include <cppuhelper/implbase1.hxx>
40 #include "ucbhelper/proxydecider.hxx"
42 using namespace com::sun::star
;
43 using namespace ucbhelper
;
45 #define CONFIG_ROOT_KEY "org.openoffice.Inet/Settings"
46 #define PROXY_TYPE_KEY "ooInetProxyType"
47 #define NO_PROXY_LIST_KEY "ooInetNoProxy"
48 #define HTTP_PROXY_NAME_KEY "ooInetHTTPProxyName"
49 #define HTTP_PROXY_PORT_KEY "ooInetHTTPProxyPort"
50 #define HTTPS_PROXY_NAME_KEY "ooInetHTTPSProxyName"
51 #define HTTPS_PROXY_PORT_KEY "ooInetHTTPSProxyPort"
52 #define FTP_PROXY_NAME_KEY "ooInetFTPProxyName"
53 #define FTP_PROXY_PORT_KEY "ooInetFTPProxyPort"
55 //=========================================================================
59 //=========================================================================
60 namespace proxydecider_impl
63 // A simple case ignoring wildcard matcher.
67 OString m_aWildString
;
70 WildCard( const OUString
& rWildCard
)
73 rWildCard
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase() ) {}
75 bool Matches( const OUString
& rStr
) const;
78 //=========================================================================
79 typedef std::pair
< WildCard
, WildCard
> NoProxyListEntry
;
81 //=========================================================================
85 typedef std::pair
< OUString
, OUString
> HostListEntry
;
87 std::list
< HostListEntry
> m_aHostList
;
88 sal_uInt32 m_nCapacity
;
91 explicit HostnameCache( sal_uInt32 nCapacity
)
92 : m_nCapacity( nCapacity
) {}
94 bool get( const OUString
& rKey
, OUString
& rValue
) const
96 std::list
< HostListEntry
>::const_iterator it
97 = m_aHostList
.begin();
98 const std::list
< HostListEntry
>::const_iterator end
103 if ( (*it
).first
== rKey
)
105 rValue
= (*it
).second
;
113 void put( const OUString
& rKey
, const OUString
& rValue
)
115 if ( m_aHostList
.size() == m_nCapacity
)
116 m_aHostList
.resize( m_nCapacity
/ 2 );
118 m_aHostList
.push_front( HostListEntry( rKey
, rValue
) );
122 //=========================================================================
123 class InternetProxyDecider_Impl
:
124 public cppu::WeakImplHelper1
< util::XChangesListener
>
126 mutable osl::Mutex m_aMutex
;
127 InternetProxyServer m_aHttpProxy
;
128 InternetProxyServer m_aHttpsProxy
;
129 InternetProxyServer m_aFtpProxy
;
130 const InternetProxyServer m_aEmptyProxy
;
131 sal_Int32 m_nProxyType
;
132 uno::Reference
< util::XChangesNotifier
> m_xNotifier
;
133 std::vector
< NoProxyListEntry
> m_aNoProxyList
;
134 mutable HostnameCache m_aHostnames
;
137 bool shouldUseProxy( const OUString
& rHost
,
139 bool bUseFullyQualified
) const;
141 InternetProxyDecider_Impl(
142 const uno::Reference
< uno::XComponentContext
>& rxContext
);
143 virtual ~InternetProxyDecider_Impl();
147 const InternetProxyServer
& getProxy( const OUString
& rProtocol
,
148 const OUString
& rHost
,
149 sal_Int32 nPort
) const;
152 virtual void SAL_CALL
changesOccurred( const util::ChangesEvent
& Event
)
153 throw( uno::RuntimeException
);
155 // XEventListener ( base of XChangesLisetenr )
156 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
)
157 throw( uno::RuntimeException
);
160 void setNoProxyList( const OUString
& rNoProxyList
);
163 //=========================================================================
164 //=========================================================================
166 // WildCard Implementation.
168 //=========================================================================
169 //=========================================================================
171 bool WildCard::Matches( const OUString
& rString
) const
175 rString
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase();
176 const char * pStr
= aString
.getStr();
177 const char * pWild
= m_aWildString
.getStr();
182 while ( *pWild
|| flag
)
192 if ( ( *pWild
== '\\' ) && ( ( *( pWild
+ 1 ) == '?' )
193 || ( *( pWild
+ 1 ) == '*') ) )
195 if ( *pWild
!= *pStr
)
203 // Note: fall-thru's are intended!
206 while ( *pWild
== '*' )
208 if ( *pWild
== '\0' )
213 return ( *pWild
== '\0' );
214 while ( *pStr
&& *pStr
!= *pWild
)
216 if ( *pWild
== '?' ) {
218 while ( *pWild
== '*' )
223 return ( *pWild
== '\0' );
227 if ( *pWild
!= '\0' )
236 return ( *pStr
== '\0' ) && ( *pWild
== '\0' );
239 //=========================================================================
240 bool getConfigStringValue(
241 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
247 if ( !( xNameAccess
->getByName( OUString::createFromAscii( key
) )
250 OSL_FAIL( "InternetProxyDecider - "
251 "Error getting config item value!" );
255 catch ( lang::WrappedTargetException
const & )
259 catch ( container::NoSuchElementException
const & )
266 //=========================================================================
267 bool getConfigInt32Value(
268 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
274 uno::Any aValue
= xNameAccess
->getByName(
275 OUString::createFromAscii( key
) );
276 if ( aValue
.hasValue() && !( aValue
>>= value
) )
278 OSL_FAIL( "InternetProxyDecider - "
279 "Error getting config item value!" );
283 catch ( lang::WrappedTargetException
const & )
287 catch ( container::NoSuchElementException
const & )
294 //=========================================================================
295 //=========================================================================
297 // InternetProxyDecider_Impl Implementation.
299 //=========================================================================
300 //=========================================================================
302 InternetProxyDecider_Impl::InternetProxyDecider_Impl(
303 const uno::Reference
< uno::XComponentContext
>& rxContext
)
305 m_aHostnames( 256 ) // cache size
309 //////////////////////////////////////////////////////////////
310 // Read proxy configuration from config db.
311 //////////////////////////////////////////////////////////////
313 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
314 configuration::theDefaultProvider::get( rxContext
);
316 uno::Sequence
< uno::Any
> aArguments( 1 );
317 aArguments
[ 0 ] <<= OUString( CONFIG_ROOT_KEY
);
319 uno::Reference
< uno::XInterface
> xInterface(
320 xConfigProv
->createInstanceWithArguments(
322 "com.sun.star.configuration.ConfigurationAccess" ),
325 OSL_ENSURE( xInterface
.is(),
326 "InternetProxyDecider - No config access!" );
328 if ( xInterface
.is() )
330 uno::Reference
< container::XNameAccess
> xNameAccess(
331 xInterface
, uno::UNO_QUERY
);
332 OSL_ENSURE( xNameAccess
.is(),
333 "InternetProxyDecider - No name access!" );
335 if ( xNameAccess
.is() )
337 // *** Proxy type ***
339 xNameAccess
, PROXY_TYPE_KEY
, m_nProxyType
);
341 // *** No proxy list ***
342 OUString aNoProxyList
;
343 getConfigStringValue(
344 xNameAccess
, NO_PROXY_LIST_KEY
, aNoProxyList
);
345 setNoProxyList( aNoProxyList
);
348 getConfigStringValue(
349 xNameAccess
, HTTP_PROXY_NAME_KEY
, m_aHttpProxy
.aName
);
351 m_aHttpProxy
.nPort
= -1;
353 xNameAccess
, HTTP_PROXY_PORT_KEY
, m_aHttpProxy
.nPort
);
354 if ( m_aHttpProxy
.nPort
== -1 )
355 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
358 getConfigStringValue(
359 xNameAccess
, HTTPS_PROXY_NAME_KEY
, m_aHttpsProxy
.aName
);
361 m_aHttpsProxy
.nPort
= -1;
363 xNameAccess
, HTTPS_PROXY_PORT_KEY
, m_aHttpsProxy
.nPort
);
364 if ( m_aHttpsProxy
.nPort
== -1 )
365 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
368 getConfigStringValue(
369 xNameAccess
, FTP_PROXY_NAME_KEY
, m_aFtpProxy
.aName
);
371 m_aFtpProxy
.nPort
= -1;
373 xNameAccess
, FTP_PROXY_PORT_KEY
, m_aFtpProxy
.nPort
);
376 // Register as listener for config changes.
378 m_xNotifier
= uno::Reference
< util::XChangesNotifier
>(
379 xInterface
, uno::UNO_QUERY
);
381 OSL_ENSURE( m_xNotifier
.is(),
382 "InternetProxyDecider - No notifier!" );
384 if ( m_xNotifier
.is() )
385 m_xNotifier
->addChangesListener( this );
388 catch ( uno::Exception
const & )
390 // createInstance, createInstanceWithArguments
391 OSL_FAIL( "InternetProxyDecider - Exception!" );
395 //=========================================================================
397 InternetProxyDecider_Impl::~InternetProxyDecider_Impl()
401 //=========================================================================
402 void InternetProxyDecider_Impl::dispose()
404 uno::Reference
< util::XChangesNotifier
> xNotifier
;
406 if ( m_xNotifier
.is() )
408 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
410 if ( m_xNotifier
.is() )
412 xNotifier
= m_xNotifier
;
417 // Do this unguarded!
418 if ( xNotifier
.is() )
419 xNotifier
->removeChangesListener( this );
422 //=========================================================================
423 bool InternetProxyDecider_Impl::shouldUseProxy( const OUString
& rHost
,
425 bool bUseFullyQualified
) const
427 OUStringBuffer aBuffer
;
429 if ( ( rHost
.indexOf( ':' ) != -1 ) &&
430 ( rHost
[ 0 ] != sal_Unicode( '[' ) ) )
432 // host is given as numeric IPv6 address
433 aBuffer
.appendAscii( "[" );
434 aBuffer
.append( rHost
);
435 aBuffer
.appendAscii( "]" );
439 // host is given either as numeric IPv4 address or non-numeric hostname
440 aBuffer
.append( rHost
);
443 aBuffer
.append( sal_Unicode( ':' ) );
444 aBuffer
.append( OUString::valueOf( nPort
) );
445 const OUString
aHostAndPort( aBuffer
.makeStringAndClear() );
447 std::vector
< NoProxyListEntry
>::const_iterator it
448 = m_aNoProxyList
.begin();
449 const std::vector
< NoProxyListEntry
>::const_iterator end
450 = m_aNoProxyList
.end();
454 if ( bUseFullyQualified
)
456 if ( (*it
).second
.Matches( aHostAndPort
) )
461 if ( (*it
).first
.Matches( aHostAndPort
) )
470 //=========================================================================
471 const InternetProxyServer
& InternetProxyDecider_Impl::getProxy(
472 const OUString
& rProtocol
,
473 const OUString
& rHost
,
474 sal_Int32 nPort
) const
476 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
478 if ( m_nProxyType
== 0 )
481 return m_aEmptyProxy
;
484 if ( !rHost
.isEmpty() && !m_aNoProxyList
.empty() )
486 //////////////////////////////////////////////////////////////////
487 // First, try direct hostname match - #110515#
488 //////////////////////////////////////////////////////////////////
490 if ( !shouldUseProxy( rHost
, nPort
, false ) )
491 return m_aEmptyProxy
;
493 //////////////////////////////////////////////////////////////////
494 // Second, try match against full qualified hostname - #104401#
495 //////////////////////////////////////////////////////////////////
499 if ( ( rHost
[ 0 ] == sal_Unicode( '[' ) ) &&
500 ( rHost
.getLength() > 1 ) )
502 // host is given as numeric IPv6 address. name resolution
503 // functions need hostname without square brackets.
504 aHost
= rHost
.copy( 1, rHost
.getLength() - 2 );
511 OUString aFullyQualifiedHost
;
512 if ( !m_aHostnames
.get( aHost
, aFullyQualifiedHost
) )
514 // This might be quite expensive (DNS lookup).
515 const osl::SocketAddr
aAddr( aHost
, nPort
);
516 aFullyQualifiedHost
= aAddr
.getHostname().toAsciiLowerCase();
517 m_aHostnames
.put( aHost
, aFullyQualifiedHost
);
520 // Error resolving name? -> fallback.
521 if ( aFullyQualifiedHost
.isEmpty() )
522 aFullyQualifiedHost
= aHost
;
524 if ( aFullyQualifiedHost
!= aHost
)
526 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, false ) )
527 return m_aEmptyProxy
;
530 //////////////////////////////////////////////////////////////////
531 // Third, try match of fully qualified entries in no-proxy list
532 // against full qualified hostname
535 // list: staroffice-doc -> full: xyz.germany.sun.com
536 // in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com
538 //////////////////////////////////////////////////////////////////
540 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, true ) )
541 return m_aEmptyProxy
;
544 if ( rProtocol
.toAsciiLowerCase() == "ftp" )
546 if ( !m_aFtpProxy
.aName
.isEmpty() && m_aFtpProxy
.nPort
>= 0 )
549 else if ( rProtocol
.toAsciiLowerCase() == "https" )
551 if ( !m_aHttpsProxy
.aName
.isEmpty() )
552 return m_aHttpsProxy
;
554 else if ( !m_aHttpProxy
.aName
.isEmpty() )
556 // All other protocols use the HTTP proxy.
559 return m_aEmptyProxy
;
562 //=========================================================================
564 void SAL_CALL
InternetProxyDecider_Impl::changesOccurred(
565 const util::ChangesEvent
& Event
)
566 throw( uno::RuntimeException
)
568 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
570 sal_Int32 nCount
= Event
.Changes
.getLength();
573 const util::ElementChange
* pElementChanges
574 = Event
.Changes
.getConstArray();
575 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
577 const util::ElementChange
& rElem
= pElementChanges
[ n
];
579 if ( ( rElem
.Accessor
>>= aKey
) && !aKey
.isEmpty() )
581 if ( aKey
== PROXY_TYPE_KEY
)
583 if ( !( rElem
.Element
>>= m_nProxyType
) )
585 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
586 "Error getting config item value!" );
589 else if ( aKey
== NO_PROXY_LIST_KEY
)
591 OUString aNoProxyList
;
592 if ( !( rElem
.Element
>>= aNoProxyList
) )
594 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
595 "Error getting config item value!" );
598 setNoProxyList( aNoProxyList
);
600 else if ( aKey
== HTTP_PROXY_NAME_KEY
)
602 if ( !( rElem
.Element
>>= m_aHttpProxy
.aName
) )
604 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
605 "Error getting config item value!" );
608 else if ( aKey
== HTTP_PROXY_PORT_KEY
)
610 if ( !( rElem
.Element
>>= m_aHttpProxy
.nPort
) )
612 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
613 "Error getting config item value!" );
616 if ( m_aHttpProxy
.nPort
== -1 )
617 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
619 else if ( aKey
== HTTPS_PROXY_NAME_KEY
)
621 if ( !( rElem
.Element
>>= m_aHttpsProxy
.aName
) )
623 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
624 "Error getting config item value!" );
627 else if ( aKey
== HTTPS_PROXY_PORT_KEY
)
629 if ( !( rElem
.Element
>>= m_aHttpsProxy
.nPort
) )
631 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
632 "Error getting config item value!" );
635 if ( m_aHttpsProxy
.nPort
== -1 )
636 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
638 else if ( aKey
== FTP_PROXY_NAME_KEY
)
640 if ( !( rElem
.Element
>>= m_aFtpProxy
.aName
) )
642 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
643 "Error getting config item value!" );
646 else if ( aKey
== FTP_PROXY_PORT_KEY
)
648 if ( !( rElem
.Element
>>= m_aFtpProxy
.nPort
) )
650 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
651 "Error getting config item value!" );
659 //=========================================================================
661 void SAL_CALL
InternetProxyDecider_Impl::disposing(const lang::EventObject
&)
662 throw( uno::RuntimeException
)
664 if ( m_xNotifier
.is() )
666 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
668 if ( m_xNotifier
.is() )
673 //=========================================================================
674 void InternetProxyDecider_Impl::setNoProxyList(
675 const OUString
& rNoProxyList
)
677 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
679 m_aNoProxyList
.clear();
681 if ( !rNoProxyList
.isEmpty() )
683 // List of connection endpoints hostname[:port],
684 // separated by semicolon. Wilcards allowed.
687 sal_Int32 nEnd
= rNoProxyList
.indexOf( ';' );
688 sal_Int32 nLen
= rNoProxyList
.getLength();
695 OUString aToken
= rNoProxyList
.copy( nPos
, nEnd
- nPos
);
697 if ( !aToken
.isEmpty() )
702 // numerical IPv6 address?
703 bool bIPv6Address
= false;
704 sal_Int32 nClosedBracketPos
= aToken
.indexOf( ']' );
705 if ( nClosedBracketPos
== -1 )
706 nClosedBracketPos
= 0;
710 sal_Int32 nColonPos
= aToken
.indexOf( ':', nClosedBracketPos
);
711 if ( nColonPos
== -1 )
713 // No port given, server pattern equals current token
714 aPort
= OUString("*");
715 if ( aToken
.indexOf( '*' ) == -1 )
717 // pattern describes exactly one server
721 aToken
+= OUString(":*");
725 // Port given, extract server pattern
726 sal_Int32 nAsterixPos
= aToken
.indexOf( '*' );
727 aPort
= aToken
.copy( nColonPos
+ 1 );
728 if ( nAsterixPos
< nColonPos
)
730 // pattern describes exactly one server
731 aServer
= aToken
.copy( 0, nColonPos
);
735 OUStringBuffer aFullyQualifiedHost
;
736 if ( !aServer
.isEmpty() )
738 // Remember fully qualified server name if current list
739 // entry specifies exactly one non-fully qualified server
742 // remove square brackets from host name in case it's
743 // a numerical IPv6 address.
745 aServer
= aServer
.copy( 1, aServer
.getLength() - 2 );
747 // This might be quite expensive (DNS lookup).
748 const osl::SocketAddr
aAddr( aServer
, 0 );
749 OUString aTmp
= aAddr
.getHostname().toAsciiLowerCase();
750 if ( aTmp
!= aServer
.toAsciiLowerCase() )
754 aFullyQualifiedHost
.appendAscii( "[" );
755 aFullyQualifiedHost
.append( aTmp
);
756 aFullyQualifiedHost
.appendAscii( "]" );
760 aFullyQualifiedHost
.append( aTmp
);
762 aFullyQualifiedHost
.appendAscii( ":" );
763 aFullyQualifiedHost
.append( aPort
);
767 m_aNoProxyList
.push_back(
768 NoProxyListEntry( WildCard( aToken
),
771 .makeStringAndClear() ) ) );
777 nEnd
= rNoProxyList
.indexOf( ';', nPos
);
780 while ( nEnd
!= nLen
);
784 } // namespace proxydecider_impl
786 //=========================================================================
787 //=========================================================================
789 // InternetProxyDecider Implementation.
791 //=========================================================================
792 //=========================================================================
794 InternetProxyDecider::InternetProxyDecider(
795 const uno::Reference
< uno::XComponentContext
>& rxContext
)
796 : m_pImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxContext
) )
801 //=========================================================================
802 InternetProxyDecider::~InternetProxyDecider()
804 // Break circular reference between config listener and notifier.
811 //=========================================================================
812 bool InternetProxyDecider::shouldUseProxy( const OUString
& rProtocol
,
813 const OUString
& rHost
,
814 sal_Int32 nPort
) const
816 const InternetProxyServer
& rData
= m_pImpl
->getProxy( rProtocol
,
819 return !rData
.aName
.isEmpty();
822 //=========================================================================
823 const InternetProxyServer
& InternetProxyDecider::getProxy(
824 const OUString
& rProtocol
,
825 const OUString
& rHost
,
826 sal_Int32 nPort
) const
828 return m_pImpl
->getProxy( rProtocol
, rHost
, nPort
);
831 } // namespace ucbhelper
833 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */