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 <cppuhelper/supportsservice.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <sal/log.hxx>
23 
24 #include "wininetbackend.hxx"
25 
26 #if !defined WIN32_LEAN_AND_MEAN
27 # define WIN32_LEAN_AND_MEAN
28 #endif
29 #include <windows.h>
30 #include <wininet.h>
31 #include <sal/alloca.h>
32 
33 #define WININET_DLL_NAME L"wininet.dll"
34 #define EQUAL_SIGN '='
35 #define COLON      ':'
36 #define SPACE      ' '
37 #define SEMI_COLON ';'
38 
39 namespace {
40 
41 struct Library {
42     HMODULE module;
43 
Library__anon393d2e710111::Library44     explicit Library(HMODULE theModule): module(theModule) {}
45 
~Library__anon393d2e710111::Library46     ~Library() { if (module) FreeLibrary(module); }
47 };
48 
49 }
50 
51 struct ProxyEntry
52 {
53     OUString Server;
54     OUString Port;
55 };
56 
57 
58 namespace
59 {
ReadProxyEntry(const OUString & aProxy,sal_Int32 & i)60     ProxyEntry ReadProxyEntry(const OUString& aProxy, sal_Int32& i)
61     {
62         ProxyEntry aProxyEntry;
63 
64         aProxyEntry.Server = aProxy.getToken( 0, COLON, i );
65         if ( i > -1 )
66             aProxyEntry.Port = aProxy.getToken( 0, COLON, i );
67 
68         return aProxyEntry;
69     }
70 
FindProxyEntry(const OUString & aProxyList,const OUString & aType)71     ProxyEntry FindProxyEntry(const OUString& aProxyList, const OUString& aType)
72     {
73         sal_Int32 nIndex = 0;
74 
75         do
76         {
77             // get the next token, e.g. ftp=server:port
78             OUString nextToken = aProxyList.getToken( 0, SPACE, nIndex );
79 
80             // split the next token again into the parts separated
81             // through '=', e.g. ftp=server:port -> ftp and server:port
82             sal_Int32 i = 0;
83             if( nextToken.indexOf( EQUAL_SIGN ) > -1 )
84             {
85                 if( aType.equals( nextToken.getToken( 0, EQUAL_SIGN, i ) ) )
86                     return ReadProxyEntry(nextToken, i);
87             }
88             else if( aType.isEmpty())
89                 return ReadProxyEntry(nextToken, i);
90 
91         } while ( nIndex >= 0 );
92 
93         return ProxyEntry();
94     }
95 
96 } // unnamed namespace
97 
WinInetBackend()98 WinInetBackend::WinInetBackend()
99 {
100     Library hWinInetDll( LoadLibraryW( WININET_DLL_NAME ) );
101     if( hWinInetDll.module )
102     {
103         typedef BOOL ( WINAPI *InternetQueryOption_Proc_T )( HINTERNET, DWORD, LPVOID, LPDWORD );
104 
105         InternetQueryOption_Proc_T lpfnInternetQueryOption =
106             reinterpret_cast< InternetQueryOption_Proc_T >(
107                 GetProcAddress( hWinInetDll.module, "InternetQueryOptionW" ) );
108         if (lpfnInternetQueryOption)
109         {
110             // Some Windows versions would fail the InternetQueryOption call
111             // with ERROR_OUTOFMEMORY when the initial dwLength were zero (and
112             // are apparently fine with the initial sizeof (INTERNET_PROXY_INFO)
113             // and need no reallocation), while other versions fail with
114             // ERROR_INSUFFICIENT_BUFFER upon that initial dwLength and need a
115             // reallocation:
116             INTERNET_PROXY_INFO pi;
117             LPINTERNET_PROXY_INFO lpi = &pi;
118             DWORD dwLength = sizeof (pi);
119             BOOL ok = lpfnInternetQueryOption(
120                 nullptr,
121                 INTERNET_OPTION_PROXY,
122                 lpi,
123                 &dwLength );
124             if (!ok)
125             {
126                 DWORD err = GetLastError();
127                 if (err == ERROR_INSUFFICIENT_BUFFER)
128                 {
129                     // allocate sufficient space on the stack
130                     // insufficient space on the stack results
131                     // in a stack overflow exception, we assume
132                     // this never happens, because of the relatively
133                     // small amount of memory we need
134                     // alloca is nice because it is fast and we don't
135                     // have to free the allocated memory, it will be
136                     // automatically done
137                     lpi = static_cast< LPINTERNET_PROXY_INFO >(
138                         alloca( dwLength ) );
139                     ok = lpfnInternetQueryOption(
140                         nullptr,
141                         INTERNET_OPTION_PROXY,
142                         lpi,
143                         &dwLength );
144                     if (!ok)
145                     {
146                         err = GetLastError();
147                     }
148                 }
149                 if (!ok)
150                 {
151                     SAL_WARN(
152                         "shell",
153                         "InternetQueryOption INTERNET_OPTION_PROXY"
154                             " GetLastError=" << err);
155                     return;
156                 }
157             }
158 
159             // if a proxy is disabled, InternetQueryOption returns
160             // an empty proxy list, so we don't have to check if
161             // proxy is enabled or not
162 
163             // We use InternetQueryOptionW (see https://msdn.microsoft.com/en-us/library/aa385101);
164             // it fills INTERNET_PROXY_INFO struct which is defined in WinInet.h to have LPCTSTR
165             // (i.e., the UNICODE-dependent generic string type expanding to const wchar_t* when
166             // UNICODE is defined, and InternetQueryOption macro expands to InternetQueryOptionW).
167             // Thus, it's natural to expect that W version would return wide strings. But it's not
168             // true. The W version still returns const char* in INTERNET_PROXY_INFO.
169             OUString aProxyList       = OUString::createFromAscii( lpi->lpszProxy );
170             OUString aProxyBypassList = OUString::createFromAscii( lpi->lpszProxyBypass );
171 
172             // override default for ProxyType, which is "0" meaning "No proxies".
173             valueProxyType_.IsPresent = true;
174             valueProxyType_.Value <<= sal_Int32(1);
175 
176             // fill proxy bypass list
177             if( aProxyBypassList.getLength() > 0 )
178             {
179                 OUStringBuffer aReverseList;
180                 sal_Int32 nIndex = 0;
181                 do
182                 {
183                     OUString aToken = aProxyBypassList.getToken( 0, SPACE, nIndex );
184                     if ( aProxyList.indexOf( aToken ) == -1 )
185                     {
186                         if ( aReverseList.getLength() )
187                         {
188                             aReverseList.insert( 0, sal_Unicode( SEMI_COLON ) );
189                             aReverseList.insert( 0, aToken );
190                         }
191                         else
192                             aReverseList = aToken;
193                     }
194                 }
195                 while ( nIndex >= 0 );
196 
197                 aProxyBypassList = aReverseList.makeStringAndClear();
198 
199                 valueNoProxy_.IsPresent = true;
200                 valueNoProxy_.Value <<= aProxyBypassList.replace( SPACE, SEMI_COLON );
201             }
202 
203             if( aProxyList.getLength() > 0 )
204             {
205 
206                 // this implementation follows the algorithm
207                 // of the internet explorer
208                 // if there are type-dependent proxy settings
209                 // and type independent proxy settings in the
210                 // registry the internet explorer chooses the
211                 // type independent proxy for all settings
212                 // e.g. imagine the following registry entry
213                 // ftp=server:port;http=server:port;server:port
214                 // the last token server:port is type independent
215                 // so the ie chooses this proxy server
216 
217                 // if there is no port specified for a type independent
218                 // server the ie uses the port of an http server if
219                 // there is one and it has a port
220 
221 
222                 ProxyEntry aTypeIndepProxy = FindProxyEntry( aProxyList, OUString());
223                 ProxyEntry aHttpProxy = FindProxyEntry( aProxyList, "http" );
224                 ProxyEntry aHttpsProxy  = FindProxyEntry( aProxyList, "https" );
225 
226                 ProxyEntry aFtpProxy  = FindProxyEntry( aProxyList, "ftp" );
227 
228                 if( aTypeIndepProxy.Server.getLength() )
229                 {
230                     aHttpProxy.Server = aTypeIndepProxy.Server;
231                     aHttpsProxy.Server  = aTypeIndepProxy.Server;
232                     aFtpProxy.Server  = aTypeIndepProxy.Server;
233 
234                     if( aTypeIndepProxy.Port.getLength() )
235                     {
236                         aHttpProxy.Port = aTypeIndepProxy.Port;
237                         aHttpsProxy.Port  = aTypeIndepProxy.Port;
238                         aFtpProxy.Port  = aTypeIndepProxy.Port;
239                     }
240                     else
241                     {
242                         aFtpProxy.Port  = aHttpProxy.Port;
243                         aHttpsProxy.Port  = aHttpProxy.Port;
244                     }
245                 }
246 
247                 // http proxy name
248                 if( aHttpProxy.Server.getLength() > 0 )
249                 {
250                     valueHttpProxyName_.IsPresent = true;
251                     valueHttpProxyName_.Value <<= aHttpProxy.Server;
252                 }
253 
254                 // http proxy port
255                 if( aHttpProxy.Port.getLength() > 0 )
256                 {
257                     valueHttpProxyPort_.IsPresent = true;
258                     valueHttpProxyPort_.Value <<= aHttpProxy.Port.toInt32();
259                 }
260 
261                 // https proxy name
262                 if( aHttpsProxy.Server.getLength() > 0 )
263                 {
264                     valueHttpsProxyName_.IsPresent = true;
265                     valueHttpsProxyName_.Value <<= aHttpsProxy.Server;
266                 }
267 
268                 // https proxy port
269                 if( aHttpsProxy.Port.getLength() > 0 )
270                 {
271                     valueHttpsProxyPort_.IsPresent = true;
272                     valueHttpsProxyPort_.Value <<= aHttpsProxy.Port.toInt32();
273                 }
274 
275                 // ftp proxy name
276                 if( aFtpProxy.Server.getLength() > 0 )
277                 {
278                     valueFtpProxyName_.IsPresent = true;
279                     valueFtpProxyName_.Value <<= aFtpProxy.Server;
280                 }
281 
282                 // ftp proxy port
283                 if( aFtpProxy.Port.getLength() > 0 )
284                 {
285                     valueFtpProxyPort_.IsPresent = true;
286                     valueFtpProxyPort_.Value <<= aFtpProxy.Port.toInt32();
287                 }
288             }
289         }
290     }
291 }
292 
~WinInetBackend()293 WinInetBackend::~WinInetBackend()
294 {
295 }
296 
createInstance()297 WinInetBackend* WinInetBackend::createInstance()
298 {
299     return new WinInetBackend;
300 }
301 
setPropertyValue(OUString const &,css::uno::Any const &)302 void WinInetBackend::setPropertyValue(
303     OUString const &, css::uno::Any const &)
304 {
305     throw css::lang::IllegalArgumentException(
306         "setPropertyValue not supported",
307         static_cast< cppu::OWeakObject * >(this), -1);
308 }
309 
getPropertyValue(OUString const & PropertyName)310 css::uno::Any WinInetBackend::getPropertyValue(
311     OUString const & PropertyName)
312 {
313     if ( PropertyName == "ooInetFTPProxyName" )
314     {
315         return css::uno::makeAny(valueFtpProxyName_);
316     } else if ( PropertyName == "ooInetFTPProxyPort" )
317     {
318         return css::uno::makeAny(valueFtpProxyPort_);
319     } else if ( PropertyName == "ooInetHTTPProxyName" )
320     {
321         return css::uno::makeAny(valueHttpProxyName_);
322     } else if ( PropertyName == "ooInetHTTPProxyPort" )
323     {
324         return css::uno::makeAny(valueHttpProxyPort_);
325     } else if ( PropertyName == "ooInetHTTPSProxyName" )
326     {
327         return css::uno::makeAny(valueHttpsProxyName_);
328     } else if ( PropertyName == "ooInetHTTPSProxyPort" )
329     {
330         return css::uno::makeAny(valueHttpsProxyPort_);
331     } else if ( PropertyName == "ooInetNoProxy" )
332     {
333         return css::uno::makeAny(valueNoProxy_);
334     } else if ( PropertyName == "ooInetProxyType" )
335     {
336         return css::uno::makeAny(valueProxyType_);
337     } else {
338         throw css::beans::UnknownPropertyException(
339             PropertyName, static_cast< cppu::OWeakObject * >(this));
340     }
341 }
342 
getBackendName()343 OUString WinInetBackend::getBackendName() {
344     return "com.sun.star.comp.configuration.backend.WinInetBackend" ;
345 }
346 
getImplementationName()347 OUString SAL_CALL WinInetBackend::getImplementationName()
348 {
349     return getBackendName() ;
350 }
351 
getBackendServiceNames()352 uno::Sequence<OUString> WinInetBackend::getBackendServiceNames()
353 {
354     uno::Sequence<OUString> aServiceNameList { "com.sun.star.configuration.backend.WinInetBackend" };
355 
356     return aServiceNameList ;
357 }
358 
supportsService(const OUString & aServiceName)359 sal_Bool SAL_CALL WinInetBackend::supportsService(const OUString& aServiceName)
360 {
361     return cppu::supportsService(this, aServiceName);
362 }
363 
getSupportedServiceNames()364 uno::Sequence<OUString> SAL_CALL WinInetBackend::getSupportedServiceNames()
365 {
366     return getBackendServiceNames() ;
367 }
368 
369 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
370