[icedtea-web] RFC: add proxy auto config support

Dr Andrew John Hughes ahughes at redhat.com
Wed Feb 16 11:51:30 PST 2011


On 14:21 Wed 16 Feb     , Omair Majid wrote:
> Hi,
> 
> The attached patch adds support for reading, parsing and using proxy 
> auto config (PAC) files to icedtea-web.
> 
> PAC files are written in javascript, so this patch also adds a 
> dependency on rhino for building. A dependency on jrunscript is also 
> added for testing.
> 
> PAC files will be used by both plugin and netx if deployment.proxy.type 
> is 2 (PAC) and deployment.proxy.auto.config.url points to a valid PAC 
> file. PAC files may also be used by netx if deployment.proxy.type is 3 
> (browser) and the browser (firefox) is using a proxy auto config file 
> for resolving proxies.
> 
> The PAC file is evaluated inside a sandbox - which only has permissions 
> to resolve urls. Because we dont want to grant createclassloader 
> permissions, I have had to set (in ProxyAutoConfig.java):
>                  cx.setOptimizationLevel(-1);
> which works without creating classes (and so works without 
> createClassLoader permissions). This option is not available by going 
> through the Java ScriptEngine apis. The patch therefore uses the rhino 
> apis directly.
> 
> Since parsing a PAC file requires access to a set of additional 
> functions, the patch also implements them. Tests for these are included.
> 
> make check should run these tests and end by printing a line like:
> X of Y tests failed
> 
> Any thoughts or comments?
> 

I thought libproxy was going to be used to handle these files.  Why are
we implementing this ourselves?

There seems to be a completely broken assumption that rhino.jar is going
to be in the JDK??? It should detect it as IcedTea does.

What is jrunscript?  I've never heard of this before.

Further comments inline.

> Cheers,
> Omair

> diff -r cd1eda4f0d97 Makefile.am
> --- a/Makefile.am	Tue Feb 15 11:01:01 2011 -0500
> +++ b/Makefile.am	Wed Feb 16 13:59:02 2011 -0500
> @@ -8,7 +8,7 @@
>  # Build directories
>  
>  BOOT_DIR = $(abs_top_builddir)/bootstrap/jdk1.6.0
> -RUNTIME = $(BOOT_DIR)/jre/lib/rt.jar:$(BOOT_DIR)/jre/lib/jsse.jar
> +RUNTIME = $(BOOT_DIR)/jre/lib/rt.jar:$(BOOT_DIR)/jre/lib/jsse.jar:$(BOOT_DIR)/jre/lib/rhino.jar
>  

This is broken; you can't just assume that there is a rhino.jar in jre/lib.

>  # Flags
>  IT_CFLAGS=$(CFLAGS) $(ARCHFLAG)
> @@ -86,6 +86,8 @@
>  all-local: stamps/netx-dist.stamp extra-lib/about.jar stamps/plugin.stamp $(NETX_DIR)/launcher/javaws \
>   javaws.desktop stamps/docs.stamp $(NETX_DIR)/launcher/controlpanel/itweb-settings itweb-settings.desktop
>  
> +check-local: check-pac-functions
> +
>  clean-local: clean-netx clean-plugin clean-liveconnect clean-extra clean-bootstrap-directory \
>   clean-native-ecj clean-desktop-files clean-docs
>  	if [ -e stamps ] ; then \
> @@ -411,6 +413,14 @@
>  	rm -rf ${abs_top_builddir}/docs/plugin
>  	rm -f stamps/plugin-docs.stamp
>  
> +
> +# Tests
> +# ==========================
> +
> +check-pac-functions: stamps/bootstrap-directory.stamp
> +	$(BOOT_DIR)/bin/jrunscript $(abs_top_srcdir)/tests/netx/pac/pac-funcs-test.js \
> +	  $$(readlink -f $(abs_top_srcdir)/netx/net/sourceforge/jnlp/resources/pac-funcs.js)
> +
>  # plugin tests
>  
>  if ENABLE_PLUGIN
> @@ -450,12 +460,18 @@
>  	ln -sf $(JAR) $(BOOT_DIR)/bin/jar
>  	ln -sf $(abs_top_builddir)/javac $(BOOT_DIR)/bin/javac
>  	ln -sf $(JAVADOC) $(BOOT_DIR)/bin/javadoc
> +	ln -sf $(JRUNSCRIPT) $(BOOT_DIR)/bin/jrunscript
>  	mkdir -p $(BOOT_DIR)/jre/lib && \
>  	ln -s $(SYSTEM_JDK_DIR)/jre/lib/rt.jar $(BOOT_DIR)/jre/lib && \
>  	if [ -e $(SYSTEM_JDK_DIR)/jre/lib/jsse.jar ] ; then \
>  	  ln -s $(SYSTEM_JDK_DIR)/jre/lib/jsse.jar $(BOOT_DIR)/jre/lib ; \
>  	else \
>  	  ln -s rt.jar $(BOOT_DIR)/jre/lib/jsse.jar ; \
> +	fi && \

No reason to link to this fi command.  That just breaks error reporting.

> +	if [ -e $(SYSTEM_JDK_DIR)/jre/lib/rhino.jar ] ; then \
> +	  ln -s $(SYSTEM_JDK_DIR)/jre/lib/rhino.jar $(BOOT_DIR)/jre/lib ; \
> +	else \
> +	  ln -s rt.jar $(BOOT_DIR)/jre/lib/rhino.jar ; \

This is even more confusing.  First you assume that rhino.jar is going to be in
the JDK.  If it isn't there, you then symlink to rt.jar??? 

