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/implbase1.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 WildCard( const OUString
& rWildCard
)
68 rWildCard
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase() ) {}
70 bool Matches( const OUString
& rStr
) const;
74 typedef std::pair
< WildCard
, WildCard
> NoProxyListEntry
;
80 typedef std::pair
< OUString
, OUString
> HostListEntry
;
82 std::list
< HostListEntry
> m_aHostList
;
83 sal_uInt32 m_nCapacity
;
86 explicit HostnameCache( sal_uInt32 nCapacity
)
87 : m_nCapacity( nCapacity
) {}
89 bool get( const OUString
& rKey
, OUString
& rValue
) const
91 std::list
< HostListEntry
>::const_iterator it
92 = m_aHostList
.begin();
93 const std::list
< HostListEntry
>::const_iterator end
98 if ( (*it
).first
== rKey
)
100 rValue
= (*it
).second
;
108 void put( const OUString
& rKey
, const OUString
& rValue
)
110 if ( m_aHostList
.size() == m_nCapacity
)
111 m_aHostList
.resize( m_nCapacity
/ 2 );
113 m_aHostList
.push_front( HostListEntry( rKey
, rValue
) );
118 class InternetProxyDecider_Impl
:
119 public cppu::WeakImplHelper1
< util::XChangesListener
>
121 mutable osl::Mutex m_aMutex
;
122 InternetProxyServer m_aHttpProxy
;
123 InternetProxyServer m_aHttpsProxy
;
124 InternetProxyServer m_aFtpProxy
;
125 const InternetProxyServer m_aEmptyProxy
;
126 sal_Int32 m_nProxyType
;
127 uno::Reference
< util::XChangesNotifier
> m_xNotifier
;
128 std::vector
< NoProxyListEntry
> m_aNoProxyList
;
129 mutable HostnameCache m_aHostnames
;
132 bool shouldUseProxy( const OUString
& rHost
,
134 bool bUseFullyQualified
) const;
136 InternetProxyDecider_Impl(
137 const uno::Reference
< uno::XComponentContext
>& rxContext
);
138 virtual ~InternetProxyDecider_Impl();
142 const InternetProxyServer
& getProxy( const OUString
& rProtocol
,
143 const OUString
& rHost
,
144 sal_Int32 nPort
) const;
147 virtual void SAL_CALL
changesOccurred( const util::ChangesEvent
& Event
)
148 throw( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
150 // XEventListener ( base of XChangesLisetenr )
151 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
)
152 throw( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
155 void setNoProxyList( const OUString
& rNoProxyList
);
161 // WildCard Implementation.
166 bool WildCard::Matches( const OUString
& rString
) const
170 rString
, RTL_TEXTENCODING_UTF8
).toAsciiLowerCase();
171 const char * pStr
= aString
.getStr();
172 const char * pWild
= m_aWildString
.getStr();
177 while ( *pWild
|| flag
)
187 if ( ( *pWild
== '\\' ) && ( ( *( pWild
+ 1 ) == '?' )
188 || ( *( pWild
+ 1 ) == '*') ) )
190 if ( *pWild
!= *pStr
)
198 // Note: fall-through are intended!
201 while ( *pWild
== '*' )
203 if ( *pWild
== '\0' )
208 return ( *pWild
== '\0' );
209 while ( *pStr
&& *pStr
!= *pWild
)
211 if ( *pWild
== '?' ) {
213 while ( *pWild
== '*' )
218 return ( *pWild
== '\0' );
222 if ( *pWild
!= '\0' )
231 return ( *pStr
== '\0' ) && ( *pWild
== '\0' );
235 bool getConfigStringValue(
236 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
242 if ( !( xNameAccess
->getByName( OUString::createFromAscii( key
) )
245 OSL_FAIL( "InternetProxyDecider - "
246 "Error getting config item value!" );
250 catch ( lang::WrappedTargetException
const & )
254 catch ( container::NoSuchElementException
const & )
262 bool getConfigInt32Value(
263 const uno::Reference
< container::XNameAccess
> & xNameAccess
,
269 uno::Any aValue
= xNameAccess
->getByName(
270 OUString::createFromAscii( key
) );
271 if ( aValue
.hasValue() && !( aValue
>>= value
) )
273 OSL_FAIL( "InternetProxyDecider - "
274 "Error getting config item value!" );
278 catch ( lang::WrappedTargetException
const & )
282 catch ( container::NoSuchElementException
const & )
292 // InternetProxyDecider_Impl Implementation.
297 InternetProxyDecider_Impl::InternetProxyDecider_Impl(
298 const uno::Reference
< uno::XComponentContext
>& rxContext
)
300 m_aHostnames( 256 ) // cache size
305 // Read proxy configuration from config db.
308 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
309 configuration::theDefaultProvider::get( rxContext
);
311 uno::Sequence
< uno::Any
> aArguments( 1 );
312 aArguments
[ 0 ] <<= OUString( CONFIG_ROOT_KEY
);
314 uno::Reference
< uno::XInterface
> xInterface(
315 xConfigProv
->createInstanceWithArguments(
317 "com.sun.star.configuration.ConfigurationAccess" ),
320 OSL_ENSURE( xInterface
.is(),
321 "InternetProxyDecider - No config access!" );
323 if ( xInterface
.is() )
325 uno::Reference
< container::XNameAccess
> xNameAccess(
326 xInterface
, uno::UNO_QUERY
);
327 OSL_ENSURE( xNameAccess
.is(),
328 "InternetProxyDecider - No name access!" );
330 if ( xNameAccess
.is() )
332 // *** Proxy type ***
334 xNameAccess
, PROXY_TYPE_KEY
, m_nProxyType
);
336 // *** No proxy list ***
337 OUString aNoProxyList
;
338 getConfigStringValue(
339 xNameAccess
, NO_PROXY_LIST_KEY
, aNoProxyList
);
340 setNoProxyList( aNoProxyList
);
343 getConfigStringValue(
344 xNameAccess
, HTTP_PROXY_NAME_KEY
, m_aHttpProxy
.aName
);
346 m_aHttpProxy
.nPort
= -1;
348 xNameAccess
, HTTP_PROXY_PORT_KEY
, m_aHttpProxy
.nPort
);
349 if ( m_aHttpProxy
.nPort
== -1 )
350 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
353 getConfigStringValue(
354 xNameAccess
, HTTPS_PROXY_NAME_KEY
, m_aHttpsProxy
.aName
);
356 m_aHttpsProxy
.nPort
= -1;
358 xNameAccess
, HTTPS_PROXY_PORT_KEY
, m_aHttpsProxy
.nPort
);
359 if ( m_aHttpsProxy
.nPort
== -1 )
360 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
363 getConfigStringValue(
364 xNameAccess
, FTP_PROXY_NAME_KEY
, m_aFtpProxy
.aName
);
366 m_aFtpProxy
.nPort
= -1;
368 xNameAccess
, FTP_PROXY_PORT_KEY
, m_aFtpProxy
.nPort
);
371 // Register as listener for config changes.
373 m_xNotifier
= uno::Reference
< util::XChangesNotifier
>(
374 xInterface
, uno::UNO_QUERY
);
376 OSL_ENSURE( m_xNotifier
.is(),
377 "InternetProxyDecider - No notifier!" );
379 if ( m_xNotifier
.is() )
380 m_xNotifier
->addChangesListener( this );
383 catch ( uno::Exception
const & )
385 // createInstance, createInstanceWithArguments
386 OSL_FAIL( "InternetProxyDecider - Exception!" );
392 InternetProxyDecider_Impl::~InternetProxyDecider_Impl()
397 void InternetProxyDecider_Impl::dispose()
399 uno::Reference
< util::XChangesNotifier
> xNotifier
;
401 if ( m_xNotifier
.is() )
403 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
405 if ( m_xNotifier
.is() )
407 xNotifier
= m_xNotifier
;
412 // Do this unguarded!
413 if ( xNotifier
.is() )
414 xNotifier
->removeChangesListener( this );
418 bool InternetProxyDecider_Impl::shouldUseProxy( const OUString
& rHost
,
420 bool bUseFullyQualified
) const
422 OUStringBuffer aBuffer
;
424 if ( ( rHost
.indexOf( ':' ) != -1 ) &&
425 ( rHost
[ 0 ] != '[' ) )
427 // host is given as numeric IPv6 address
428 aBuffer
.appendAscii( "[" );
429 aBuffer
.append( rHost
);
430 aBuffer
.appendAscii( "]" );
434 // host is given either as numeric IPv4 address or non-numeric hostname
435 aBuffer
.append( rHost
);
438 aBuffer
.append( ':' );
439 aBuffer
.append( OUString::number( nPort
) );
440 const OUString
aHostAndPort( aBuffer
.makeStringAndClear() );
442 std::vector
< NoProxyListEntry
>::const_iterator it
443 = m_aNoProxyList
.begin();
444 const std::vector
< NoProxyListEntry
>::const_iterator end
445 = m_aNoProxyList
.end();
449 if ( bUseFullyQualified
)
451 if ( (*it
).second
.Matches( aHostAndPort
) )
456 if ( (*it
).first
.Matches( aHostAndPort
) )
466 const InternetProxyServer
& InternetProxyDecider_Impl::getProxy(
467 const OUString
& rProtocol
,
468 const OUString
& rHost
,
469 sal_Int32 nPort
) const
471 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
473 if ( m_nProxyType
== 0 )
476 return m_aEmptyProxy
;
479 if ( !rHost
.isEmpty() && !m_aNoProxyList
.empty() )
482 // First, try direct hostname match - #110515#
485 if ( !shouldUseProxy( rHost
, nPort
, false ) )
486 return m_aEmptyProxy
;
489 // Second, try match against full qualified hostname - #104401#
494 if ( ( rHost
.getLength() > 1 ) &&
495 ( rHost
[ 0 ] == '[' ))
497 // host is given as numeric IPv6 address. name resolution
498 // functions need hostname without square brackets.
499 aHost
= rHost
.copy( 1, rHost
.getLength() - 2 );
506 OUString aFullyQualifiedHost
;
507 if ( !m_aHostnames
.get( aHost
, aFullyQualifiedHost
) )
509 // This might be quite expensive (DNS lookup).
510 const osl::SocketAddr
aAddr( aHost
, nPort
);
511 aFullyQualifiedHost
= aAddr
.getHostname().toAsciiLowerCase();
512 m_aHostnames
.put( aHost
, aFullyQualifiedHost
);
515 // Error resolving name? -> fallback.
516 if ( aFullyQualifiedHost
.isEmpty() )
517 aFullyQualifiedHost
= aHost
;
519 if ( aFullyQualifiedHost
!= aHost
)
521 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, false ) )
522 return m_aEmptyProxy
;
526 // Third, try match of fully qualified entries in no-proxy list
527 // against full qualified hostname
530 // list: staroffice-doc -> full: xyz.germany.sun.com
531 // in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com
535 if ( !shouldUseProxy( aFullyQualifiedHost
, nPort
, true ) )
536 return m_aEmptyProxy
;
539 if ( rProtocol
.toAsciiLowerCase() == "ftp" )
541 if ( !m_aFtpProxy
.aName
.isEmpty() && m_aFtpProxy
.nPort
>= 0 )
544 else if ( rProtocol
.toAsciiLowerCase() == "https" )
546 if ( !m_aHttpsProxy
.aName
.isEmpty() )
547 return m_aHttpsProxy
;
549 else if ( !m_aHttpProxy
.aName
.isEmpty() )
551 // All other protocols use the HTTP proxy.
554 return m_aEmptyProxy
;
559 void SAL_CALL
InternetProxyDecider_Impl::changesOccurred(
560 const util::ChangesEvent
& Event
)
561 throw( uno::RuntimeException
, std::exception
)
563 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
565 sal_Int32 nCount
= Event
.Changes
.getLength();
568 const util::ElementChange
* pElementChanges
569 = Event
.Changes
.getConstArray();
570 for ( sal_Int32 n
= 0; n
< nCount
; ++n
)
572 const util::ElementChange
& rElem
= pElementChanges
[ n
];
574 if ( ( rElem
.Accessor
>>= aKey
) && !aKey
.isEmpty() )
576 if ( aKey
== PROXY_TYPE_KEY
)
578 if ( !( rElem
.Element
>>= m_nProxyType
) )
580 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
581 "Error getting config item value!" );
584 else if ( aKey
== NO_PROXY_LIST_KEY
)
586 OUString aNoProxyList
;
587 if ( !( rElem
.Element
>>= aNoProxyList
) )
589 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
590 "Error getting config item value!" );
593 setNoProxyList( aNoProxyList
);
595 else if ( aKey
== HTTP_PROXY_NAME_KEY
)
597 if ( !( rElem
.Element
>>= m_aHttpProxy
.aName
) )
599 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
600 "Error getting config item value!" );
603 else if ( aKey
== HTTP_PROXY_PORT_KEY
)
605 if ( !( rElem
.Element
>>= m_aHttpProxy
.nPort
) )
607 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
608 "Error getting config item value!" );
611 if ( m_aHttpProxy
.nPort
== -1 )
612 m_aHttpProxy
.nPort
= 80; // standard HTTP port.
614 else if ( aKey
== HTTPS_PROXY_NAME_KEY
)
616 if ( !( rElem
.Element
>>= m_aHttpsProxy
.aName
) )
618 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
619 "Error getting config item value!" );
622 else if ( aKey
== HTTPS_PROXY_PORT_KEY
)
624 if ( !( rElem
.Element
>>= m_aHttpsProxy
.nPort
) )
626 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
627 "Error getting config item value!" );
630 if ( m_aHttpsProxy
.nPort
== -1 )
631 m_aHttpsProxy
.nPort
= 443; // standard HTTPS port.
633 else if ( aKey
== FTP_PROXY_NAME_KEY
)
635 if ( !( rElem
.Element
>>= m_aFtpProxy
.aName
) )
637 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
638 "Error getting config item value!" );
641 else if ( aKey
== FTP_PROXY_PORT_KEY
)
643 if ( !( rElem
.Element
>>= m_aFtpProxy
.nPort
) )
645 OSL_FAIL( "InternetProxyDecider - changesOccurred - "
646 "Error getting config item value!" );
656 void SAL_CALL
InternetProxyDecider_Impl::disposing(const lang::EventObject
&)
657 throw( uno::RuntimeException
, std::exception
)
659 if ( m_xNotifier
.is() )
661 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
663 if ( m_xNotifier
.is() )
669 void InternetProxyDecider_Impl::setNoProxyList(
670 const OUString
& rNoProxyList
)
672 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
674 m_aNoProxyList
.clear();
676 if ( !rNoProxyList
.isEmpty() )
678 // List of connection endpoints hostname[:port],
679 // separated by semicolon. Wilcards allowed.
682 sal_Int32 nEnd
= rNoProxyList
.indexOf( ';' );
683 sal_Int32 nLen
= rNoProxyList
.getLength();
690 OUString aToken
= rNoProxyList
.copy( nPos
, nEnd
- nPos
);
692 if ( !aToken
.isEmpty() )
697 // numerical IPv6 address?
698 bool bIPv6Address
= false;
699 sal_Int32 nClosedBracketPos
= aToken
.indexOf( ']' );
700 if ( nClosedBracketPos
== -1 )
701 nClosedBracketPos
= 0;
705 sal_Int32 nColonPos
= aToken
.indexOf( ':', nClosedBracketPos
);
706 if ( nColonPos
== -1 )
708 // No port given, server pattern equals current token
710 if ( aToken
.indexOf( '*' ) == -1 )
712 // pattern describes exactly one server
720 // Port given, extract server pattern
721 sal_Int32 nAsteriskPos
= aToken
.indexOf( '*' );
722 aPort
= aToken
.copy( nColonPos
+ 1 );
723 if ( nAsteriskPos
< nColonPos
)
725 // pattern describes exactly one server
726 aServer
= aToken
.copy( 0, nColonPos
);
730 OUStringBuffer aFullyQualifiedHost
;
731 if ( !aServer
.isEmpty() )
733 // Remember fully qualified server name if current list
734 // entry specifies exactly one non-fully qualified server
737 // remove square brackets from host name in case it's
738 // a numerical IPv6 address.
740 aServer
= aServer
.copy( 1, aServer
.getLength() - 2 );
742 // This might be quite expensive (DNS lookup).
743 const osl::SocketAddr
aAddr( aServer
, 0 );
744 OUString aTmp
= aAddr
.getHostname().toAsciiLowerCase();
745 if ( aTmp
!= aServer
.toAsciiLowerCase() )
749 aFullyQualifiedHost
.appendAscii( "[" );
750 aFullyQualifiedHost
.append( aTmp
);
751 aFullyQualifiedHost
.appendAscii( "]" );
755 aFullyQualifiedHost
.append( aTmp
);
757 aFullyQualifiedHost
.appendAscii( ":" );
758 aFullyQualifiedHost
.append( aPort
);
762 m_aNoProxyList
.push_back(
763 NoProxyListEntry( WildCard( aToken
),
766 .makeStringAndClear() ) ) );
772 nEnd
= rNoProxyList
.indexOf( ';', nPos
);
775 while ( nEnd
!= nLen
);
779 } // namespace proxydecider_impl
784 // InternetProxyDecider Implementation.
789 InternetProxyDecider::InternetProxyDecider(
790 const uno::Reference
< uno::XComponentContext
>& rxContext
)
791 : m_pImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxContext
) )
797 InternetProxyDecider::~InternetProxyDecider()
799 // Break circular reference between config listener and notifier.
807 bool InternetProxyDecider::shouldUseProxy( const OUString
& rProtocol
,
808 const OUString
& rHost
,
809 sal_Int32 nPort
) const
811 const InternetProxyServer
& rData
= m_pImpl
->getProxy( rProtocol
,
814 return !rData
.aName
.isEmpty();
818 const InternetProxyServer
& InternetProxyDecider::getProxy(
819 const OUString
& rProtocol
,
820 const OUString
& rHost
,
821 sal_Int32 nPort
) const
823 return m_pImpl
->getProxy( rProtocol
, rHost
, nPort
);
826 } // namespace ucbhelper
828 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */