build fix
[LibreOffice.git] / ucbhelper / source / client / proxydecider.cxx
blobe8a3b76c5aa103919852c8d29d604b19f2ecf8e1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <utility>
21 #include <vector>
22 #include <list>
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"
51 namespace ucbhelper
55 namespace proxydecider_impl
58 // A simple case ignoring wildcard matcher.
59 class WildCard
61 private:
62 OString m_aWildString;
64 public:
65 explicit WildCard( const OUString& rWildCard )
66 : m_aWildString(
67 OUStringToOString(
68 rWildCard, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() ) {}
70 bool Matches( const OUString & rStr ) const;
74 typedef std::pair< WildCard, WildCard > NoProxyListEntry;
77 class HostnameCache
79 typedef std::pair< OUString, OUString > HostListEntry;
81 std::list< HostListEntry > m_aHostList;
82 sal_uInt32 m_nCapacity;
84 public:
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
93 = m_aHostList.end();
95 while ( it != end )
97 if ( (*it).first == rKey )
99 rValue = (*it).second;
100 return true;
102 ++it;
104 return false;
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;
130 private:
131 bool shouldUseProxy( const OUString & rHost,
132 sal_Int32 nPort,
133 bool bUseFullyQualified ) const;
134 public:
135 explicit InternetProxyDecider_Impl(
136 const uno::Reference< uno::XComponentContext >& rxContext );
137 virtual ~InternetProxyDecider_Impl() override;
139 void dispose();
141 const InternetProxyServer & getProxy( const OUString & rProtocol,
142 const OUString & rHost,
143 sal_Int32 nPort ) const;
145 // XChangesListener
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;
153 private:
154 void setNoProxyList( const OUString & rNoProxyList );
158 // WildCard Implementation.
161 bool WildCard::Matches( const OUString& rString ) const
163 OString aString
164 = OUStringToOString(
165 rString, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
166 const char * pStr = aString.getStr();
167 const char * pWild = m_aWildString.getStr();
169 int pos = 0;
170 int flag = 0;
172 while ( *pWild || flag )
174 switch ( *pWild )
176 case '?':
177 if ( *pStr == '\0' )
178 return false;
179 break;
181 default:
182 if ( ( *pWild == '\\' ) && ( ( *( pWild + 1 ) == '?' )
183 || ( *( pWild + 1 ) == '*') ) )
184 pWild++;
185 if ( *pWild != *pStr )
186 if ( !pos )
187 return false;
188 else
189 pWild += pos;
190 else
191 break;
193 SAL_FALLTHROUGH;
195 case '*':
196 while ( *pWild == '*' )
197 pWild++;
198 if ( *pWild == '\0' )
199 return true;
200 flag = 1;
201 pos = 0;
202 if ( *pStr == '\0' )
203 return ( *pWild == '\0' );
204 while ( *pStr && *pStr != *pWild )
206 if ( *pWild == '?' ) {
207 pWild++;
208 while ( *pWild == '*' )
209 pWild++;
211 pStr++;
212 if ( *pStr == '\0' )
213 return ( *pWild == '\0' );
215 break;
217 if ( *pWild != '\0' )
218 pWild++;
219 if ( *pStr != '\0' )
220 pStr++;
221 else
222 flag = 0;
223 if ( flag )
224 pos--;
226 return ( *pStr == '\0' ) && ( *pWild == '\0' );
230 bool getConfigStringValue(
231 const uno::Reference< container::XNameAccess > & xNameAccess,
232 const char * key,
233 OUString & value )
237 if ( !( xNameAccess->getByName( OUString::createFromAscii( key ) )
238 >>= value ) )
240 OSL_FAIL( "InternetProxyDecider - "
241 "Error getting config item value!" );
242 return false;
245 catch ( lang::WrappedTargetException const & )
247 return false;
249 catch ( container::NoSuchElementException const & )
251 return false;
253 return true;
257 bool getConfigInt32Value(
258 const uno::Reference< container::XNameAccess > & xNameAccess,
259 const char * key,
260 sal_Int32 & value )
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!" );
270 return false;
273 catch ( lang::WrappedTargetException const & )
275 return false;
277 catch ( container::NoSuchElementException const & )
279 return false;
281 return true;
285 // InternetProxyDecider_Impl Implementation.
288 InternetProxyDecider_Impl::InternetProxyDecider_Impl(
289 const uno::Reference< uno::XComponentContext >& rxContext )
290 : m_nProxyType( 0 ),
291 m_aHostnames()
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",
308 aArguments ) );
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 ***
323 getConfigInt32Value(
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 );
332 // *** HTTP ***
333 getConfigStringValue(
334 xNameAccess, HTTP_PROXY_NAME_KEY, m_aHttpProxy.aName );
336 m_aHttpProxy.nPort = -1;
337 getConfigInt32Value(
338 xNameAccess, HTTP_PROXY_PORT_KEY, m_aHttpProxy.nPort );
339 if ( m_aHttpProxy.nPort == -1 )
340 m_aHttpProxy.nPort = 80; // standard HTTP port.
342 // *** HTTPS ***
343 getConfigStringValue(
344 xNameAccess, HTTPS_PROXY_NAME_KEY, m_aHttpsProxy.aName );
346 m_aHttpsProxy.nPort = -1;
347 getConfigInt32Value(
348 xNameAccess, HTTPS_PROXY_PORT_KEY, m_aHttpsProxy.nPort );
349 if ( m_aHttpsProxy.nPort == -1 )
350 m_aHttpsProxy.nPort = 443; // standard HTTPS port.
352 // *** FTP ***
353 getConfigStringValue(
354 xNameAccess, FTP_PROXY_NAME_KEY, m_aFtpProxy.aName );
356 m_aFtpProxy.nPort = -1;
357 getConfigInt32Value(
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!" );
380 // virtual
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;
397 m_xNotifier.clear();
401 // Do this unguarded!
402 if ( xNotifier.is() )
403 xNotifier->removeChangesListener( this );
407 bool InternetProxyDecider_Impl::shouldUseProxy( const OUString & rHost,
408 sal_Int32 nPort,
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( "]" );
421 else
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();
436 while ( it != end )
438 if ( bUseFullyQualified )
440 if ( (*it).second.Matches( aHostAndPort ) )
441 return false;
443 else
445 if ( (*it).first.Matches( aHostAndPort ) )
446 return false;
448 ++it;
451 return true;
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 )
464 // Never use proxy.
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#
481 OUString aHost;
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 );
490 else
492 aHost = rHost;
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
518 // Example:
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 )
530 return m_aFtpProxy;
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.
540 return m_aHttpProxy;
542 return m_aEmptyProxy;
546 // virtual
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();
554 if ( nCount )
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 ];
561 OUString aKey;
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!" );
643 // virtual
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() )
652 m_xNotifier.clear();
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.
669 sal_Int32 nPos = 0;
670 sal_Int32 nEnd = rNoProxyList.indexOf( ';' );
671 sal_Int32 nLen = rNoProxyList.getLength();
675 if ( nEnd == -1 )
676 nEnd = nLen;
678 OUString aToken = rNoProxyList.copy( nPos, nEnd - nPos );
680 if ( !aToken.isEmpty() )
682 OUString aServer;
683 OUString aPort;
685 // numerical IPv6 address?
686 bool bIPv6Address = false;
687 sal_Int32 nClosedBracketPos = aToken.indexOf( ']' );
688 if ( nClosedBracketPos == -1 )
689 nClosedBracketPos = 0;
690 else
691 bIPv6Address = true;
693 sal_Int32 nColonPos = aToken.indexOf( ':', nClosedBracketPos );
694 if ( nColonPos == -1 )
696 // No port given, server pattern equals current token
697 aPort = "*";
698 if ( aToken.indexOf( '*' ) == -1 )
700 // pattern describes exactly one server
701 aServer = aToken;
704 aToken += ":*";
706 else
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
723 // name.
725 // remove square brackets from host name in case it's
726 // a numerical IPv6 address.
727 if ( bIPv6Address )
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() )
735 if ( bIPv6Address )
737 aFullyQualifiedHost.append( "[" );
738 aFullyQualifiedHost.append( aTmp );
739 aFullyQualifiedHost.append( "]" );
741 else
743 aFullyQualifiedHost.append( aTmp );
745 aFullyQualifiedHost.append( ":" );
746 aFullyQualifiedHost.append( aPort );
750 m_aNoProxyList.push_back(
751 NoProxyListEntry( WildCard( aToken ),
752 WildCard(
753 aFullyQualifiedHost
754 .makeStringAndClear() ) ) );
757 if ( nEnd != nLen )
759 nPos = nEnd + 1;
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.
783 m_xImpl->dispose();
787 bool InternetProxyDecider::shouldUseProxy( const OUString & rProtocol,
788 const OUString & rHost,
789 sal_Int32 nPort ) const
791 const InternetProxyServer & rData = m_xImpl->getProxy( rProtocol,
792 rHost,
793 nPort );
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: */