>  	fi
>  	ln -sf $(SYSTEM_JDK_DIR)/jre/lib/$(JRE_ARCH_DIR) \
>  	  $(BOOT_DIR)/jre/lib/ && \
> diff -r cd1eda4f0d97 acinclude.m4
> --- a/acinclude.m4	Tue Feb 15 11:01:01 2011 -0500
> +++ b/acinclude.m4	Wed Feb 16 13:59:02 2011 -0500
> @@ -250,6 +250,40 @@
>    AC_SUBST(ECJ_JAR)
>  ])
>  
> +AC_DEFUN([FIND_JRUNSCRIPT],

Missing prefix; should be IT_FIND_JRUNSCRIPT.

> +[
> +  AC_MSG_CHECKING([for jrunscript])
> +  AC_ARG_WITH([jrunscript],
> +              [AS_HELP_STRING(--with-jrunscript,specify location of the jrunscript binary)],
> +  [
> +    if test -f "${withval}"; then
> +      JRUNSCRIPT="${withval}"
> +    fi
> +  ],

You don't handle withval being yes or no, which is valid (generated by --with-jrunscript or
--without-jrunscript).

> +  [
> +    JRUNSCRIPT=${SYSTEM_JDK_DIR}/bin/jrunscript
> +  ])
> +  if test -z "${JRUNSCRIPT}"; then
> +    for dir in /usr/lib/jvm/java-openjdk/bin /usr/lib/jvm/icedtea6/bin \
> +               /usr/lib/jvm/java-6-openjdk/bin /usr/lib/jvm/openjdk/bin \
> +               /usr/lib/jvm/java-icedtea/bin /usr/lib/jvm/java-gcj/bin \
> +               /usr/lib/jvm/gcj-jdk/bin ; do
> +        if test -e $dir/jrunscript; then
> +          JRUNSCRIPT=$dir/jrunscript
> +	  break
> +        fi
> +      done
> +      if test -z "${JRUNSCRIPT}"; then
> +        JRUNSCRIPT=no
> +      fi
> +  fi
> +  AC_MSG_RESULT(${JRUNSCRIPT})
> +  if test "x${JRUNSCRIPT}" = "xno" ; then
> +      AC_MSG_WARN([cannot find a jrunscript, use --with-jrunscript])
> +  fi
> +  AC_SUBST(JRUNSCRIPT)
> +])
> +
>  AC_DEFUN_ONCE([IT_CHECK_PLUGIN],
>  [
>  AC_MSG_CHECKING([whether to build the browser plugin])
> diff -r cd1eda4f0d97 configure.ac
> --- a/configure.ac	Tue Feb 15 11:01:01 2011 -0500
> +++ b/configure.ac	Wed Feb 16 13:59:02 2011 -0500
> @@ -34,6 +34,7 @@
>  FIND_ECJ_JAR
>  IT_FIND_JAVADOC
>  AC_CONFIG_FILES([javac], [chmod +x javac])
> +FIND_JRUNSCRIPT
>  
>  IT_SET_VERSION
>  IT_CHECK_XULRUNNER_VERSION
> @@ -64,6 +65,7 @@
>  IT_CHECK_FOR_CLASS(JAVA_NET_COOKIEMANAGER, [java.net.CookieManager])
>  IT_CHECK_FOR_CLASS(JAVA_NET_HTTPCOOKIE, [java.net.HttpCookie])
>  IT_CHECK_FOR_CLASS(JAVA_NET_COOKIEHANDLER, [java.net.CookieHandler])
> +IT_CHECK_FOR_CLASS(SUN_ORG_MOZILLA_JAVASCRIPT_CONTEXT, [sun.org.mozilla.javascript.Context])

Ugh... we should be making this list shorter not adding to it.
Why is this necessary?  Why can't you use a standard class from the JDK or Rhino?

>  IT_CHECK_FOR_CLASS(SUN_SECURITY_PROVIDER_X509FACTORY, [sun.security.provider.X509Factory])
>  IT_CHECK_FOR_CLASS(SUN_SECURITY_UTIL_SECURITYCONSTANTS, [sun.security.util.SecurityConstants])
>  IT_CHECK_FOR_CLASS(SUN_SECURITY_UTIL_HOSTNAMECHECKER, [sun.security.util.HostnameChecker])
> diff -r cd1eda4f0d97 netx/net/sourceforge/jnlp/browser/BrowserAwareProxySelector.java
> --- a/netx/net/sourceforge/jnlp/browser/BrowserAwareProxySelector.java	Tue Feb 15 11:01:01 2011 -0500
> +++ b/netx/net/sourceforge/jnlp/browser/BrowserAwareProxySelector.java	Wed Feb 16 13:59:02 2011 -0500
> @@ -54,6 +54,7 @@
>  
>  import net.sourceforge.jnlp.runtime.JNLPProxySelector;
>  import net.sourceforge.jnlp.runtime.JNLPRuntime;
> +import net.sourceforge.jnlp.runtime.ProxyAutoConfig;
>  
>  /**
>   * A ProxySelector which can read proxy settings from a browser's
> @@ -85,6 +86,8 @@
>      private String browserSocks4ProxyHost;
>      private int browserSocks4ProxyPort;
>  
> +    private ProxyAutoConfig browserProxyAutoConfig = null;
> +
>      /**
>       * Create a new instance of this class, reading configuration fropm the browser
>       */
> @@ -128,6 +131,12 @@
>              e.printStackTrace();
>          }
>  
> +        if (browserProxyType == BROWSER_PROXY_TYPE_PAC) {
> +            if (browserAutoConfigUrl != null) {
> +                browserProxyAutoConfig = new ProxyAutoConfig(browserAutoConfigUrl);
> +            }
> +        }
> +
>          browserUseSameProxy = Boolean.valueOf(prefs.get("network.proxy.share_proxy_settings"));
>  
>          browserHttpProxyHost = prefs.get("network.proxy.http");
> @@ -216,8 +225,21 @@
>       * browser.
>       */
>      private List<Proxy> getFromBrowserPAC(URI uri) {
> -        System.err.println(R("RPRoxyPacNotImplemented"));
> -        return Arrays.asList(new Proxy[] { Proxy.NO_PROXY });
> +        if (browserAutoConfigUrl == null || uri.getScheme().equals("socket")) {
> +            return Arrays.asList(new Proxy[] { Proxy.NO_PROXY });
> +        }
> +
> +        List<Proxy> proxies = new ArrayList<Proxy>();
> +
> +        try {
> +            String proxiesString = browserProxyAutoConfig.getProxies(uri.toURL());
> +            proxies.addAll(getProxiesFromPacResult(proxiesString));
> +        } catch (MalformedURLException e) {
> +            e.printStackTrace();
> +            proxies.add(Proxy.NO_PROXY);
> +        }
> +
> +        return proxies;
>      }
>  
>      /**
> diff -r cd1eda4f0d97 netx/net/sourceforge/jnlp/resources/Messages.properties
> --- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Tue Feb 15 11:01:01 2011 -0500
> +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Feb 16 13:59:02 2011 -0500
> @@ -141,7 +141,6 @@
>  RUnexpected=Unexpected {0} at {1}
>  RConfigurationError=Fatal error while reading the configuration
>  RConfigurationFatal=ERROR: a fatal error has occurred while loading configuration. Perhaps a global configuration was required but could not be found
> -RPRoxyPacNotImplemented=Using Proxy Auto Config (PAC) files is not supported yet.
>  RProxyFirefoxNotFound=Unable to use Firefox's proxy settings. Using "DIRECT" as proxy type.
>  RProxyFirefoxOptionNotImplemented=Browser proxy option "{0}" ({1}) not supported yet.
>  
> diff -r cd1eda4f0d97 netx/net/sourceforge/jnlp/resources/pac-funcs.js
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/netx/net/sourceforge/jnlp/resources/pac-funcs.js	Wed Feb 16 13:59:02 2011 -0500
> @@ -0,0 +1,830 @@
> +/* pac-funcs.js
> +   Copyright (C) 2011 Red Hat, Inc.
> +
> +This file is part of IcedTea.
> +
> +IcedTea is free software; you can redistribute it and/or
> +modify it under the terms of the GNU General Public License as published by
> +the Free Software Foundation, version 2.
> +

Shouldn't these headers now say IcedTea-Web?

> +IcedTea is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with IcedTea; see the file COPYING.  If not, write to
> +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301 USA.
> +
> +Linking this library statically or dynamically with other modules is
> +making a combined work based on this library.  Thus, the terms and
> +conditions of the GNU General Public License cover the whole
> +combination.
> +
> +As a special exception, the copyright holders of this library give you
> +permission to link this library with independent modules to produce an
> +executable, regardless of the license terms of these independent
> +modules, and to copy and distribute the resulting executable under
> +terms of your choice, provided that you also meet, for each linked
> +independent module, the terms and conditions of the license of that
> +module.  An independent module is a module which is not derived from
> +or based on this library.  If you modify this library, you may extend
> +this exception to your version of the library, but you are not
> +obligated to do so.  If you do not wish to do so, delete this
> +exception statement from your version.
> +*/
> +
> +/*
> + * These helper functions are required to be able to parse Proxy Auto Config
> + * (PAC) files. PAC files will use these helper functions to decide the best
> + * proxy for connecting to a host.
> + *
> + * This implementation is based on the description at:
> + * http://web.archive.org/web/20060424005037/wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html
> + */

Based on?  To what extent? Is any of this code copied?  Is it legal?

> +
> +/**
> + * Returns true if the host does not contain a domain (there are no dots)
> + */
> +function isPlainHostName(host) {
> +    if (host.indexOf(".") === -1) {
> +        return true;
> +    } else {
> +        return false;
> +    }
> +}
> +
> +/**
> + * Returns true if the host is part of the domain (the host ends with domain)
> + */
> +function dnsDomainIs(host, domain) {
> +    var loc = host.lastIndexOf(domain);
> +    if (loc === -1) {
> +        return false;
> +    }
> +    if (loc + domain.length === host.length) {
> +        // host ends with domain
> +        return true;
> +    }
> +    return false;
> +}
> +
> +/**
> + * Returns true if the host is an exact match of hostdom or if host is not a
> + * fully qualified name but has the same hostname as hostdom
> + */
> +function localHostOrDomainIs(host, hostdom) {
> +    if (host === hostdom) {
> +        // exact match
> +        return true;
> +    }
> +    var firstdot = hostdom.indexOf(".");
> +    if (firstdot === -1) {
> +        // hostdom has no dots
> +        return false;
> +    }
> +    if (host === hostdom.substring(0, firstdot)) {
> +        // hostname matches
> +        return true;
> +    }
> +    return false;
> +}
> +
> +/**
> + * Returns true if the host name can be resolved.
> + */
> +function isResolvable(host) {
> +    try {
> +        java.net.InetAddress.getByName(host);
> +        return true;
> +    } catch (e) {
> +        //if (e.javaException instanceof java.net.UnknownHostException) {
> +        	return false;
> +        //} else {
> +        // 	throw e;
> +        //}
> +    }
> +}
> +
> +/**
> + * Return true if the ip address of the host matches the pattern given the mask.
> + */
> +function isInNet(host, pattern, mask) {
> +    if (!isResolvable(host)) {
> +        return false;
> +    }
> +
> +    var hostIp = dnsResolve(host);
> +    var hostParts = hostIp.split(".");
> +    var patternParts = pattern.split(".");
> +    var maskParts = mask.split(".");
> +
> +    if (hostParts.length !== 4 ||
> +        patternParts.length !== hostParts.length ||
> +        maskParts.length !== hostParts.length) {
> +        return false;
> +    }
> +
> +    var matched = true;
> +    for (var i = 0; i < hostParts.length; i++) {
> +        var partMatches = (hostParts[i] & maskParts[i]) === (patternParts[i] & maskParts[i]);
> +        matched = matched && partMatches;
> +    }
> +
> +    return matched;
> +}
> +
> +/**
> + * Returns the IP address of the host as a string
> + */
> +function dnsResolve(host) {
> +    return java.net.InetAddress.getByName(host).getHostAddress() + "";
> +}
> +
> +/**
> + * Returns the local IP address
> + */
> +function myIpAddress() {
> +    return java.net.InetAddress.getLocalHost().getHostAddress() + "";
> +}
> +
> +/**
> + * Returns the number of domains in a hostname
> + */
> +function dnsDomainLevels(host) {
> +    var levels = 0;
> +    for (var i = 0; i < host.length; i++) {
> +        if (host[i] === '.') {
> +            levels++;
> +        }
> +    }
> +    return levels;
> +}
> +
> +/**
> + * Returns true if the shell expression matches the given input string
> + */
> +function shExpMatch(str, shExp) {
> +
> +    // TODO support all special characters
> +    // right now we support only * and ?
> +
> +
> +    try {
> +
> +        // turn shExp into a regular expression
> +
> +        var work = "";
> +
> +        // escape characters
> +        for (var i = 0; i < shExp.length; i++) {
> +            var ch = shExp[i];
> +            switch (ch) {
> +                case "\\": work = work + "\\\\"; break;
> +                case "^": work = work + "\\^"; break;
> +                case "$": work = work + "\\$"; break;
> +                case "+": work = work + "\\+"; break;
> +                case ".": work = work + "\\."; break;
> +                case "(": work = work + "\\("; break;
> +                case ")": work = work + "\\)"; break;
> +                case "{": work = work + "\\{"; break;
> +                case "}": work = work + "\\}"; break;
> +                case "[": work = work + "\\["; break;
> +                case "]": work = work + "\\]"; break;
> +
> +                case "?": work = work + ".{1}"; break;
> +                case "*": work = work + ".*"; break;
> +
> +                default:
> +                    work = work + ch;
> +            }
> +
> +        }
> +
> +        var regExp = "^" +  work + "$";
> +
> +        // match
> +        //java.lang.System.out.println("")
> +        //java.lang.System.out.println("Input String  : " + str);
> +        //java.lang.System.out.println("Input Pattern : " + shExp);
> +        //java.lang.System.out.println("RegExp        : " + regExp.toString());
> +        var match = str.match(regExp);
> +
> +        if (match === null) {
> +            return false;
> +        } else {
> +            return true;
> +        }
> +
> +
> +    } catch (e) {
> +        return false;
> +    }
> +
> +}
> +
> +
> +/**
> + * Returns true if the current weekday matches the desired weekday(s)
> + *
> + * Possible ways of calling:
> + *   weekdayRange(wd1);
> + *   weekdayRange(wd1, "GMT");
> + *   weekdayRange(wd1, wd2);
> + *   weekdayRange(wd1, wd2, "GMT");
> + *
> + * Where wd1 and wd2 are one of "SUN", "MON", "TUE", "WED", "THU", "FRI" and
> + * "SAT"
> + *
> + * The argument "GMT", if present, is always the last argument
> + */
> +function weekdayRange() {
> +	var wd1;
> +	var wd2;
> +	var gmt = false;
> +
> +	function isWeekDay(day) {
> +		if (day === "SUN" || day === "MON" || day === "TUE" || day === "WED" ||
> +            day === "THU" || day === "FRI" || day === "SAT") {
> +			return true;
> +		}
> +		return false;
> +	}
> +
> +    function strToWeekDay(str) {
> +        switch (str) {
> +            case "SUN": return 0;
> +            case "MON": return 1;
> +            case "TUE": return 2;
> +            case "WED": return 3;
> +            case "THU": return 4;
> +            case "FRI": return 5;
> +            case "SAT": return 6;
> +            default: return 0;
> +        }
> +    }
> +
> +	if (arguments.length > 1) {
> +		if (arguments[arguments.length-1] === "GMT") {
> +			gmt = true;
> +            arguments.splice(0,arguments.length-1);
> +        }
> +	}
> +
> +    if (arguments.length === 0) { return false; }
> +
> +    wd1 = arguments[0];
> +
> +	if (!isWeekDay(wd1)) { return false; }
> +
> +    var today = new Date().getDay();
> +    if (arguments.length >= 2) {
> +        // return true if current weekday is between wd1 and wd2 (inclusive)
> +        wd2 = arguments[1];
> +        if (!isWeekDay(wd2)) { return false; }
> +
> +        var day1 = strToWeekDay(wd1);
> +        var day2 = strToWeekDay(wd2);
> +
> +        if (day1 <= day2) {
> +            if ( day1 <= today && today <= day2) {
> +                return true;
> +            }
> +            return false;
> +        } else {
> +            if (day1 <= today || today <= day2) {
> +                return true;
> +            }
> +            return false;
> +        }
> +    } else {
> +        // return true if the current weekday is wd1
> +        if (strToWeekDay(wd1) === today) {
> +            return true;
> +        }
> +        return false;
> +    }
> +}
> +
> +/**
> + * Returns true if the current date matches the given date(s)
> + *
> + * Possible ways of calling:
> + *   dateRange(day)
> + *   dateRange(day1, day2)
> + *   dateRange(month)
> + *   dateRange(month1, month2)
> + *   dateRange(year)
> + *   dateRange(year1, year2)
> + *   dateRange(day1, month1, day2, month2)
> + *   dateRange(month1, year1, month2, year2)
> + *   dateRange(day1, month1, year1, day2, month2, year2)
> + *
> + * The parameter "GMT" may additionally be passed as the last argument in any
> + * of the above ways of calling.
> + */
> +function dateRange() {
> +
> +    // note: watch out for wrapping around of dates. date ranges, like
> +    // month=9 to month=8, wrap around and cover the entire year. this
> +    // makes everything more interesting
> +
> +    var gmt;
> +	if (arguments.length > 1) {
> +		if (arguments[arguments.length-1] === "GMT") {
> +			gmt = true;
> +            arguments.splice(0,arguments.length-1);
> +        }
> +	}
> +
> +    function isDate(date) {
> +        if (typeof(date) === 'number' && (date <= 31 && date >= 1)) {
> +            return true;
> +        }
> +        return false;
> +    }
> +
> +    function strToMonth(month) {
> +        switch (month) {
> +            case "JAN": return 0;
> +            case "FEB": return 1;
> +            case "MAR": return 2;
> +            case "APR": return 3;
> +            case "MAY": return 4;
> +            case "JUN": return 5;
> +            case "JUL": return 6;
> +            case "AUG": return 7;
> +            case "SEP": return 8;
> +            case "OCT": return 9;
> +            case "NOV": return 10;
> +            case "DEC": return 11;
> +            default: return 0;
> +        }
> +    }
> +
> +    function isMonth(month) {
> +        if (month === "JAN" || month === "FEB" || month === "MAR" ||
> +                month === "APR" || month === "MAY" || month === "JUN" ||
> +                month === "JUL" || month === "AUG" || month === "SEP" ||
> +                month === "OCT" || month === "NOV" || month === "DEC") {
> +            return true;
> +        }
> +        return false;
> +    }
> +
> +    function isYear(year) {
> +        if (typeof(year) === 'number') {
> +            return true;
> +        }
> +        return false;
> +    }
> +
> +    function inDateRange(today, date1, date2) {
> +        if (date1 <= date2) {
> +            if (date1 <= today.getDate() && today.getDate() <= date2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            if (date1 <= today.getDate() || today.getDate() <= date2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    function inMonthRange(today, month1, month2) {
> +        if (month1 <= month2) {
> +            if (month1 <= today.getMonth() && today.getMonth() <= month2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            if (month1 <= today.getMonth() || today.getMonth() <= month2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    function inYearRange(today, year1, year2) {
> +        if (year1 <= today.getYear() && today.getYear() <= year2) {
> +            return true;
> +        } else {
> +            return false;
> +        }
> +    }
> +
> +    function inMonthDateRange(today, date1, month1, date2, month2) {
> +        if (month1 === month2) {
> +            if (today.getMonth() === month1) {
> +                if (date1 <= today.getDate() && today.getDate() <= date2) {
> +                    return true;
> +                } else {
> +                    return false;
> +                }
> +            } else {
> +                if (date1 <= date2) {
> +                    return false;
> +                } else {
> +                    return true;
> +                }
> +            }
> +        } else if (month1 < month2) {
> +            if (month1 <= today.getMonth() && today.getMonth() <= month2) {
> +                if (today.getMonth() === month1) {
> +                    if (today.getDate() >= date1) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else if (today.getMonth() === month2) {
> +                    if (today.getDate() <= date2) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else {
> +                    return true;
> +                }
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            if (month1 <= today.getMonth() || today.getMonth() <= month2) {
> +                if (today.getMonth() === month1) {
> +                    if (today.getDate() >= date1) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else if (today.getMonth() === month2) {
> +                    if (today.getDate() <= date2) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else {
> +                    return true;
> +                }
> +            } else {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    function inYearMonthRange(today, month1, year1, month2, year2) {
> +        if (year1 === year2) {
> +            if (today.getYear() === year1) {
> +               if (month1 <= today.getMonth() && today.getMonth() <= month2) {
> +                   return true;
> +               } else {
> +                   return false;
> +               }
> +            } else {
> +                return false;
> +            }
> +        }
> +        if (year1 < year2) {
> +            if (year1 <= today.getYear() && today.getYear() <= year2) {
> +                if (today.getYear() === year1) {
> +                    if (today.getMonth() >= month1) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else if (today.getYear() === year2) {
> +                    if (today.getMonth() <= month2) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else {
> +                    return true;
> +                }
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            return false;
> +        }
> +
> +    }
> +
> +    function inYearMonthDateRange(today, date1, month1, year1, date2, month2, year2) {
> +        if (year1 === year2) {
> +            if (year1 === today.getYear()) {
> +                if ((month1 <= today.getMonth()) && (today.getMonth() <= month2)) {
> +                    if (month1 === month2) {
> +                        if (date1 <= today.getDate() && today.getDate() <= date2) {
> +                            return true;
> +                        } else  {
> +                            return false;
> +                        }
> +                    } else if (today.getMonth() === month1) {
> +                        if (today.getDate() >= date1) {
> +                            return true;
> +                        } else {
> +                            return false;
> +                        }
> +                    } else if (today.getMonth() === month2) {
> +                        if (today.getDate() <= date2) {
> +                            return true;
> +                        } else {
> +                            return false;
> +                        }
> +                    } else  {
> +                        return true;
> +                    }
> +                } else {
> +                    return false;
> +                }
> +            } else {
> +                return false;
> +            }
> +        } else if (year1 < year2) {
> +            if (year1 <= today.getYear() && today.getYear() <= year2) {
> +                if (today.getYear() === year1) {
> +                    if (today.getMonth() === month1) {
> +                        if (today.getDate() >= date1) {
> +                            return true;
> +                        } else {
> +                            return false;
> +                        }
> +                    } else if (today.getMonth() > month1) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else if (today.getYear() === year2) {
> +                    if (today.getMonth() <= month2) {
> +
> +                    } else {
> +                        return true;
> +                    }
> +                } else {
> +                    return true;
> +                }
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            return false;
> +        }
> +    }
> +
> +    // TODO: change date to gmt, whatever
> +    var today = new Date();
> +
> +    var arg1;
> +    var arg2;
> +    var arg3;
> +    var arg4;
> +    var arg5;
> +    var arg6;
> +
> +    switch (arguments.length) {
> +        case 1:
> +            var arg = arguments[0];
> +            if (isDate(arg)) {
> +                if (today.getDate() === arg) {
> +                    return true;
> +                } else {
> +                    return false;
> +                }
> +            } else if (isMonth(arg)) {
> +                if (strToMonth(arg) === today.getMonth()) {
> +                    return true;
> +                } else {
> +                    return false;
> +                }
> +            } else { // year
> +                if (today.getYear() === arg) {
> +                    return true;
> +                } else {
> +                    return false;
> +                }
> +            }
> +        case 2:
> +            arg1 = arguments[0];
> +            arg2 = arguments[1];
> +            if (isDate(arg1) && isDate(arg2)) {
> +                var date1 = arg1;
> +                var date2 = arg2;
> +
> +                return inDateRange(today, date1, date2);
> +
> +            } else if (isMonth(arg1) && isMonth(arg2)) {
> +                var month1 = strToMonth(arg1);
> +                var month2 = strToMonth(arg2);
> +
> +                return inMonthRange(today, month1, month2);
> +
> +            } else if (isYear(arg1) && isYear(arg2)) {
> +                var year1 = arg1;
> +                var year2 = arg2;
> +
> +                return inYearRange(today, year1, year2);
> +            } else {
> +                return false;
> +            }
> +        case 4:
> +            arg1 = arguments[0];
> +            arg2 = arguments[1];
> +            arg3 = arguments[2];
> +            arg4 = arguments[3];
> +
> +            if (isDate(arg1) && isMonth(arg2) && isDate(arg3) && isMonth(arg4)) {
> +                var date1 = arg1;
> +                var month1 = strToMonth(arg2);
> +                var date2 = arg3;
> +                var month2 = strToMonth(arg4);
> +
> +                return inMonthDateRange(today, date1, month1, date2, month2);
> +
> +            } else if (isMonth(arg1) && isYear(arg2) && isMonth(arg3) && isYear(arg4)) {
> +                var month1 = strToMonth(arg1);
> +                var year1 = arg2;
> +                var month2 = strToMonth(arg3);
> +                var year2 = arg4;
> +
> +                return inYearMonthRange(today, month1, year1, month2, year2);
> +            } else {
> +                return false;
> +            }
> +        case 6:
> +            arg1 = arguments[0];
> +            arg2 = arguments[1];
> +            arg3 = arguments[2];
> +            arg4 = arguments[3];
> +            arg5 = arguments[4];
> +            arg6 = arguments[5];
> +            if (isDate(arg1) && isMonth(arg2) && isYear(arg3) &&
> +                isDate(arg4) && isMonth(arg5) && isYear(arg6)) {
> +                var day1 = arg1;
> +                var month1 = strToMonth(arg2);
> +                var year1 = arg3;
> +                var day2 = arg4;
> +                var month2 = strToMonth(arg5);
> +                var year2 = arg6;
> +
> +                return inYearMonthDateRange(today, day1, month1, year1, day2, month2, year2);
> +            } else {
> +                return false;
> +            }
> +        default:
> +            return false;
> +    }
> +
> +}
> +
> +/**
> + * Returns true if the current time matches the range given
> + *
> + * timeRange(hour)
> + * timeRange(hour1, hour2)
> + * timeRange(hour1, min1, hour2, min2)
> + * timeRange(hour1, min1, sec1, hour2, min2, sec2)
> + *
> + * The string "GMT" can be used as the last additional parameter in addition to
> + * the methods listed above.
> + */
> +function timeRange() {
> +
> +    // watch out for wrap around of times
> +
> +    var gmt;
> +	if (arguments.length > 1) {
> +		if (arguments[arguments.length-1] === "GMT") {
> +			gmt = true;
> +            arguments.splice(0,arguments.length-1);
> +        }
> +	}
> +
> +    function isHour(hour) {
> +        if (typeof(hour) === "number" && ( 0 <= hour && hour <= 23)) {
> +            return true;
> +        } else {
> +            return false;
> +        }
> +    }
> +
> +    function isMin(minute) {
> +        if (typeof(minute) === "number" && (0 <= minute && minute <= 59)) {
> +            return true;
> +        } else {
> +            return false;
> +        }
> +    }
> +
> +    function inHourRange(now, hour1, hour2) {
> +        if (hour1 === hour2) {
> +            if (now.getHours() === hour1) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        } else if (hour1 < hour2) {
> +           if (hour1 <= now.getHours() && now.getHours() <= hour2) {
> +                return true;
> +           } else {
> +               return false;
> +           }
> +        } else {
> +            if (hour1 <= now.getHours() || now.getHours() <= hour2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    function inHourMinuteRange(now, hour1, min1, hour2, min2) {
> +        if (hour1 == hour2) {
> +            if (now.getHours() == hour1) {
> +                if (min1 <= min2) {
> +                    if (min1 <= now.getMinutes() && now.getMinutes() <= min2) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                } else {
> +                    if (min1 <= now.getMinutes() || now.getMinutes() <= min2) {
> +                        return true;
> +                    } else {
> +                        return false;
> +                    }
> +                }
> +            } else {
> +                if (min1 <= min2) {
> +                    return false;
> +                } else {
> +                    return true;
> +                }
> +            }
> +        } else if (hour1 < hour2) {
> +            if (hour1 <= now.getHours() && now.getHours() <= hour2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        } else {
> +            if (hour1 <= now.getHours() || now.getHours() <= hour2) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        }
> +    }
> +
> +    var today = new Date();
> +
> +    switch (arguments.length) {
> +        case 1:
> +            var hour = arguments[0];
> +            if (today.getHours() === hour) {
> +                return true;
> +            } else {
> +                return false;
> +            }
> +        case 2:
> +            var hour1 = arguments[0];
> +            var hour2 = arguments[1];
> +            if (isHour(hour1) && isHour(hour2)) {
> +                return inHourRange(today, hour1, hour2);
> +            } else {
> +                return false;
> +            }
> +        case 4:
> +            var hour1 = arguments[0];
> +            var min1 = arguments[1];
> +            var hour2 = arguments[2];
> +            var min2 = arguments[3];
> +
> +            if (isHour(hour1) && isMin(min1) && isHour(hour2) && isMin(min2)) {
> +                return inHourMinuteRange(today, hour1, min1, hour2, min2);
> +            } else {
> +                return false;
> +            }
> +
> +        case 6:
> +            var hour1 = arguments[0];
> +            var min1 = arguments[1];
> +            var sec1 = arguments[2];
> +            var hour2 = arguments[3];
> +            var min2 = arguments[4];
> +            var sec2 = arguments[5];
> +
> +            // TODO handle seconds properly
> +
> +            return inHourMinuteRange(today, hour1, min1, hour2, min2);
> +        default:
> +            return false;
> +    }
> +}
> +
> diff -r cd1eda4f0d97 netx/net/sourceforge/jnlp/runtime/JNLPProxySelector.java
> --- a/netx/net/sourceforge/jnlp/runtime/JNLPProxySelector.java	Tue Feb 15 11:01:01 2011 -0500
> +++ b/netx/net/sourceforge/jnlp/runtime/JNLPProxySelector.java	Wed Feb 16 13:59:02 2011 -0500
> @@ -16,7 +16,6 @@
>  
>  package net.sourceforge.jnlp.runtime;
>  
> -import static net.sourceforge.jnlp.runtime.Translator.R;
>  import java.io.IOException;
>  import java.net.InetAddress;
>  import java.net.InetSocketAddress;
> @@ -52,6 +51,8 @@
>      /** The default port to use as a fallback. Currently squid's default port */
>      public static final int FALLBACK_PROXY_PORT = 3128;
>  
> +    private ProxyAutoConfig proxyAutoConfig = null;
> +
>      /** The proxy type. See PROXY_TYPE_* constants */
>      private int proxyType = PROXY_TYPE_UNKNOWN;
>  
> @@ -96,8 +97,7 @@
>  
>          proxyType = Integer.valueOf(config.getProperty(DeploymentConfiguration.KEY_PROXY_TYPE));
>  
> -        String autoConfigString = config
> -                .getProperty(DeploymentConfiguration.KEY_PROXY_AUTO_CONFIG_URL);
> +        String autoConfigString = config.getProperty(DeploymentConfiguration.KEY_PROXY_AUTO_CONFIG_URL);
>          if (autoConfigString != null) {
>              try {
>                  autoConfigUrl = new URL(autoConfigString);
> @@ -106,6 +106,10 @@
>              }
>          }
>  
> +        if (autoConfigUrl != null) {
> +            proxyAutoConfig = new ProxyAutoConfig(autoConfigUrl);
> +        }
> +
>          bypassList = new ArrayList<String>();
>          String proxyBypass = config.getProperty(DeploymentConfiguration.KEY_PROXY_BYPASS_LIST);
>          if (proxyBypass != null) {
> @@ -333,14 +337,22 @@
>       *
>       * @return a List of valid Proxy objects
>       */
> -    private List<Proxy> getFromPAC(URI uri) {
> -        if (autoConfigUrl == null) {
> +    protected List<Proxy> getFromPAC(URI uri) {
> +        if (autoConfigUrl == null || uri.getScheme().equals("socket")) {
>              return Arrays.asList(new Proxy[] { Proxy.NO_PROXY });
>          }
> -        // TODO implement this by reading and using the PAC file
> -        System.err.println(R("RPRoxyPacNotImplemented"));
>  
> -        return Arrays.asList(new Proxy[] { Proxy.NO_PROXY });
> +        List<Proxy> proxies = new ArrayList<Proxy>();
> +
> +        try {
> +            String proxiesString = proxyAutoConfig.getProxies(uri.toURL());
> +            proxies.addAll(getProxiesFromPacResult(proxiesString));
> +        } catch (MalformedURLException e) {
> +            e.printStackTrace();
> +            proxies.add(Proxy.NO_PROXY);
> +        }
> +
> +        return proxies;

This appears to be the same as the method in getFromBrowserPAC.  Could this code not be shared?

>      }
>  
>      /**
> @@ -351,5 +363,56 @@
>       */
>      protected abstract List<Proxy> getFromBrowser(URI uri);
>  
> +    /**
> +     * Converts a proxy string from a browser into a List of Proxy objects
> +     * suitable for java.
> +     * @param pacString a string indicating proxies. For example
> +     * "PROXY foo.bar:3128; DIRECT"
> +     * @return a list of Proxy objects represeting the parsed string.
> +     */
> +    public static List<Proxy> getProxiesFromPacResult(String pacString) {
> +        List<Proxy> proxies = new ArrayList<Proxy>();
> +
> +        String[] tokens = pacString.split(";");
> +        for (String token: tokens) {
> +            if (token.startsWith("PROXY")) {
> +                String hostPortPair = token.substring("PROXY".length()).trim();
> +                if (!hostPortPair.contains(":")) {
> +                    continue;
> +                }
> +                String host = hostPortPair.split(":")[0];
> +                int port;
> +                try {
> +                    port = Integer.valueOf(hostPortPair.split(":")[1]);
> +                } catch (NumberFormatException nfe) {
> +                    continue;
> +                }
> +                SocketAddress sa = new InetSocketAddress(host, port);
> +                proxies.add(new Proxy(Type.HTTP, sa));
> +            } else if (token.startsWith("SOCKS")) {
> +                String hostPortPair = token.substring("SOCKS".length()).trim();
> +                if (!hostPortPair.contains(":")) {
> +                    continue;
> +                }
> +                String host = hostPortPair.split(":")[0];
> +                int port;
> +                try {
> +                    port = Integer.valueOf(hostPortPair.split(":")[1]);
> +                } catch (NumberFormatException nfe) {
> +                    continue;
> +                }
> +                SocketAddress sa = new InetSocketAddress(host, port);
> +                proxies.add(new Proxy(Type.SOCKS, sa));
> +            } else if (token.startsWith("DIRECT")) {
> +                proxies.add(Proxy.NO_PROXY);
> +            } else {
> +                if (JNLPRuntime.isDebug()) {
> +                    System.out.println("Unrecognized proxy token: " + token);
> +                }
> +            }
> +        }
> +
> +        return proxies;
> +    }
>  
>  }
> diff -r cd1eda4f0d97 netx/net/sourceforge/jnlp/runtime/ProxyAutoConfig.java
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/netx/net/sourceforge/jnlp/runtime/ProxyAutoConfig.java	Wed Feb 16 13:59:02 2011 -0500
> @@ -0,0 +1,252 @@
> +/* ProxyAutoConfig.java
> +   Copyright (C) 2011 Red Hat, Inc.
> +
> +This file is part of IcedTea.
> +
> +IcedTea is free software; you can redistribute it and/or
> +modify it under the terms of the GNU General Public License as published by
> +the Free Software Foundation, version 2.
> +
> +IcedTea is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with IcedTea; see the file COPYING.  If not, write to
> +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301 USA.
> +
> +Linking this library statically or dynamically with other modules is
> +making a combined work based on this library.  Thus, the terms and
> +conditions of the GNU General Public License cover the whole
> +combination.
> +
> +As a special exception, the copyright holders of this library give you
> +permission to link this library with independent modules to produce an
> +executable, regardless of the license terms of these independent
> +modules, and to copy and distribute the resulting executable under
> +terms of your choice, provided that you also meet, for each linked
> +independent module, the terms and conditions of the license of that
> +module.  An independent module is a module which is not derived from
> +or based on this library.  If you modify this library, you may extend
> +this exception to your version of the library, but you are not
> +obligated to do so.  If you do not wish to do so, delete this
> +exception statement from your version.
> +*/
> +
> +package net.sourceforge.jnlp.runtime;
> +
> +import java.io.BufferedReader;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.net.SocketPermission;
> +import java.net.URL;
> +import java.security.AccessControlContext;
> +import java.security.AccessController;
> +import java.security.Permissions;
> +import java.security.PrivilegedAction;
> +import java.security.ProtectionDomain;
> +
> +import net.sourceforge.jnlp.util.TimedHashMap;

> +import sun.org.mozilla.javascript.Context;
> +import sun.org.mozilla.javascript.Function;
> +import sun.org.mozilla.javascript.Scriptable;

Please don't add more sun.* dependencies.

> +
> +/**
> + * Represents a ProxyAutoConfig file. This object can be used to evaluate the
> + * proxy file to find the proxy for a given url.
> + *
> + * @see http://en.wikipedia.org/wiki/Proxy_auto-config#The_PAC_file
> + */
> +public class ProxyAutoConfig {
> +
> +    private final String pacHelperFunctionContents;
> +    private final String pacContents;
> +    private final URL pacUrl;
> +    private final TimedHashMap<String, String> cache;
> +
> +    /**
> +     * Initialize a new object by using the PAC file located at the given URL.
> +     *
> +     * @param pacUrl the url of the PAC file to use
> +     */
> +    public ProxyAutoConfig(URL pacUrl) {
> +        pacHelperFunctionContents = getHelperFunctionContents();
> +        this.pacUrl = pacUrl;
> +        pacContents = getPacContents(pacUrl);
> +        cache = new TimedHashMap<String, String>();
> +    }
> +
> +    /**
> +     * 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
> +     * <pre>"PROXY foo.example.com:8080; PROXY bar.example.com:8080; DIRECT"</pre>
> +     *
> +     * @see #getProxiesWithoutCaching(URL)
> +     */
> +    public String getProxies(URL url) {
> +        String cachedResult = getFromCache(url);
> +        if (cachedResult != null) {
> +            return cachedResult;
> +        }
> +
> +        String result = getProxiesWithoutCaching(url);
> +        addToCache(url, cachedResult);
> +        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
> +     * <pre>"PROXY example.com:3128; DIRECT"</pre>
> +     *
> +     * @see #getProxies(URL)
> +     */
> +    public String getProxiesWithoutCaching(URL url) {
> +        if (pacHelperFunctionContents == null) {
> +            System.err.println("Error loading pac functions");
> +            return "DIRECT";
> +        }
> +
> +        EvaluatePacAction evaluatePacAction = new EvaluatePacAction(pacContents, pacUrl.toString(),
> +                pacHelperFunctionContents, url);
> +        Permissions p = new Permissions();
> +        p.add(new RuntimePermission("accessClassInPackage.sun.org.mozilla.javascript"));
> +        p.add(new SocketPermission("*", "resolve"));
> +        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;
> +            BufferedReader pacReader = new BufferedReader(new InputStreamReader(pacUrl.openStream()));
> +            contents = new StringBuilder();
> +            while ((line = pacReader.readLine()) != null) {
> +                // System.out.println(line);
> +                contents = contents.append(line).append("\n");
> +            }
> +        } 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/resources/pac-funcs.js");
> +            BufferedReader pacFuncsReader = new BufferedReader(new InputStreamReader(in));
> +            contents = new StringBuilder();
> +            while ((line = pacFuncsReader.readLine()) != null) {
> +                // System.out.println(line);
> +                contents = contents.append(line).append("\n");
> +            }
> +        } catch (IOException e) {
> +            e.printStackTrace();
> +            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<String> {
> +
> +        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)) {
> +                    System.err.println("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) {
> +                e.printStackTrace();
> +                return "DIRECT";
> +            } finally {
> +                Context.exit();
> +            }
> +        }
> +    }
> +
> +}
> diff -r cd1eda4f0d97 tests/netx/pac/pac-funcs-test.js
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/netx/pac/pac-funcs-test.js	Wed Feb 16 13:59:02 2011 -0500
> @@ -0,0 +1,446 @@
> +
> +var ICEDTEA_CLASSPATH_ORG_IP = "208.78.240.231";
> +var CLASSPATH_ORG_IP = "199.232.41.10";

These should be able to be overridden.  Same with others below.

> +
> +var testsFailed = 0;
> +var testsPassed = 0;
> +
> +print("loading needed files\n");
> +file = arguments[0] + "";
> +load(file)
> +print("finished loaded needed files\n");
> +
> +
> +function main() {
> +
> +	testIsPlainHostName();
> +	testDnsDomainIs();
> +	testLocalHostOrDomainIs();
> +	testIsResolvable();
> +	testIsInNet();
> +	testDnsResolve();
> +	testDnsDomainLevels();
> +	testShExpMatch();
> +	testWeekdayRange();
> +	testDateRange();
> +	testTimeRange();
> +
> +	java.lang.System.out.println(testsFailed + " of " + (testsFailed + testsPassed) + " tests failed");
> +}
> +
> +function runTests(name, tests) {
> +	java.lang.System.out.println("Testing: " + name.name);
> +
> +	var undefined_var;
> +
> +	for ( var i = 0; i < tests.length; i++) {
> +
> +		java.lang.System.out.print("Test " + (i + 1) + ": ");
> +		var expectedVal = tests[i][0];
> +		var args = tests[i].slice(1);
> +		var returnVal;
> +//		try {
> +			returnVal = name.apply(null, args);
> +//		} catch (e) {
> +//			returnVal = undefined_var;
> +//		}
> +		if (returnVal === expectedVal) {
> +			java.lang.System.out.println("Passed.");
> +			testsPassed++;
> +		} else {
> +			java.lang.System.out.println("FAILED.");
> +            java.lang.System.out.println(name.name + "(" + args.join(", ") + ")");
> +			java.lang.System.out.println("Expected '" + expectedVal + "' but got '" + returnVal + "'");
> +			testsFailed++;
> +		}
> +	}
> +}
> +
> +function testIsPlainHostName() {
> +    var tests = [
> +        [ false, "icedtea.classpath.org" ],
> +        [ false, "classpath.org" ],
> +        [ true, "org" ],
> +        [ true, "icedtea" ],
> +        [ false, ".icedtea.classpath.org" ],
> +        [ false, "icedtea." ],
> +        [ false, "icedtea.classpath." ]
> +    ];
> +
> +    runTests(isPlainHostName, tests);
> +}
> +
> +function testDnsDomainIs() {
> +    var tests = [
> +        [ true, "icedtea.classpath.org", "icedtea.classpath.org" ],
> +        [ true, "icedtea.classpath.org", ".classpath.org" ],
> +        [ true, "icedtea.classpath.org", ".org" ],
> +        [ false, "icedtea.classpath.org", "icedtea.classpath.com" ],
> +        [ false, "icedtea.classpath.org", "icedtea.classpath" ],
> +        [ false, "icedtea.classpath", "icedtea.classpath.org" ],
> +        [ false, "icedtea", "icedtea.classpath.org" ]
> +    ];
> +
> +    runTests(dnsDomainIs, tests);
> +}
> +
> +function testLocalHostOrDomainIs() {
> +
> +    var tests = [
> +        [ true, "icedtea.classpath.org", "icedtea.classpath.org" ],
> +        [ true, "icedtea", "icedtea.classpath.org" ],
> +        [ false, "icedtea.classpath.org", "icedtea.classpath.com" ],
> +        [ false, "icedtea.classpath", "icedtea.classpath.org" ],
> +        [ false, "foo.classpath.org", "icedtea.classpath.org" ],
> +        [ false, "foo", "icedtea.classpath.org" ]
> +    ];
> +
> +    runTests(localHostOrDomainIs, tests);
> +}
> +
> +function testIsResolvable() {
> +
> +    var tests = [
> +        [ true, "icedtea.classpath.org", "icedtea.classpath.org" ],
> +        [ true, "classpath.org" ],
> +        [ false, "icedtea" ],
> +        [ false, "foobar.classpath.org" ],
> +        [ false, "icedtea.classpath.com" ]
> +    ];
> +
> +    runTests(isResolvable, tests);
> +}
> +
> +function testIsInNet() {
> +
> +	var parts = ICEDTEA_CLASSPATH_ORG_IP.split("\.");
> +
> +	var fakeParts = ICEDTEA_CLASSPATH_ORG_IP.split("\.");
> +	fakeParts[0] = fakeParts[0] + 1;
> +
> +	function createIp(array) {
> +		return array[0] + "." + array[1] + "." + array[2] + "." + array[3];
> +	}
> +
> +	var tests = [
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "255.255.255.255"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "255.255.255.0"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "255.255.0.0"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "255.0.0.0"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "0.0.0.0"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "255.255.255.255"],
> +	    [ true, "icedtea.classpath.org", ICEDTEA_CLASSPATH_ORG_IP, "0.0.0.0"],
> +	    [ true, "icedtea.classpath.org", createIp(parts), "255.255.255.255" ],
> +	    [ false, "icedtea.classpath.org", createIp(fakeParts), "255.255.255.255"],
> +	    [ false, "icedtea.classpath.org", createIp(fakeParts), "255.255.255.0"],
> +	    [ false, "icedtea.classpath.org", createIp(fakeParts), "255.255.0.0"],
> +	    [ false, "icedtea.classpath.org", createIp(fakeParts), "255.0.0.0"],
> +	    [ true, "icedtea.classpath.org", createIp(fakeParts), "0.0.0.0"]
> +	];
> +
> +	runTests(isInNet, tests);
> +}
> +
> +function testDnsResolve() {
> +    var tests = [
> +        [ ICEDTEA_CLASSPATH_ORG_IP, "icedtea.classpath.org" ],
> +        //[ CLASSPATH_ORG_IP, "classpath.org" ],
> +        [ "127.0.0.1", "localhost" ]
> +    ];
> +
> +    runTests(dnsResolve, tests);
> +}
> +
> +function testDnsDomainLevels() {
> +    var tests = [
> +        [ 0, "org" ],
> +        [ 1, "classpath.org" ],
> +        [ 2, "icedtea.classpath.org" ],
> +        [ 3, "foo.icedtea.classpath.org" ]
> +    ];
> +
> +    runTests(dnsDomainLevels, tests);
> +
> +}
> +function testShExpMatch() {
> +    var tests = [
> +         [ true, "icedtea.classpath.org", "icedtea.classpath.org"],
> +         [ false, "icedtea.classpath.org", ".org"],
> +         [ false, "icedtea.classpath.org", "icedtea."],
> +         [ false, "icedtea", "icedtea.classpath.org"],
> +
> +         [ true, "icedtea.classpath.org", "*" ],
> +         [ true, "icedtea.classpath.org", "*.classpath.org" ],
> +         [ true, "http://icedtea.classpath.org", "*.classpath.org" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*.classpath.org/*" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*/foobar/*" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*foobar*" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*foo*" ],
> +         [ false, "http://icedtea.classpath.org/foobar/", "*/foo/*" ],
> +         [ false, "http://icedtea.classpath.org/foobar/", "*/foob/*" ],
> +         [ false, "http://icedtea.classpath.org/foobar/", "*/fooba/*" ],
> +         [ false, "http://icedtea.classpath.org/foo/", "*foobar*" ],
> +
> +         [ true, "1", "?" ],
> +         [ true, "12", "??" ],
> +         [ true, "123", "1?3" ],
> +         [ true, "123", "?23" ],
> +         [ true, "123", "12?" ],
> +         [ true, "1234567890", "??????????" ],
> +         [ false, "1234567890", "?????????" ],
> +         [ false, "123", "1?1" ],
> +         [ false, "123", "??" ],
> +
> +         [ true, "http://icedtea.classpath.org/f1/", "*/f?/*" ],
> +         [ true, "http://icedtea1.classpath.org/f1/", "*icedtea?.classpath*/f?/*" ],
> +         [ false, "http://icedtea.classpath.org/f1/", "*/f2/*" ],
> +         [ true, "http://icedtea.classpath.org/f1/", "*/f?/*" ],
> +         [ false, "http://icedtea.classpath.org/f1", "f?*"],
> +         [ false, "http://icedtea.classpath.org/f1", "f?*"],
> +         [ false, "http://icedtea.classpath.org/f1", "f?*"],
> +
> +         [ true, "http://icedtea.classpath.org/foobar/", "*.classpath.org/*" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*.classpath.org/*" ],
> +         [ true, "http://icedtea.classpath.org/foobar/", "*.classpath.org/*" ],
> +
> +         [ true, "http://icedtea.classpath.org/foo.php?id=bah", "*foo.php*" ],
> +         [ false, "http://icedtea.classpath.org/foo_php?id=bah", "*foo.php*" ]
> +     ];
> +
> +     runTests(shExpMatch, tests);
> +}
> +
> +function testWeekdayRange() {
> +
> +    var today = new Date();
> +    var day = today.getDay();
> +
> +    function dayToStr(day) {
> +        switch (day) {
> +            case -2: return "FRI";
> +            case -1: return "SAT";
> +            case 0: return "SUN";
> +            case 1: return "MON";
> +            case 2: return "TUE";
> +            case 3: return "WED";
> +            case 4: return "THU";
> +            case 5: return "FRI";
> +            case 6: return "SAT";
> +            case 7: return "SUN";
> +            case 8: return "MON";
> +            default: return "FRI";
> +        }
> +
> +    }
> +
> +    var tests = [
> +       [ true, dayToStr(day) ],
> +       [ false, dayToStr(day+1) ],
> +       [ false, dayToStr(day-1) ],
> +    ];
> +}
> +
> +function testDateRange() {
> +
> +    function incDate(date) {
> +        return (date + 1 - 1) % 31 +1 ;
> +    }
> +
> +    function decDate(date) {
> +        return (date - 1 - 1 + 31) % 31 + 1;
> +    }
> +
> +    function monthToStr(month) {
> +        switch (month) {
> +            case -1: return "DEC";
> +            case 0: return "JAN";
> +            case 1: return "FEB";
> +            case 2: return "MAR";
> +            case 3: return "APR";
> +            case 4: return "MAY";
> +            case 5: return "JUN";
> +            case 6: return "JUL";
> +            case 7: return "AUG";
> +            case 8: return "SEP";
> +            case 9: return "OCT";
> +            case 10: return "NOV";
> +            case 11: return "DEC";
> +            case 12: return "JAN";
> +            default: throw "Invalid Month";
> +        }
> +    }
> +
> +    var today = new Date();
> +    var date = today.getDate();
> +    var month = today.getMonth();
> +    var year = today.getYear();
> +
> +    var tests = [
> +        [ true, date ],
> +        [ false, incDate(date) ],
> +        [ false, decDate(date) ],
> +
> +        [ true, monthToStr(month) ],
> +        [ false, monthToStr(month+1) ],
> +        [ false, monthToStr(month-1) ],
> +
> +        [ true, year ],
> +        [ false, year - 1],
> +        [ false, year + 1],
> +
> +        [ true, date, date ],
> +        [ true, date, incDate(date) ],
> +        [ true, decDate(date), date ],
> +        [ true, decDate(date), incDate(date) ],
> +        [ false, incDate(date), decDate(date) ],
> +        [ false, decDate(decDate(date)), decDate(date) ],
> +        [ false, incDate(date), incDate(incDate(date)) ],
> +
> +        [ true, monthToStr(month), monthToStr(month) ],
> +        [ true, monthToStr(month), monthToStr(month+1) ],
> +        [ true, monthToStr(month-1), monthToStr(month) ],
> +        [ true, monthToStr(month-1), monthToStr(month+1) ],
> +        [ true, "JAN", "DEC" ],
> +        [ true, "DEC", "NOV" ],
> +        [ true, "JUL", "JUN"],
> +        [ false, monthToStr(month+1), monthToStr(month+1) ],
> +        [ false, monthToStr(month-1), monthToStr(month-1) ],
> +        [ false, monthToStr(month+1), monthToStr(month-1) ],
> +
> +        [ true, year, year ],
> +        [ true, year, year+1 ],
> +        [ true, year-1, year ],
> +        [ true, year-1, year+1 ],
> +        [ false, year-2, year-1 ],
> +        [ false, year+1, year+1 ],
> +        [ false, year+1, year+2 ],
> +        [ false, year+1, year-1 ],
> +
> +        [ true, date, monthToStr(month) , date, monthToStr(month) ],
> +        [ true, decDate(date), monthToStr(month) , date, monthToStr(month) ],
> +        [ false, decDate(date), monthToStr(month) , decDate(date), monthToStr(month) ],
> +        [ true, date, monthToStr(month) , incDate(date), monthToStr(month) ],
> +        [ false, incDate(date), monthToStr(month) , incDate(date), monthToStr(month) ],
> +        [ true, decDate(date), monthToStr(month) , incDate(date), monthToStr(month) ],
> +        [ false, incDate(date), monthToStr(month) , decDate(date), monthToStr(month) ],
> +        [ true, date, monthToStr(month-1) , date, monthToStr(month) ],
> +        [ true, date, monthToStr(month) , date, monthToStr(month+1) ],
> +        [ true, date, monthToStr(month-1) , date, monthToStr(month+1) ],
> +        [ true, incDate(date), monthToStr(month-1) , date, monthToStr(month+1) ],
> +        [ true, date, monthToStr(month-1) , decDate(date), monthToStr(month+1) ],
> +        [ false, date, monthToStr(month+1) , date, monthToStr(month-1) ],
> +        [ false, incDate(date), monthToStr(month+1) , incDate(date), monthToStr(month-1) ],
> +        [ false, decDate(date), monthToStr(month+1) , decDate(date), monthToStr(month-1) ],
> +        [ true, 1, "JAN", 31, "DEC" ],
> +        [ true, 2, "JAN", 1, "JAN" ],
> +        [ false, 1, monthToStr(month+1), 31, monthToStr(month+1) ],
> +        [ false, 1, monthToStr(month-1), 31, monthToStr(month-1) ],
> +
> +        [ true, monthToStr(month), year, monthToStr(month), year ],
> +        [ true, monthToStr(month-1), year, monthToStr(month), year ],
> +        [ true, monthToStr(month), year, monthToStr(month+1), year ],
> +        [ true, monthToStr(month-1), year, monthToStr(month+1), year ],
> +        [ true, monthToStr(0), year, monthToStr(11), year ],
> +        [ false, monthToStr(month+1), year, monthToStr(month-1), year ],
> +        [ false, monthToStr(month+1), year, monthToStr(month+1), year ],
> +        [ false, monthToStr(month-1), year, monthToStr(month-1), year ],
> +        [ false, monthToStr(month), year-1, monthToStr(month-1), year ],
> +        [ true, monthToStr(month), year, monthToStr(month), year + 1 ],
> +        [ true, monthToStr(month), year-1, monthToStr(month), year ],
> +        [ true, monthToStr(month), year-1, monthToStr(month), year+1 ],
> +        [ true, monthToStr(0), year, monthToStr(0), year+1 ],
> +        [ true, monthToStr(0), year-1, monthToStr(0), year+1 ],
> +        [ false, monthToStr(0), year-1, monthToStr(11), year-1 ],
> +        [ false, monthToStr(0), year+1, monthToStr(11), year+1 ],
> +
> +        [ true, date, monthToStr(month), year, date, monthToStr(month), year ],
> +        [ true, decDate(date), monthToStr(month), year, incDate(date), monthToStr(month), year ],
> +        [ true, decDate(date), monthToStr(month-1), year, incDate(date), monthToStr(month+1), year ],
> +        [ true, decDate(date), monthToStr(month-1), year-1, incDate(date), monthToStr(month+1), year+1 ],
> +        [ true, incDate(date), monthToStr(month-1), year-1, incDate(date), monthToStr(month+1), year+1 ],
> +        [ false, incDate(date), monthToStr(month), year, incDate(date), monthToStr(month+1), year+1 ],
> +        [ false, date, monthToStr(month+1), year, incDate(date), monthToStr(month+1), year+1 ],
> +        [ true, 1, monthToStr(0), 0, 31, monthToStr(11), 100000 ],
> +        [ true, 1, monthToStr(0), year, 31, monthToStr(11), year ],
> +        [ true, 1, monthToStr(0), year-1, 31, monthToStr(11), year+1 ],
> +        [ false, 1, monthToStr(0), year-1, 31, monthToStr(11), year-1 ],
> +        [ false, 1, monthToStr(0), year+1, 31, monthToStr(11), year+1 ],
> +
> +    ];
> +
> +    runTests(dateRange, tests);
> +
> +}
> +
> +function testTimeRange() {
> +    var now = new Date();
> +
> +    var hour = now.getHours();
> +    var min = now.getMinutes();
> +    var sec = now.getSeconds();
> +
> +    function toHour(input) {
> +        if (input < 0) {
> +            while (input < 0) {
> +                input = input + 24;
> +            }
> +            return (input % 24);
> +        } else {
> +            return (input % 24);
> +        }
> +    }
> +
> +    function toMin(input) {
> +        if (input < 0) {
> +            while (input < 0) {
> +                input = input + 60;
> +            }
> +            return (input % 60);
> +        } else {
> +            return (input % 60);
> +        }
> +    }
> +
> +    tests = [
> +        [ true, hour ],
> +        [ false, toHour(hour+1)],
> +        [ false, toHour(hour-1)],
> +
> +        [ true, hour, hour ],
> +        [ true, toHour(hour-1), hour ],
> +        [ true, hour, toHour(hour+1)],
> +        [ true, toHour(hour-1), toHour(hour+1)],
> +        [ true, toHour(hour+1), hour ],
> +        [ true, hour, toHour(hour-1) ],
> +        [ false, toHour(hour-2), toHour(hour-1)],
> +        [ false, toHour(hour+1), toHour(hour+2)],
> +        [ false, toHour(hour+1), toHour(hour-1) ],
> +        [ true, 0, 23 ],
> +        [ true, 12, 11 ],
> +
> +        [ true, hour, min, hour, min ],
> +        [ true, hour, min, hour, toMin(min+1) ],
> +        [ true, hour, toMin(min-1), hour, min ],
> +        [ true, hour, toMin(min-1), hour, toMin(min+1) ],
> +        [ true, hour, toMin(min+2), hour, toMin(min+1) ],
> +        [ false, hour, toMin(min+1), hour, toMin(min+1) ],
> +        [ false, hour, toMin(min-1), hour, toMin(min-1) ],
> +        [ false, hour, toMin(min+1), hour, toMin(min-1) ],
> +        [ true, toHour(hour-1), min, hour, min ],
> +        [ true, hour, min, toHour(hour+1), min ],
> +        [ true, toHour(hour-1), min, toHour(hour+1), min ],
> +        [ true, 0, 0, 23, 59 ],
> +        [ true, 0, 1, 0, 0 ],
> +
> +        [ true, 0, 1, 0, 0, 0, 0 ],
> +        [ true, hour, min, sec, hour, min, sec ],
> +        [ true, hour, min, sec, hour, min + 10, sec ],
> +        [ true, hour, min, sec - 10, hour, min, sec ],
> +        [ true, hour, min, sec, hour, min-1 , sec ],
> +
> +    ];
> +
> +    runTests(timeRange, tests);
> +}
> +
> +main();

Needs work.
-- 
Andrew :)

Free Java Software Engineer
Red Hat, Inc. (http://www.redhat.com)

Support Free Java!
Contribute to GNU Classpath and IcedTea
http://www.gnu.org/software/classpath
http://icedtea.classpath.org
PGP Key: F5862A37 (https://keys.indymedia.org/)
Fingerprint = EA30 D855 D50F 90CD F54D  0698 0713 C3ED F586 2A37



More information about the distro-pkg-dev mailing list