();
}
/**
* Get the proxies for accessing a given URL. The result is obtained by
* evaluating the PAC file with the given url (and the host) as input.
*
* This method performs caching of the result.
*
* @param url the url for which a proxy is desired
* @return a list of proxies in a string like
* "PROXY foo.example.com:8080; PROXY bar.example.com:8080; DIRECT"
*
* @see #getProxiesWithoutCaching(URL)
*/
public String getProxies(URL url) {
String cachedResult = getFromCache(url);
if (cachedResult != null) {
return cachedResult;
}
String result = getProxiesWithoutCaching(url);
addToCache(url, result);
return result;
}
/**
* Get the proxies for accessing a given URL. The result is obtained by
* evaluating the PAC file with the given url (and the host) as input.
*
* @param url the url for which a proxy is desired
* @return a list of proxies in a string like
* "PROXY example.com:3128; DIRECT"
*
* @see #getProxies(URL)
*/
private String getProxiesWithoutCaching(URL url) {
if (pacHelperFunctionContents == null) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "Error loading pac functions");
return "DIRECT";
}
EvaluatePacAction evaluatePacAction = new EvaluatePacAction(pacContents, pacUrl.toString(),
pacHelperFunctionContents, url);
// Purposefully giving only these permissions rather than using java.policy. The "evaluatePacAction"
// isn't supposed to do very much and so doesn't require all the default permissions given by
// java.policy
Permissions p = new Permissions();
p.add(new RuntimePermission("accessClassInPackage.org.mozilla.javascript"));
p.add(new SocketPermission("*", "resolve"));
p.add(new PropertyPermission("java.vm.name", "read"));
ProtectionDomain pd = new ProtectionDomain(null, p);
AccessControlContext context = new AccessControlContext(new ProtectionDomain[] { pd });
return AccessController.doPrivileged(evaluatePacAction, context);
}
/**
* Returns the contents of file at pacUrl as a String.
*/
private String getPacContents(URL pacUrl) {
StringBuilder contents = null;
try {
String line = null;
contents = new StringBuilder();
BufferedReader pacReader = new BufferedReader(new InputStreamReader(pacUrl.openStream()));
try {
while ((line = pacReader.readLine()) != null) {
// OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, line);
contents = contents.append(line).append("\n");
}
} finally {
pacReader.close();
}
} catch (IOException e) {
contents = null;
}
return (contents != null) ? contents.toString() : null;
}
/**
* Returns the pac helper functions as a String. The functions are read
* from net/sourceforge/jnlp/resources/pac-funcs.js
*/
private String getHelperFunctionContents() {
StringBuilder contents = null;
try {
String line;
ClassLoader cl = this.getClass().getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
InputStream in = cl.getResourceAsStream("net/sourceforge/jnlp/runtime/pac-funcs.js");
BufferedReader pacFuncsReader = new BufferedReader(new InputStreamReader(in));
try {
contents = new StringBuilder();
while ((line = pacFuncsReader.readLine()) != null) {
// OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL,line);
contents = contents.append(line).append("\n");
}
} finally {
pacFuncsReader.close();
}
} catch (IOException e) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
contents = null;
}
return (contents != null) ? contents.toString() : null;
}
/**
* Gets an entry from the cache
*/
private String getFromCache(URL url) {
String lookupString = url.getProtocol() + "://" + url.getHost();
String result = cache.get(lookupString);
return result;
}
/**
* Adds an entry to the cache
*/
private void addToCache(URL url, String proxyResult) {
String lookupString = url.getAuthority() + "://" + url.getHost();
cache.put(lookupString, proxyResult);
}
/**
* Helper classs to run remote javascript code (specified by the user as
* PAC URL) inside a sandbox.
*/
private static class EvaluatePacAction implements PrivilegedAction {
private String pacContents;
private String pacUrl;
private String pacFuncsContents;
private URL url;
public EvaluatePacAction(String pacContents, String pacUrl, String pacFuncsContents, URL url) {
this.pacContents = pacContents;
this.pacUrl = pacUrl;
this.pacFuncsContents = pacFuncsContents;
this.url = url;
}
public String run() {
Context cx = Context.enter();
try {
/*
* TODO defense in depth.
*
* This is already running within a sandbox, but we can (and we
* should) lock it down further. Look into ClassShutter.
*/
Scriptable scope = cx.initStandardObjects();
// any optimization level greater than -1 will trigger code generation
// and this block will then need classloader permissions
cx.setOptimizationLevel(-1);
Object result = null;
result = cx.evaluateString(scope, pacFuncsContents, "internal", 1, null);
result = cx.evaluateString(scope, pacContents, pacUrl, 1, null);
Object functionObj = scope.get("FindProxyForURL", scope);
if (!(functionObj instanceof Function)) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "FindProxyForURL not found");
return null;
} else {
Function findProxyFunction = (Function) functionObj;
Object[] args = { url.toString(), url.getHost() };
result = findProxyFunction.call(cx, scope, scope, args);
return (String) result;
}
} catch (Exception e) {
OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
return "DIRECT";
} finally {
Context.exit();
}
}
}
}