diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2018-06-26 14:39:49 +1000 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2018-06-26 09:33:24 +0200 |
commit | 2e142c0ee54744d35517f0b9c49a24302fb32d47 (patch) | |
tree | e828e65d70fc4a908f0d0ad8f04cd2cf4df8ecd4 | |
parent | 20c4a9ce69e73a182d60718e9b52510d66c8f20e (diff) |
tdf#114227: Add support for PAC to ucbhelper::InternetProxyDecider on Windows
Change-Id: I62c76efb354949699615a44d9482df24e3eaa314
Reviewed-on: https://gerrit.libreoffice.org/56433
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
-rw-r--r-- | include/ucbhelper/proxydecider.hxx | 4 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/NeonSession.cxx | 2 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/NeonSession.hxx | 2 | ||||
-rw-r--r-- | ucbhelper/Library_ucbhelper.mk | 4 | ||||
-rw-r--r-- | ucbhelper/source/client/proxydecider.cxx | 154 |
5 files changed, 159 insertions, 7 deletions
diff --git a/include/ucbhelper/proxydecider.hxx b/include/ucbhelper/proxydecider.hxx index 0ee4400a1392..3c873e2fe193 100644 --- a/include/ucbhelper/proxydecider.hxx +++ b/include/ucbhelper/proxydecider.hxx @@ -119,10 +119,10 @@ public: * If host is not empty this parameter must always contain a valid * port number, for instance the default port for the requested * protocol(i.e. 80 or http). - * @return a InternetProxyServer reference. If member aName of the + * @return a InternetProxyServer struct. If member aName of the * InternetProxyServer is empty no proxy server is to be used. */ - const InternetProxyServer & + InternetProxyServer getProxy( const OUString & rProtocol, const OUString & rHost, sal_Int32 nPort ) const; diff --git a/ucb/source/ucp/webdav-neon/NeonSession.cxx b/ucb/source/ucp/webdav-neon/NeonSession.cxx index 40bbb9d13568..aac868e3d87a 100644 --- a/ucb/source/ucp/webdav-neon/NeonSession.cxx +++ b/ucb/source/ucp/webdav-neon/NeonSession.cxx @@ -1696,7 +1696,7 @@ void NeonSession::abort() SAL_INFO( "ucb.ucp.webdav", "neon commands cannot be aborted" ); } -const ucbhelper::InternetProxyServer & NeonSession::getProxySettings() const +ucbhelper::InternetProxyServer NeonSession::getProxySettings() const { if ( m_aScheme == "http" || m_aScheme == "https" ) { diff --git a/ucb/source/ucp/webdav-neon/NeonSession.hxx b/ucb/source/ucp/webdav-neon/NeonSession.hxx index ec080f1f0473..87026ad23ec4 100644 --- a/ucb/source/ucp/webdav-neon/NeonSession.hxx +++ b/ucb/source/ucp/webdav-neon/NeonSession.hxx @@ -219,7 +219,7 @@ private: const OUString & inPath, const DAVRequestEnvironment & rEnv ); - const ucbhelper::InternetProxyServer & getProxySettings() const; + ucbhelper::InternetProxyServer getProxySettings() const; bool removeExpiredLocktoken( const OUString & inURL, const DAVRequestEnvironment & rEnv ); diff --git a/ucbhelper/Library_ucbhelper.mk b/ucbhelper/Library_ucbhelper.mk index 85c8e16f1573..8734c5291d91 100644 --- a/ucbhelper/Library_ucbhelper.mk +++ b/ucbhelper/Library_ucbhelper.mk @@ -50,5 +50,9 @@ $(eval $(call gb_Library_add_exception_objects,ucbhelper,\ ucbhelper/source/provider/simplenameclashresolverequest \ )) +$(eval $(call gb_Library_use_system_win32_libs,ucbhelper,\ + Winhttp \ +)) + # vim: set noet sw=4 ts=4: diff --git a/ucbhelper/source/client/proxydecider.cxx b/ucbhelper/source/client/proxydecider.cxx index cc0dd60afeab..fdf4506add26 100644 --- a/ucbhelper/source/client/proxydecider.cxx +++ b/ucbhelper/source/client/proxydecider.cxx @@ -34,6 +34,13 @@ #include <cppuhelper/implbase.hxx> #include <ucbhelper/proxydecider.hxx> +#ifdef _WIN32 +#include <o3tl/char16_t2wchar_t.hxx> +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#include <Winhttp.h> +#endif + using namespace com::sun::star; using namespace ucbhelper; @@ -131,7 +138,7 @@ public: void dispose(); - const InternetProxyServer & getProxy( const OUString & rProtocol, + InternetProxyServer getProxy(const OUString& rProtocol, const OUString & rHost, sal_Int32 nPort ) const; @@ -428,8 +435,138 @@ bool InternetProxyDecider_Impl::shouldUseProxy( const OUString & rHost, return true; } +#ifdef _WIN32 +namespace +{ +struct GetPACProxyData +{ + const OUString& m_rProtocol; + const OUString& m_rHost; + sal_Int32 m_nPort; + bool m_bAutoDetect = false; + OUString m_sAutoConfigUrl; + InternetProxyServer m_ProxyServer; + + GetPACProxyData(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort) + : m_rProtocol(rProtocol) + , m_rHost(rHost) + , m_nPort(nPort) + { + } +}; + +// Tries to get proxy configuration using WinHttpGetProxyForUrl, which supports Web Proxy Auto-Discovery +// (WPAD) protocol and manually configured address to get Proxy Auto-Configuration (PAC) file. +// The WinINet/WinHTTP functions cannot correctly run in a STA COM thread, so use a dedicated thread +DWORD WINAPI GetPACProxyThread(_In_ LPVOID lpParameter) +{ + assert(lpParameter); + GetPACProxyData* pData = static_cast<GetPACProxyData*>(lpParameter); + + OUString url(pData->m_rProtocol + "://" + pData->m_rHost + ":" + + OUString::number(pData->m_nPort)); + + HINTERNET hInternet = WinHttpOpen(L"Mozilla 5.0", WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + DWORD nError = GetLastError(); + if (!hInternet) + return nError; + + WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions{}; + if (pData->m_bAutoDetect) + { + AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + AutoProxyOptions.dwAutoDetectFlags + = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + if (!pData->m_sAutoConfigUrl.isEmpty()) + { + AutoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + AutoProxyOptions.lpszAutoConfigUrl = o3tl::toW(pData->m_sAutoConfigUrl.getStr()); + } + // First, try without autologon. According to + // https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/web/winhttp/WinhttpProxySample/GetProxy.cpp + // autologon prevents caching, and so causes repetitive network traffic. + AutoProxyOptions.fAutoLogonIfChallenged = FALSE; + WINHTTP_PROXY_INFO ProxyInfo{}; + BOOL bResult + = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()), &AutoProxyOptions, &ProxyInfo); + nError = GetLastError(); + if (!bResult && nError == ERROR_WINHTTP_LOGIN_FAILURE) + { + AutoProxyOptions.fAutoLogonIfChallenged = TRUE; + bResult = WinHttpGetProxyForUrl(hInternet, o3tl::toW(url.getStr()), + &AutoProxyOptions, &ProxyInfo); + nError = GetLastError(); + } + WinHttpCloseHandle(hInternet); + if (bResult) + { + if (ProxyInfo.lpszProxyBypass) + GlobalFree(ProxyInfo.lpszProxyBypass); + if (ProxyInfo.lpszProxy) + { + OUString sProxyResult = o3tl::toU(ProxyInfo.lpszProxy); + GlobalFree(ProxyInfo.lpszProxy); + // Get the first of possibly multiple results + sProxyResult = sProxyResult.getToken(0, ';'); + sal_Int32 nPortSepPos = sProxyResult.indexOf(':'); + if (nPortSepPos != -1) + { + pData->m_ProxyServer.nPort = sProxyResult.copy(nPortSepPos + 1).toInt32(); + sProxyResult = sProxyResult.copy(0, nPortSepPos); + } + else + { + pData->m_ProxyServer.nPort = 0; + } + pData->m_ProxyServer.aName = sProxyResult; + } + } + + return nError; +} + +InternetProxyServer GetPACProxy(const OUString& rProtocol, const OUString& rHost, sal_Int32 nPort) +{ + GetPACProxyData aData(rProtocol, rHost, nPort); + + // WinHTTP only supports http(s), so don't try for other protocols + if (!(rProtocol.equalsIgnoreAsciiCase("http") || rProtocol.equalsIgnoreAsciiCase("https"))) + return aData.m_ProxyServer; + + // Only try to get configuration from PAC (with all the overhead, including new thread) + // if configured to do so + { + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG aProxyConfig{}; + BOOL bResult = WinHttpGetIEProxyConfigForCurrentUser(&aProxyConfig); + if (aProxyConfig.lpszProxy) + GlobalFree(aProxyConfig.lpszProxy); + if (aProxyConfig.lpszProxyBypass) + GlobalFree(aProxyConfig.lpszProxyBypass); + // Don't try WPAD if AutoDetection or AutoConfig script URL are not configured + if (!bResult || !(aProxyConfig.fAutoDetect || aProxyConfig.lpszAutoConfigUrl)) + return aData.m_ProxyServer; + aData.m_bAutoDetect = aProxyConfig.fAutoDetect; + if (aProxyConfig.lpszAutoConfigUrl) + { + aData.m_sAutoConfigUrl = o3tl::toU(aProxyConfig.lpszAutoConfigUrl); + GlobalFree(aProxyConfig.lpszAutoConfigUrl); + } + } + + HANDLE hThread = CreateThread(nullptr, 0, GetPACProxyThread, &aData, 0, nullptr); + if (hThread) + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + return aData.m_ProxyServer; +} +} +#endif // _WIN32 -const InternetProxyServer & InternetProxyDecider_Impl::getProxy( +InternetProxyServer InternetProxyDecider_Impl::getProxy( const OUString & rProtocol, const OUString & rHost, sal_Int32 nPort ) const @@ -442,6 +579,17 @@ const InternetProxyServer & InternetProxyDecider_Impl::getProxy( return m_aEmptyProxy; } +#ifdef _WIN32 + // If get from system + if (m_nProxyType == 1 && !rHost.isEmpty()) + { + InternetProxyServer aProxy(GetPACProxy(rProtocol, rHost, nPort)); + if (!aProxy.aName.isEmpty()) + return aProxy; + } +#endif // _WIN32 + + if ( !rHost.isEmpty() && !m_aNoProxyList.empty() ) { @@ -767,7 +915,7 @@ bool InternetProxyDecider::shouldUseProxy( const OUString & rProtocol, } -const InternetProxyServer & InternetProxyDecider::getProxy( +InternetProxyServer InternetProxyDecider::getProxy( const OUString & rProtocol, const OUString & rHost, sal_Int32 nPort ) const |