LibreOffice Module ucbhelper (master)  1
proxydecider.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 #include <utility>
24 #include <vector>
25 #include <deque>
26 
27 #include <osl/diagnose.h>
28 #include <osl/mutex.hxx>
29 #include <rtl/ref.hxx>
30 #include <osl/socket.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <com/sun/star/container/XNameAccess.hpp>
33 #include <com/sun/star/configuration/theDefaultProvider.hpp>
34 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
35 #include <com/sun/star/util/XChangesListener.hpp>
36 #include <com/sun/star/util/XChangesNotifier.hpp>
37 #include <cppuhelper/implbase.hxx>
39 
40 #ifdef _WIN32
42 #define WIN32_LEAN_AND_MEAN
43 #include <Windows.h>
44 #include <Winhttp.h>
45 #endif
46 
47 using namespace com::sun::star;
48 using namespace ucbhelper;
49 
50 #define CONFIG_ROOT_KEY "org.openoffice.Inet/Settings"
51 #define PROXY_TYPE_KEY "ooInetProxyType"
52 #define NO_PROXY_LIST_KEY "ooInetNoProxy"
53 #define HTTP_PROXY_NAME_KEY "ooInetHTTPProxyName"
54 #define HTTP_PROXY_PORT_KEY "ooInetHTTPProxyPort"
55 #define HTTPS_PROXY_NAME_KEY "ooInetHTTPSProxyName"
56 #define HTTPS_PROXY_PORT_KEY "ooInetHTTPSProxyPort"
57 #define FTP_PROXY_NAME_KEY "ooInetFTPProxyName"
58 #define FTP_PROXY_PORT_KEY "ooInetFTPProxyPort"
59 
60 
61 namespace ucbhelper
62 {
63 
64 
65 namespace proxydecider_impl
66 {
67 
68 namespace {
69 
70 // A simple case ignoring wildcard matcher.
71 class WildCard
72 {
73 private:
74  OString m_aWildString;
75 
76 public:
77  explicit WildCard( std::u16string_view rWildCard )
78  : m_aWildString(
80  rWildCard, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase() ) {}
81 
82  bool Matches( std::u16string_view rStr ) const;
83 };
84 
85 }
86 
87 typedef std::pair< WildCard, WildCard > NoProxyListEntry;
88 
89 namespace {
90 
91 class HostnameCache
92 {
93  typedef std::pair< OUString, OUString > HostListEntry;
94 
95  std::deque< HostListEntry > m_aHostList;
96 
97 public:
98  bool get( std::u16string_view rKey, OUString & rValue ) const
99  {
100  for (auto const& host : m_aHostList)
101  {
102  if ( host.first == rKey )
103  {
104  rValue = host.second;
105  return true;
106  }
107  }
108  return false;
109  }
110 
111  void put( const OUString & rKey, const OUString & rValue )
112  {
113  static constexpr sal_uInt32 nCapacity = 256;
114 
115  if ( m_aHostList.size() == nCapacity )
116  m_aHostList.resize( nCapacity / 2 );
117 
118  m_aHostList.push_front( HostListEntry( rKey, rValue ) );
119  }
120 };
121 
122 }
123 
125  public cppu::WeakImplHelper< util::XChangesListener >
126 {
127  // see officecfg/registry/schema/org/openoffice/Inet.xcs for the definition of these values
128  enum class ProxyType { NoProxy, Automatic, Manual };
129  mutable osl::Mutex m_aMutex;
135  uno::Reference< util::XChangesNotifier > m_xNotifier;
136  std::vector< NoProxyListEntry > m_aNoProxyList;
137  mutable HostnameCache m_aHostnames;
138 
139 private:
140  bool shouldUseProxy( const OUString & rHost,
141  sal_Int32 nPort,
142  bool bUseFullyQualified ) const;
143 public:
144  explicit InternetProxyDecider_Impl(
145  const uno::Reference< uno::XComponentContext >& rxContext );
146 
147  void dispose();
148 
149  InternetProxyServer getProxy(const OUString& rProtocol,
150  const OUString & rHost,
151  sal_Int32 nPort ) const;
152 
153  // XChangesListener
154  virtual void SAL_CALL changesOccurred( const util::ChangesEvent& Event ) override;
155 
156  // XEventListener ( base of XChangesLisetenr )
157  virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
158 
159 private:
160  void setNoProxyList( const OUString & rNoProxyList );
161 };
162 
163 
164 // WildCard Implementation.
165 
166 
167 bool WildCard::Matches( std::u16string_view rString ) const
168 {
169  OString aString
170  = OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
171  const char * pStr = aString.getStr();
172  const char * pWild = m_aWildString.getStr();
173 
174  int pos = 0;
175  int flag = 0;
176 
177  while ( *pWild || flag )
178  {
179  switch ( *pWild )
180  {
181  case '?':
182  if ( *pStr == '\0' )
183  return false;
184  break;
185 
186  default:
187  if ( ( *pWild == '\\' ) && ( ( *( pWild + 1 ) == '?' )
188  || ( *( pWild + 1 ) == '*') ) )
189  pWild++;
190  if ( *pWild != *pStr )
191  if ( !pos )
192  return false;
193  else
194  pWild += pos;
195  else
196  break;
197 
198  [[fallthrough]];
199 
200  case '*':
201  while ( *pWild == '*' )
202  pWild++;
203  if ( *pWild == '\0' )
204  return true;
205  flag = 1;
206  pos = 0;
207  if ( *pStr == '\0' )
208  return ( *pWild == '\0' );
209  while ( *pStr && *pStr != *pWild )
210  {
211  if ( *pWild == '?' ) {
212  pWild++;
213  while ( *pWild == '*' )
214  pWild++;
215  }
216  pStr++;
217  if ( *pStr == '\0' )
218  return ( *pWild == '\0' );
219  }
220  break;
221  }
222  if ( *pWild != '\0' )
223  pWild++;
224  if ( *pStr != '\0' )
225  pStr++;
226  else
227  flag = 0;
228  if ( flag )
229  pos--;
230  }
231  return ( *pStr == '\0' ) && ( *pWild == '\0' );
232 }
233 
234 
236  const uno::Reference< container::XNameAccess > & xNameAccess,
237  const char * key,
238  OUString & value )
239 {
240  try
241  {
242  if ( !( xNameAccess->getByName( OUString::createFromAscii( key ) )
243  >>= value ) )
244  {
245  OSL_FAIL( "InternetProxyDecider - "
246  "Error getting config item value!" );
247  return false;
248  }
249  }
250  catch ( lang::WrappedTargetException const & )
251  {
252  return false;
253  }
254  catch ( container::NoSuchElementException const & )
255  {
256  return false;
257  }
258  return true;
259 }
260 
261 
263  const uno::Reference< container::XNameAccess > & xNameAccess,
264  const char * key,
265  sal_Int32 & value )
266 {
267  try
268  {
269  uno::Any aValue = xNameAccess->getByName(
270  OUString::createFromAscii( key ) );
271  if ( aValue.hasValue() && !( aValue >>= value ) )
272  {
273  OSL_FAIL( "InternetProxyDecider - "
274  "Error getting config item value!" );
275  return false;
276  }
277  }
278  catch ( lang::WrappedTargetException const & )
279  {
280  return false;
281  }
282  catch ( container::NoSuchElementException const & )
283  {
284  return false;
285  }
286  return true;
287 }
288 
289 
290 // InternetProxyDecider_Impl Implementation.
291 
292 
293 InternetProxyDecider_Impl::InternetProxyDecider_Impl(
294  const uno::Reference< uno::XComponentContext >& rxContext )
295  : m_nProxyType( ProxyType::NoProxy ),
296  m_aHostnames()
297 {
298  try
299  {
300 
301  // Read proxy configuration from config db.
302 
303 
304  uno::Reference< lang::XMultiServiceFactory > xConfigProv =
305  configuration::theDefaultProvider::get( rxContext );
306 
307  uno::Sequence< uno::Any > aArguments( 1 );
308  aArguments[ 0 ] <<= OUString( CONFIG_ROOT_KEY );
309 
310  uno::Reference< uno::XInterface > xInterface(
311  xConfigProv->createInstanceWithArguments(
312  "com.sun.star.configuration.ConfigurationAccess",
313  aArguments ) );
314 
315  OSL_ENSURE( xInterface.is(),
316  "InternetProxyDecider - No config access!" );
317 
318  if ( xInterface.is() )
319  {
320  uno::Reference< container::XNameAccess > xNameAccess(
321  xInterface, uno::UNO_QUERY );
322  OSL_ENSURE( xNameAccess.is(),
323  "InternetProxyDecider - No name access!" );
324 
325  if ( xNameAccess.is() )
326  {
327  // *** Proxy type ***
328  sal_Int32 tmp = 0;
330  xNameAccess, PROXY_TYPE_KEY, tmp );
331  m_nProxyType = static_cast<ProxyType>(tmp);
332 
333  // *** No proxy list ***
334  OUString aNoProxyList;
336  xNameAccess, NO_PROXY_LIST_KEY, aNoProxyList );
337  setNoProxyList( aNoProxyList );
338 
339  // *** HTTP ***
341  xNameAccess, HTTP_PROXY_NAME_KEY, m_aHttpProxy.aName );
342 
343  m_aHttpProxy.nPort = -1;
345  xNameAccess, HTTP_PROXY_PORT_KEY, m_aHttpProxy.nPort );
346  if ( m_aHttpProxy.nPort == -1 )
347  m_aHttpProxy.nPort = 80; // standard HTTP port.
348 
349  // *** HTTPS ***
351  xNameAccess, HTTPS_PROXY_NAME_KEY, m_aHttpsProxy.aName );
352 
353  m_aHttpsProxy.nPort = -1;
355  xNameAccess, HTTPS_PROXY_PORT_KEY, m_aHttpsProxy.nPort );
356  if ( m_aHttpsProxy.nPort == -1 )
357  m_aHttpsProxy.nPort = 443; // standard HTTPS port.
358 
359  // *** FTP ***
361  xNameAccess, FTP_PROXY_NAME_KEY, m_aFtpProxy.aName );
362 
363  m_aFtpProxy.nPort = -1;
365  xNameAccess, FTP_PROXY_PORT_KEY, m_aFtpProxy.nPort );
366  }
367 
368  // Register as listener for config changes.
369 
370  m_xNotifier.set( xInterface, uno::UNO_QUERY );
371 
372  OSL_ENSURE( m_xNotifier.is(),
373  "InternetProxyDecider - No notifier!" );
374 
375  if ( m_xNotifier.is() )
376  m_xNotifier->addChangesListener( this );
377  }
378  }
379  catch ( uno::Exception const & )
380  {
381  // createInstance, createInstanceWithArguments
382  OSL_FAIL( "InternetProxyDecider - Exception!" );
383  }
384 }
385 
387 {
388  uno::Reference< util::XChangesNotifier > xNotifier;
389 
390  if ( m_xNotifier.is() )
391  {
392  osl::Guard< osl::Mutex > aGuard( m_aMutex );
393 
394  if ( m_xNotifier.is() )
395  {
396  xNotifier = m_xNotifier;
397  m_xNotifier.clear();
398  }
399  }
400 
401  // Do this unguarded!
402  if ( xNotifier.is() )
403  xNotifier->removeChangesListener( this );
404 }
405 
406 
407 bool InternetProxyDecider_Impl::shouldUseProxy( const OUString & rHost,
408  sal_Int32 nPort,
409  bool bUseFullyQualified ) const
410 {
411  OUStringBuffer aBuffer;
412 
413  if ( ( rHost.indexOf( ':' ) != -1 ) &&
414  ( rHost[ 0 ] != '[' ) )
415  {
416  // host is given as numeric IPv6 address
417  aBuffer.append( "[" );
418  aBuffer.append( rHost );
419  aBuffer.append( "]" );
420  }
421  else
422  {
423  // host is given either as numeric IPv4 address or non-numeric hostname
424  aBuffer.append( rHost );
425  }
426 
427  aBuffer.append( ':' );
428  aBuffer.append( nPort );
429  const OUString aHostAndPort( aBuffer.makeStringAndClear() );
430 
431  for (auto const& noProxy : m_aNoProxyList)
432  {
433  if ( bUseFullyQualified )
434  {
435  if ( noProxy.second.Matches( aHostAndPort ) )
436  return false;
437  }
438  else
439  {
440  if ( noProxy.first.Matches( aHostAndPort ) )
441  return false;
442  }
443  }
444 
445  return true;
446 }
447 
448 namespace
449 {
450 #ifdef _WIN32
451 struct GetPACProxyData
452 {
453  const OUString& m_rProtocol;
454  const OUString& m_rHost;
455  sal_Int32 m_nPort;
456  bool m_bAutoDetect = false;
457  OUString m_sAutoConfigUrl;
458  InternetProxyServer m_ProxyServer;
459 
460  GetPACProxyData(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort)
461  : m_rProtocol(rProtocol)
462  , m_rHost(rHost)
463  , m_nPort(nPort)
464  {
465  }
466 };
467 
468 // Tries to get proxy configuration using WinHttpGetProxyForUrl, which supports Web Proxy Auto-Discovery
469 // (WPAD) protocol and manually configured address to get Proxy Auto-Configuration (PAC) file.
470 // The WinINet/WinHTTP functions cannot correctly run in a STA COM thread, so use a dedicated thread
471 DWORD WINAPI GetPACProxyThread(_In_ LPVOID lpParameter)
472 {
473  assert(lpParameter);
474  GetPACProxyData* pData = static_cast<GetPACProxyData*>(lpParameter);
475 
476  OUString url(pData->m_rProtocol + "://" + pData->m_rHost + ":"
477  + OUString::number(pData->m_nPort));
478 
479  HINTERNET hInternet = WinHttpOpen(L"Mozilla 5.0", WINHTTP_ACCESS_TYPE_NO_PROXY,
480  WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
481  DWORD nError = GetLastError();
482  if (!hInternet)
483  return nError;
484 
485  WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions{};
486  if (pData->m_bAutoDetect)
487  {
488  AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
489  AutoProxyOptions.dwAutoDetectFlags
490  = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
491  }
492  if (!pData->m_sAutoConfigUrl.isEmpty())
493  {
494  AutoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
495  AutoProxyOptions.lpszAutoConfigUrl = o3tl::toW(pData->m_sAutoConfigUrl.getStr());
496  }
497  // First, try without autologon. According to
498  // https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/web/winhttp/WinhttpProxySample/GetProxy.cpp
499  // autologon prevents caching, and so causes repetitive network traffic.
500  AutoProxyOptions.fAutoLogonIfChallenged = FALSE;
501  WINHTTP_PROXY_INFO ProxyInfo{};
502  bool bResult
503  = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()), &AutoProxyOptions, &ProxyInfo);
504  nError = GetLastError();
505  if (!bResult && nError == ERROR_WINHTTP_LOGIN_FAILURE)
506  {
507  AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
508  bResult = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()),
509  &AutoProxyOptions, &ProxyInfo);
510  nError = GetLastError();
511  }
512  WinHttpCloseHandle(hInternet);
513  if (bResult)
514  {
515  if (ProxyInfo.lpszProxyBypass)
516  GlobalFree(ProxyInfo.lpszProxyBypass);
517  if (ProxyInfo.lpszProxy)
518  {
519  OUString sProxyResult(o3tl::toU(ProxyInfo.lpszProxy));
520  GlobalFree(ProxyInfo.lpszProxy);
521  // Get the first of possibly multiple results
522  sProxyResult = sProxyResult.getToken(0, ';');
523  sal_Int32 nPortSepPos = sProxyResult.indexOf(':');
524  if (nPortSepPos != -1)
525  {
526  pData->m_ProxyServer.nPort = sProxyResult.copy(nPortSepPos + 1).toInt32();
527  sProxyResult = sProxyResult.copy(0, nPortSepPos);
528  }
529  else
530  {
531  pData->m_ProxyServer.nPort = 0;
532  }
533  pData->m_ProxyServer.aName = sProxyResult;
534  }
535  }
536 
537  return nError;
538 }
539 
540 InternetProxyServer GetPACProxy(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort)
541 {
542  GetPACProxyData aData(rProtocol, rHost, nPort);
543 
544  // WinHTTP only supports http(s), so don't try for other protocols
545  if (!(rProtocol.equalsIgnoreAsciiCase("http") || rProtocol.equalsIgnoreAsciiCase("https")))
546  return aData.m_ProxyServer;
547 
548  // Only try to get configuration from PAC (with all the overhead, including new thread)
549  // if configured to do so
550  {
551  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG aProxyConfig{};
552  bool bResult = WinHttpGetIEProxyConfigForCurrentUser(&aProxyConfig);
553  if (aProxyConfig.lpszProxy)
554  GlobalFree(aProxyConfig.lpszProxy);
555  if (aProxyConfig.lpszProxyBypass)
556  GlobalFree(aProxyConfig.lpszProxyBypass);
557  // Don't try WPAD if AutoDetection or AutoConfig script URL are not configured
558  if (!bResult || !(aProxyConfig.fAutoDetect || aProxyConfig.lpszAutoConfigUrl))
559  return aData.m_ProxyServer;
560  aData.m_bAutoDetect = aProxyConfig.fAutoDetect;
561  if (aProxyConfig.lpszAutoConfigUrl)
562  {
563  aData.m_sAutoConfigUrl = o3tl::toU(aProxyConfig.lpszAutoConfigUrl);
564  GlobalFree(aProxyConfig.lpszAutoConfigUrl);
565  }
566  }
567 
568  HANDLE hThread = CreateThread(nullptr, 0, GetPACProxyThread, &aData, 0, nullptr);
569  if (hThread)
570  {
571  WaitForSingleObject(hThread, INFINITE);
572  CloseHandle(hThread);
573  }
574  return aData.m_ProxyServer;
575 }
576 
577 #else // .. _WIN32
578 
579 // Read the settings from the OS which are stored in env vars
580 //
581 InternetProxyServer GetUnixSystemProxy(const OUString & rProtocol)
582 {
583  // TODO this could be improved to read the "no_proxy" env variable
584  InternetProxyServer aProxy;
585  OUString protocolLower = rProtocol.toAsciiLowerCase() + "_proxy";
586  OString protocolLowerStr = OUStringToOString( protocolLower, RTL_TEXTENCODING_ASCII_US );
587  const char* pEnvProxy = getenv(protocolLowerStr.getStr());
588  if (!pEnvProxy)
589  return aProxy;
590  // expecting something like "https://example.ct:80"
591  OUString tmp = OUString::createFromAscii(pEnvProxy);
592  if (tmp.getLength() < (rProtocol.getLength() + 3))
593  return aProxy;
594  tmp = tmp.copy(rProtocol.getLength() + 3);
595  sal_Int32 x = tmp.indexOf(':');
596  if (x == -1)
597  return aProxy;
598  int nPort = tmp.copy(x + 1).toInt32();
599  if (nPort == 0)
600  return aProxy;
601  aProxy.aName = tmp.copy(0, x);
602  aProxy.nPort = nPort;
603  return aProxy;
604 }
605 
606 #endif // else .. _WIN32
607 }
608 
610  const OUString & rProtocol,
611  const OUString & rHost,
612  sal_Int32 nPort ) const
613 {
614  osl::Guard< osl::Mutex > aGuard( m_aMutex );
615 
617  {
618  // Never use proxy.
619  return m_aEmptyProxy;
620  }
621 
622  // If get from system
623  if (m_nProxyType == ProxyType::Automatic && !rHost.isEmpty())
624  {
625 #ifdef _WIN32
626  InternetProxyServer aProxy(GetPACProxy(rProtocol, rHost, nPort));
627 #else
628  InternetProxyServer aProxy(GetUnixSystemProxy(rProtocol));
629 #endif // _WIN32
630  if (!aProxy.aName.isEmpty())
631  return aProxy;
632  }
633 
634  if ( !rHost.isEmpty() && !m_aNoProxyList.empty() )
635  {
636 
637  // First, try direct hostname match - #110515#
638 
639 
640  if ( !shouldUseProxy( rHost, nPort, false ) )
641  return m_aEmptyProxy;
642 
643 
644  // Second, try match against full qualified hostname - #104401#
645 
646 
647  OUString aHost;
648 
649  if ( ( rHost.getLength() > 1 ) &&
650  ( rHost[ 0 ] == '[' ))
651  {
652  // host is given as numeric IPv6 address. name resolution
653  // functions need hostname without square brackets.
654  aHost = rHost.copy( 1, rHost.getLength() - 2 );
655  }
656  else
657  {
658  aHost = rHost;
659  }
660 
661  OUString aFullyQualifiedHost;
662  if ( !m_aHostnames.get( aHost, aFullyQualifiedHost ) )
663  {
664  // This might be quite expensive (DNS lookup).
665  const osl::SocketAddr aAddr( aHost, nPort );
666  aFullyQualifiedHost = aAddr.getHostname().toAsciiLowerCase();
667  m_aHostnames.put( aHost, aFullyQualifiedHost );
668  }
669 
670  // Error resolving name? -> fallback.
671  if ( aFullyQualifiedHost.isEmpty() )
672  aFullyQualifiedHost = aHost;
673 
674  if ( aFullyQualifiedHost != aHost )
675  {
676  if ( !shouldUseProxy( aFullyQualifiedHost, nPort, false ) )
677  return m_aEmptyProxy;
678  }
679 
680 
681  // Third, try match of fully qualified entries in no-proxy list
682  // against full qualified hostname
683 
684  // Example:
685  // list: staroffice-doc -> full: xyz.germany.sun.com
686  // in: staroffice-doc.germany.sun.com -> full: xyz.germany.sun.com
687 
688 
689  if ( !shouldUseProxy( aFullyQualifiedHost, nPort, true ) )
690  return m_aEmptyProxy;
691  }
692 
693  if ( rProtocol.toAsciiLowerCase() == "ftp" )
694  {
695  if ( !m_aFtpProxy.aName.isEmpty() && m_aFtpProxy.nPort >= 0 )
696  return m_aFtpProxy;
697  }
698  else if ( rProtocol.toAsciiLowerCase() == "https" )
699  {
700  if ( !m_aHttpsProxy.aName.isEmpty() )
701  return m_aHttpsProxy;
702  }
703  else if ( !m_aHttpProxy.aName.isEmpty() )
704  {
705  // All other protocols use the HTTP proxy.
706  return m_aHttpProxy;
707  }
708  return m_aEmptyProxy;
709 }
710 
711 // virtual
713  const util::ChangesEvent& Event )
714 {
715  osl::Guard< osl::Mutex > aGuard( m_aMutex );
716 
717  for ( const util::ElementChange& rElem : Event.Changes )
718  {
719  OUString aKey;
720  if ( ( rElem.Accessor >>= aKey ) && !aKey.isEmpty() )
721  {
722  if ( aKey == PROXY_TYPE_KEY )
723  {
724  sal_Int32 tmp;
725  if ( !( rElem.Element >>= tmp ) )
726  {
727  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
728  "Error getting config item value!" );
729  }
730  else
731  m_nProxyType = static_cast<ProxyType>(tmp);
732  }
733  else if ( aKey == NO_PROXY_LIST_KEY )
734  {
735  OUString aNoProxyList;
736  if ( !( rElem.Element >>= aNoProxyList ) )
737  {
738  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
739  "Error getting config item value!" );
740  }
741 
742  setNoProxyList( aNoProxyList );
743  }
744  else if ( aKey == HTTP_PROXY_NAME_KEY )
745  {
746  if ( !( rElem.Element >>= m_aHttpProxy.aName ) )
747  {
748  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
749  "Error getting config item value!" );
750  }
751  }
752  else if ( aKey == HTTP_PROXY_PORT_KEY )
753  {
754  if ( !( rElem.Element >>= m_aHttpProxy.nPort ) )
755  {
756  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
757  "Error getting config item value!" );
758  }
759 
760  if ( m_aHttpProxy.nPort == -1 )
761  m_aHttpProxy.nPort = 80; // standard HTTP port.
762  }
763  else if ( aKey == HTTPS_PROXY_NAME_KEY )
764  {
765  if ( !( rElem.Element >>= m_aHttpsProxy.aName ) )
766  {
767  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
768  "Error getting config item value!" );
769  }
770  }
771  else if ( aKey == HTTPS_PROXY_PORT_KEY )
772  {
773  if ( !( rElem.Element >>= m_aHttpsProxy.nPort ) )
774  {
775  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
776  "Error getting config item value!" );
777  }
778 
779  if ( m_aHttpsProxy.nPort == -1 )
780  m_aHttpsProxy.nPort = 443; // standard HTTPS port.
781  }
782  else if ( aKey == FTP_PROXY_NAME_KEY )
783  {
784  if ( !( rElem.Element >>= m_aFtpProxy.aName ) )
785  {
786  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
787  "Error getting config item value!" );
788  }
789  }
790  else if ( aKey == FTP_PROXY_PORT_KEY )
791  {
792  if ( !( rElem.Element >>= m_aFtpProxy.nPort ) )
793  {
794  OSL_FAIL( "InternetProxyDecider - changesOccurred - "
795  "Error getting config item value!" );
796  }
797  }
798  }
799  }
800 }
801 
802 
803 // virtual
804 void SAL_CALL InternetProxyDecider_Impl::disposing(const lang::EventObject&)
805 {
806  if ( m_xNotifier.is() )
807  {
808  osl::Guard< osl::Mutex > aGuard( m_aMutex );
809 
810  if ( m_xNotifier.is() )
811  m_xNotifier.clear();
812  }
813 }
814 
815 
817  const OUString & rNoProxyList )
818 {
819  osl::Guard< osl::Mutex > aGuard( m_aMutex );
820 
821  m_aNoProxyList.clear();
822 
823  if ( rNoProxyList.isEmpty() )
824  return;
825 
826  // List of connection endpoints hostname[:port],
827  // separated by semicolon. Wildcards allowed.
828 
829  sal_Int32 nPos = 0;
830  sal_Int32 nEnd = rNoProxyList.indexOf( ';' );
831  sal_Int32 nLen = rNoProxyList.getLength();
832 
833  do
834  {
835  if ( nEnd == -1 )
836  nEnd = nLen;
837 
838  OUString aToken = rNoProxyList.copy( nPos, nEnd - nPos );
839 
840  if ( !aToken.isEmpty() )
841  {
842  OUString aServer;
843  OUString aPort;
844 
845  // numerical IPv6 address?
846  bool bIPv6Address = false;
847  sal_Int32 nClosedBracketPos = aToken.indexOf( ']' );
848  if ( nClosedBracketPos == -1 )
849  nClosedBracketPos = 0;
850  else
851  bIPv6Address = true;
852 
853  sal_Int32 nColonPos = aToken.indexOf( ':', nClosedBracketPos );
854  if ( nColonPos == -1 )
855  {
856  // No port given, server pattern equals current token
857  aPort = "*";
858  if ( aToken.indexOf( '*' ) == -1 )
859  {
860  // pattern describes exactly one server
861  aServer = aToken;
862  }
863 
864  aToken += ":*";
865  }
866  else
867  {
868  // Port given, extract server pattern
869  sal_Int32 nAsteriskPos = aToken.indexOf( '*' );
870  aPort = aToken.copy( nColonPos + 1 );
871  if ( nAsteriskPos < nColonPos )
872  {
873  // pattern describes exactly one server
874  aServer = aToken.copy( 0, nColonPos );
875  }
876  }
877 
878  OUStringBuffer aFullyQualifiedHost;
879  if ( !aServer.isEmpty() )
880  {
881  // Remember fully qualified server name if current list
882  // entry specifies exactly one non-fully qualified server
883  // name.
884 
885  // remove square brackets from host name in case it's
886  // a numerical IPv6 address.
887  if ( bIPv6Address )
888  aServer = aServer.copy( 1, aServer.getLength() - 2 );
889 
890  // This might be quite expensive (DNS lookup).
891  const osl::SocketAddr aAddr( aServer, 0 );
892  OUString aTmp = aAddr.getHostname().toAsciiLowerCase();
893  if ( aTmp != aServer.toAsciiLowerCase() )
894  {
895  if ( bIPv6Address )
896  {
897  aFullyQualifiedHost.append( "[" );
898  aFullyQualifiedHost.append( aTmp );
899  aFullyQualifiedHost.append( "]" );
900  }
901  else
902  {
903  aFullyQualifiedHost.append( aTmp );
904  }
905  aFullyQualifiedHost.append( ":" );
906  aFullyQualifiedHost.append( aPort );
907  }
908  }
909 
910  m_aNoProxyList.emplace_back( WildCard( aToken ),
911  WildCard( aFullyQualifiedHost.makeStringAndClear() ) );
912  }
913 
914  if ( nEnd != nLen )
915  {
916  nPos = nEnd + 1;
917  nEnd = rNoProxyList.indexOf( ';', nPos );
918  }
919  }
920  while ( nEnd != nLen );
921 }
922 
923 } // namespace proxydecider_impl
924 
925 
926 // InternetProxyDecider Implementation.
927 
928 
930  const uno::Reference< uno::XComponentContext>& rxContext )
931 : m_xImpl( new proxydecider_impl::InternetProxyDecider_Impl( rxContext ) )
932 {
933 }
934 
935 
937 {
938  // Break circular reference between config listener and notifier.
939  m_xImpl->dispose();
940 }
941 
942 
943 bool InternetProxyDecider::shouldUseProxy( const OUString & rProtocol,
944  const OUString & rHost,
945  sal_Int32 nPort ) const
946 {
947  const InternetProxyServer & rData = m_xImpl->getProxy( rProtocol,
948  rHost,
949  nPort );
950  return !rData.aName.isEmpty();
951 }
952 
953 
955  const OUString & rProtocol,
956  const OUString & rHost,
957  sal_Int32 nPort ) const
958 {
959  return m_xImpl->getProxy( rProtocol, rHost, nPort );
960 }
961 
962 } // namespace ucbhelper
963 
964 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void SAL_CALL changesOccurred(const util::ChangesEvent &Event) override
bool hasValue()
sal_Int32 nPort
The port of the proxy server.
InternetProxyServer getProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
std::unique_ptr< ContentProperties > pData
uno::Reference< util::XChangesNotifier > m_xNotifier
const ContentProperties & rData
float x
std::pair< WildCard, WildCard > NoProxyListEntry
OString m_aWildString
#define HTTPS_PROXY_PORT_KEY
css::uno::Any const & rValue
Sequence< PropertyValue > aArguments
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
size_t pos
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
constexpr OUStringLiteral aData
#define TRUE
virtual void SAL_CALL disposing(const lang::EventObject &Source) override
void setNoProxyList(const OUString &rNoProxyList)
#define NO_PROXY_LIST_KEY
static bool getConfigStringValue(const uno::Reference< container::XNameAccess > &xNameAccess, const char *key, OUString &value)
bool shouldUseProxy(const OUString &rHost, sal_Int32 nPort, bool bUseFullyQualified) const
static bool getConfigInt32Value(const uno::Reference< container::XNameAccess > &xNameAccess, const char *key, sal_Int32 &value)
#define HTTPS_PROXY_NAME_KEY
#define FTP_PROXY_PORT_KEY
This struct describes a proxy server.
bool shouldUseProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
Informs whether a proxy server should be used.
std::unique_ptr< char[]> aBuffer
#define PROXY_TYPE_KEY
OUString aName
The name of the proxy server.
std::deque< HostListEntry > m_aHostList
#define FTP_PROXY_NAME_KEY
bool Matches(std::u16string_view rStr) const
#define FALSE
InternetProxyDecider(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
Constructor.
rtl::Reference< proxydecider_impl::InternetProxyDecider_Impl > m_xImpl
void dispose()
#define CONFIG_ROOT_KEY
#define HTTP_PROXY_PORT_KEY
InternetProxyServer getProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
Returns the proxy server to be used.
sal_uInt16 nPos
#define HTTP_PROXY_NAME_KEY