/*******************************************************************************
* Copyright (c) 2007, 2008 compeople AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* compeople AG (Stefan Liebig) - initial API and implementation
*******************************************************************************/
package de.compeople.commons.net.proxy.win32;
import static de.digitalstep.ntlmproxy.net.proxy.Helper.proxyType;
import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import com.google.common.base.Strings;
import de.compeople.commons.net.proxy.ProxySelectorUtils;
import de.compeople.commons.net.winhttp.WinHttpAutoProxyOptions;
import de.compeople.commons.net.winhttp.WinHttpCurrentUserIEProxyConfig;
import de.compeople.commons.net.winhttp.WinHttpProxyInfo;
import de.digitalstep.ntlmproxy.nativelib.WinHttp;
import de.digitalstep.ntlmproxy.net.proxy.Helper;
/**
* ProxySelector that gets its settings from the
* "internet options >> connection settings"
*/
class WinHttpProxySelector extends ProxySelector {
@SuppressWarnings("unused")
private static final WinHttp WIN_HTTP = WinHttp.initialize();
private WinHttpCurrentUserIEProxyConfig proxyConfig = null;
private WinHttpConfig winHttpConfig;
private boolean pacFailed = false;
private boolean wpadFailed = false;
private List<Proxy> wpadProxies = null;
private Set<Proxy> failed = new HashSet<Proxy>();
private final static int ERROR_WINHTTP_AUTODETECTION_FAILED = 12180;
private static final String MY_NAME = WinHttpProxySelector.class.getSimpleName();
private static final Logger LOGGER = Logger.getLogger(WinHttpProxySelector.class.getName());
/**
* Default constructor
*/
public WinHttpProxySelector() {
}
@Override
public List<Proxy> select(URI uri) {
WinHttpCurrentUserIEProxyConfig newProxyConfig = new WinHttpCurrentUserIEProxyConfig();
if (!WinHttp.getIEProxyConfigForCurrentUser(newProxyConfig)) {
LOGGER
.warning("WinHttp.GetIEProxyConfigForCurrentUser failed with error '" + WinHttp.getLastErrorMessage() + "' #"
+ WinHttp.getLastError() + ".");
return ProxySelectorUtils.getEmptyProxyList();
}
// Let's see if we are still up-to-date.
boolean proxyConfigChanged = !newProxyConfig.equals(proxyConfig);
if (proxyConfigChanged) {
LOGGER.finest("Initializing.");
proxyConfig = newProxyConfig;
// Retry pac and wpad
pacFailed = false;
wpadFailed = false;
wpadProxies = ProxySelectorUtils.getEmptyProxyList();
}
List<Proxy> proxies = new ArrayList<Proxy>();
// Explicit proxies defined?
if (!Strings.isNullOrEmpty(proxyConfig.getProxy())) {
// Yes, let's see if we are still up-to-date or not yet initialized.
if (proxyConfigChanged || winHttpConfig == null) {
winHttpConfig = new WinHttpConfig(proxyConfig);
}
if (!winHttpConfig.bypassProxyFor(uri)) {
if (winHttpConfig.useProtocolSpecificProxies()) {
proxies.addAll(winHttpConfig.getProtocolSpecificProxies(uri));
} else {
proxies.addAll(winHttpConfig.getUniversalProxies());
}
}
}
boolean isPac = proxyConfig.getAutoConfigUrl() != null;
boolean isWpad = proxyConfig.isAutoDetect();
if (isPac || isWpad) {
// Create the WinHTTP session.
int hHttpSession = WinHttp.open(MY_NAME, WinHttpProxyInfo.WINHTTP_ACCESS_TYPE_NO_PROXY, WinHttp.NO_PROXY_NAME,
WinHttp.NO_PROXY_BYPASS, 0);
if (hHttpSession == 0) {
LOGGER.warning("WinHttp.Open failed with error'" + WinHttp.getLastErrorMessage() + "' #" + WinHttp.getLastError() + ".");
} else {
try {
// PAC file?
if (isPac && !pacFailed) {
proxies.addAll(pacSelect(hHttpSession, uri));
}
// WPAD?
if (isWpad && !wpadFailed) {
if (wpadProxies == null || wpadProxies.size() == 0) {
wpadProxies = wpadSelect(hHttpSession, uri);
}
proxies.addAll(wpadProxies);
}
} finally {
WinHttp.closeHandle(hHttpSession);
}
}
}
resort(proxies);
return proxies;
}
/**
* Resort the proxies such that we shuffle the failed proxy to the end of
* the list.
*
* @param proxies
*/
private void resort(List<Proxy> proxies) {
if (!proxies.contains(Proxy.NO_PROXY)) {
proxies.add(Proxy.NO_PROXY);
}
if (failed.isEmpty()) {
return;
}
proxies.removeAll(failed);
proxies.addAll(failed);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
LOGGER.finest("Connect failed for " + uri + " on " + sa + ".");
if (uri == null) {
return;
}
failed.add(Helper.create(proxyType(uri.getScheme()), sa));
}
protected List<Proxy> pacSelect(int hHttpSession, URI uri) {
// Set up the autoproxy call.
WinHttpAutoProxyOptions autoProxyOptions = new WinHttpAutoProxyOptions();
autoProxyOptions.setFlags(WinHttpAutoProxyOptions.WINHTTP_AUTOPROXY_CONFIG_URL);
autoProxyOptions.setAutoConfigUrl(proxyConfig.getAutoConfigUrl());
autoProxyOptions.setAutoLogonIfChallenged(true);
WinHttpProxyInfo proxyInfo = new WinHttpProxyInfo();
boolean ok = WinHttp.getProxyForUrl(hHttpSession, uri.toString(), autoProxyOptions, proxyInfo);
if (!ok) {
pacFailed = true;
LOGGER.warning("WinHttp.GetProxyForUrl for pac failed with error '" + WinHttp.getLastErrorMessage() + "' #"
+ WinHttp.getLastError() + ".");
return ProxySelectorUtils.getEmptyProxyList();
}
ProxyBypass proxyBypass = new ProxyBypass(proxyInfo.getProxyBypass());
if (proxyBypass.bypassProxyFor(uri)) {
return ProxySelectorUtils.getProxyListDirectAccessOnly();
} else {
return ProxySelectorPACUtils.getProxies(proxyInfo.getProxy());
}
}
protected List<Proxy> wpadSelect(int hHttpSession, URI uri) {
// Set up the autoproxy call.
WinHttpAutoProxyOptions autoProxyOptions = new WinHttpAutoProxyOptions();
autoProxyOptions.setFlags(WinHttpAutoProxyOptions.WINHTTP_AUTOPROXY_AUTO_DETECT);
autoProxyOptions.setAutoDetectFlags(WinHttpAutoProxyOptions.WINHTTP_AUTO_DETECT_TYPE_DHCP
| WinHttpAutoProxyOptions.WINHTTP_AUTO_DETECT_TYPE_DNS_A);
autoProxyOptions.setAutoLogonIfChallenged(true);
WinHttpProxyInfo proxyInfo = new WinHttpProxyInfo();
boolean ok = WinHttp.getProxyForUrl(hHttpSession, uri.toString(), autoProxyOptions, proxyInfo);
if (!ok) {
wpadFailed = WinHttp.getLastError() == ERROR_WINHTTP_AUTODETECTION_FAILED;
LOGGER.warning("WinHttp.GetProxyForUrl for wpad failed with error '" + WinHttp.getLastErrorMessage() + "' #"
+ WinHttp.getLastError() + ".");
return ProxySelectorUtils.getEmptyProxyList();
}
ProxyBypass proxyBypass = new ProxyBypass(proxyInfo.getProxyBypass());
if (proxyBypass.bypassProxyFor(uri)) {
return ProxySelectorUtils.getProxyListDirectAccessOnly();
} else {
return ProxySelectorPACUtils.getProxies(proxyInfo.getProxy());
}
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return MY_NAME;
}
}