You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ma...@apache.org on 2017/11/12 12:30:16 UTC
[incubator-netbeans] branch master updated: [NETBEANS-96] New PAC
Script evaluation environment
This is an automated email from the ASF dual-hosted git repository.
matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new e899a7c [NETBEANS-96] New PAC Script evaluation environment
new 2dc9605 [NETBEANS-96] New PAC Script evaluation environment
e899a7c is described below
commit e899a7cc7bc2fd450b1b189fb84e249516e03c96
Author: Lars Bruun-Hansen <lb...@gmail.com>
AuthorDate: Sat Oct 21 09:32:05 2017 +0200
[NETBEANS-96] New PAC Script evaluation environment
Implements a completely new PAC script evaluation
environment with lots of bug fixes, pluggable and
with more transparency.
---
core.network/apichanges.xml | 77 ++
core.network/arch.xml | 1251 ++++++++++++++++++++
core.network/external/binaries-list | 17 -
.../nsProxyAutoConfig-mozilla2.0-license.txt | 478 --------
core.network/manifest.mf | 2 +-
core.network/nbproject/project.properties | 6 +-
core.network/nbproject/project.xml | 27 +-
.../netbeans/core/network/proxy/Bundle.properties | 4 +-
.../core/network/proxy/ProxyAutoConfig.java | 247 +---
.../core/network/proxy/pac/PacHelperMethods.java | 48 +
.../proxy/pac/PacHelperMethodsMicrosoft.java | 212 ++++
.../proxy/pac/PacHelperMethodsNetscape.java | 374 ++++++
.../core/network/proxy/pac/PacJsEntryFunction.java | 49 +
.../network/proxy/pac/PacParsingException.java | 36 +
.../core/network/proxy/pac/PacScriptEvaluator.java | 83 ++
.../proxy/pac/PacScriptEvaluatorFactory.java | 53 +
.../proxy/pac/PacScriptEvaluatorNoProxy.java | 57 +
.../netbeans/core/network/proxy/pac/PacUtils.java | 356 ++++++
.../network/proxy/pac/PacValidationException.java | 46 +
.../core/network/proxy/pac/datetime/DateRange.java | 174 +++
.../proxy/pac/datetime/PacUtilsDateTime.java | 374 ++++++
.../core/network/proxy/pac/datetime/TimeRange.java | 185 +++
.../network/proxy/pac/datetime/package-info.java | 22 +
.../proxy/pac/impl/ClassFilterPacHelpers.java | 42 +
.../proxy/pac/impl/HelperScriptFactory.java | 139 +++
.../network/proxy/pac/impl/NbPacHelperMethods.java | 343 ++++++
.../proxy/pac/impl/NbPacScriptEvaluator.java | 531 +++++++++
.../pac/impl/NbPacScriptEvaluatorFactory.java | 52 +
.../core/network/proxy/pac/impl/package-info.java | 26 +
.../core/network/proxy/pac/package-info.java | 31 +
.../netbeans/core/network/utils/HostnameUtils.java | 72 ++
.../core/network/utils/IpAddressUtils.java | 526 ++++++++
.../core/network/utils/IpAddressUtilsFilter.java | 107 ++
.../core/network/utils/LocalAddressUtils.java | 536 +++++++++
.../core/network/utils/NativeException.java | 43 +
.../core/network/utils/SimpleObjCache.java | 153 +++
.../utils/hname/linux/HostnameUtilsLinux.java | 44 +
.../network/utils/hname/linux/package-info.java | 23 +
.../network/utils/hname/mac/HostnameUtilsMac.java | 48 +
.../core/network/utils/hname/mac/package-info.java | 23 +
.../utils/hname/solaris/HostnameUtilsSolaris.java | 43 +
.../network/utils/hname/solaris/package-info.java | 23 +
.../core/network/utils/hname/unix/CLib.java | 32 +
.../utils/hname/unix/HostnameUtilsUnix.java | 54 +
.../network/utils/hname/unix/package-info.java | 23 +
.../network/utils/hname/win/HostnameUtilsWin.java | 110 ++
.../core/network/utils/hname/win/Winsock2Lib.java | 32 +
.../core/network/utils/hname/win/package-info.java | 23 +
.../netbeans/core/network/utils/package-info.java | 24 +
.../test/unit/data/pacFiles/wpad space.dat | 14 +
core.network/test/unit/data/pacFiles/wpad.dat | 14 +
.../data/pacFiles2/pac-test-sandbox-breakout.js | 32 +
core.network/test/unit/data/pacFiles2/pac-test1.js | 33 +
core.network/test/unit/data/pacFiles2/pac-test2.js | 33 +
core.network/test/unit/data/pacFiles2/pac-test3.js | 39 +
core.network/test/unit/data/pacFiles2/pac-test4.js | 242 ++++
.../core/network/proxy/ProxyAutoConfigTest.java | 12 +-
.../core/network/proxy/pac/PacEngineTest.java | 117 ++
.../network/proxy/pac/PacUtilsDateTimeTest.java | 160 +++
.../core/network/proxy/pac/PacUtilsTest.java | 150 +++
.../proxy/pac/impl/PacHelperMethodsImplTest.java | 333 ++++++
.../org/netbeans/core/network/utils/FakeDns.java | 216 ++++
.../core/network/utils/IpAddressUtilsTest.java | 167 +++
.../core/network/utils/LocalAddressUtilsTest.java | 82 ++
.../core/network/utils/SimpleCacheTest.java | 129 ++
libs.jna.platform/manifest.mf | 2 +-
libs.jna.platform/nbproject/project.xml | 1 +
nbbuild/build.properties | 1 +
nbbuild/javadoctools/links.xml | 1 +
nbbuild/javadoctools/properties.xml | 1 +
nbbuild/javadoctools/replaces.xml | 1 +
71 files changed, 8363 insertions(+), 698 deletions(-)
diff --git a/core.network/apichanges.xml b/core.network/apichanges.xml
new file mode 100644
index 0000000..c2e52a0
--- /dev/null
+++ b/core.network/apichanges.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<?xml-stylesheet type="text/xml" href="../nbbuild/javadoctools/apichanges.xsl"?>
+<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../nbbuild/javadoctools/apichanges.dtd">
+
+<apichanges>
+
+ <apidefs>
+ <apidef name="friend">PAC Script Evaluation Environment API</apidef>
+ </apidefs>
+
+ <changes>
+ <change id="pac-script-evaluator">
+ <api name="friend"/>
+ <summary>PAC Script evaluation environment is now pluggable</summary>
+ <version major="1" minor="12"/>
+ <date day="18" month="10" year="2017"/>
+ <author login="lbruun"/>
+ <compatibility addition="yes" binary="compatible" semantic="compatible" />
+ <description>
+ <p>
+ The PAC Script Evaluation environment (as part of <code>ProxySelector</code>)
+ has been completely rewritten and is now pluggable. (albeit for now only for friends)
+ </p>
+ </description>
+ <issue number="NETBEANS-4"/>
+ </change>
+
+ </changes>
+
+ <!-- Now the surrounding HTML text and document structure: -->
+
+ <htmlcontents>
+ <head>
+ <title>Change History for the Friend Network Core APIs</title>
+ <link rel="stylesheet" href="prose.css" type="text/css"/>
+ </head>
+ <body>
+
+<p class="overviewlink"><a href="overview-summary.html">Overview</a></p>
+
+<h1>Introduction</h1>
+
+<p>
+This document lists changes made to the Network Core APIs.
+
+</p>
+
+<!-- The actual lists of changes, as summaries and details: -->
+
+ <hr/><standard-changelists module-code-name="org.netbeans.core"/>
+
+ <hr/><p>@FOOTER@</p>
+
+ </body>
+ </htmlcontents>
+
+</apichanges>
diff --git a/core.network/arch.xml b/core.network/arch.xml
new file mode 100644
index 0000000..a1d257f
--- /dev/null
+++ b/core.network/arch.xml
@@ -0,0 +1,1251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<!DOCTYPE api-answers PUBLIC "-//NetBeans//DTD Arch Answers//EN" "../nbbuild/antsrc/org/netbeans/nbbuild/Arch.dtd" [
+ <!ENTITY api-questions SYSTEM "../nbbuild/antsrc/org/netbeans/nbbuild/Arch-api-questions.xml">
+]>
+
+<api-answers
+ question-version="1.29"
+ author="lbruun"
+>
+
+ &api-questions;
+
+
+<!--
+ <question id="arch-overall" when="init">
+ Describe the overall architecture.
+ <hint>
+ What will be API for
+ <a href="http://wiki.netbeans.org/API_Design#Separate_API_for_clients_from_support_API">
+ clients and what support API</a>?
+ What parts will be pluggable?
+ How will plug-ins be registered? Please use <code><api type="export"/></code>
+ to describe your general APIs and specify their
+ <a href="http://wiki.netbeans.org/API_Stability#Private">
+ stability categories</a>.
+ If possible please provide simple diagrams.
+ </hint>
+ </question>
+-->
+ <answer id="arch-overall">
+ <p>
+ The Core Network module provide ProxySelector as well as utilities related
+ to network connections.
+ </p>
+ <p>
+ <b>ProxySelector</b>
+ </p>
+ <p>
+ The role of a ProxySelector is to tell to the rest of Java which
+ proxy to use for a given URL. There's only one ProxySelector in a given
+ JVM. Most notably the ProxySelector is used by the <code>URLConnection</code>
+ classes and even the new HTTP client in JDK9 is using it, although it allows
+ more control over this than does the <code>URLConnection</code> classes.
+ Also, the Apache HttpClient is also using the default ProxySelector if you have instructed
+ it to use what is known in Apache HttpClient as <code>SystemDefaultRoutePlanner</code>.
+ </p>
+ <p>
+ This module provides an implementation of a ProxySelector which finds out
+ which proxy to use by looking at preferences the user has stored.
+ This is trivial for the case where the user has provided explicit proxy
+ information. It is more complex for the case where the user has said
+ "just use my system's proxy configuration" which is by the way the default
+ in NetBeans. This requires us to go the underlying OS and try to figure
+ out what the user's settings are. In many case case the user will not have
+ explicit proxy setting in the OS either but will simply have "Automatically
+ detect settings" or whatever. This makes it even more complex (see WPAD section
+ below).
+ </p>
+ <p>
+ The ProxySelector is instantiated by the <i>Startup</i> module, but only
+ if module <i>Core</i> also exists. Thus, for a minimal Platform application
+ which only use the modules from the "Runtime Container", there will be
+ no ProxySelector installed because module <i>Core</i> is not part of the
+ Runtime Container. In this case, the standard ProxySelector in
+ Java will then be used. This makes sense because a headless application
+ will not have stored Preferences about proxy, etc, and will be much
+ better off by just using the standard ProxySelector (which can be controlled
+ by <a href="https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html">properties</a>).
+ </p>
+ <p>
+ Module <i>Core</i> uses the Global Lookup to search for a ProxySelector,
+ so you can register your own ProxySelector if you don't like the one
+ provided by the Platform (i.e. this module). This will be somewhat difficult
+ though, because in order to implement your own ProxySelector, you would
+ need read access to the stored ProxySettings. This class is currently
+ only exposed to friends.
+ </p>
+ <p>
+ <b>Proxy Auto-Config (PAC)</b>
+ </p>
+ <p>
+ Very often the proxy configuration on a corporate network is not
+ given explictly. Instead it is given in a little JavaScript file
+ which clients are supposed to download and execute in order to find
+ out which proxy to use. This is known as <a href="https://en.wikipedia.org/wiki/Proxy_auto-config">Proxy Auto-Config</a>
+ (or PAC). This module provides a pluggable PAC evaluation environment
+ based on Nashorn. However, it will gracefully degrade to any other JavaScript
+ which may be installed in the JVM. Execution of the downloaded
+ JavaScript code is sandboxed. (only true for Nashorn)
+ </p>
+ <p>
+ If you don't like the PAC evaluation environment provided
+ by this module then you can plug in your own by registering
+ your own <code>PacScriptEvaluatorFactory</code> class in the Global Lookup.
+ If you want to build your own PAC evaluation environment then you can
+ still make use of all the utility classes provided by this module.
+ </p>
+ <p>
+ <b>Locating the PAC script (WPAD)</b>
+ </p>
+ <p>
+ Normally, a Web Browser (in comparison) can locate the PAC script either explicitly,
+ meaning the URL is directly available from the settings in the OS, or
+ it can be located via <a href="https://en.wikipedia.org/wiki/Web_Proxy_Auto-Discovery_Protocol">WPAD</a>
+ This module currently only supports the case where URL is directly available
+ from the settings. There's no support for WPAD.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="arch-quality" when="init">
+ How will the <a href="http://www.netbeans.org/community/guidelines/q-evangelism.html">quality</a>
+ of your code be tested and
+ how are future regressions going to be prevented?
+ <hint>
+ What kind of testing do
+ you want to use? How much functionality, in which areas,
+ should be covered by the tests? How you find out that your
+ project was successful?
+ </hint>
+ </question>
+-->
+ <answer id="arch-quality">
+ <p>
+ XXX no answer for arch-quality
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="arch-time" when="init">
+ What are the time estimates of the work?
+ <hint>
+ Please express your estimates of how long the design, implementation,
+ stabilization are likely to last. How many people will be needed to
+ implement this and what is the expected milestone by which the work should be
+ ready?
+ </hint>
+ </question>
+-->
+ <answer id="arch-time">
+ <p>
+ XXX no answer for arch-time
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="arch-usecases" when="init">
+ <hint>
+ Content of this answer will be displayed as part of page at
+ http://www.netbeans.org/download/dev/javadoc/usecases.html
+ You can use tags <usecase name="name> regular html description </usecase>
+ and if you want to use an URL you can prefix if with @TOP@ to begin
+ at the root of your javadoc
+ </hint>
+
+ Describe the main <a href="http://wiki.netbeans.org/API_Design#The_Importance_of_Being_Use_Case_Oriented">
+ use cases</a> of the new API. Who will use it under
+ what circumstances? What kind of code would typically need to be written
+ to use the module?
+ </question>
+-->
+ <answer id="arch-usecases">
+ <p>
+ <usecase name="Custom ProxySelector" id="custom-proxyselector">
+ You can provide your own ProxySelector instead of the one
+ provided by this module. You do this by registering your
+ own {@link java.net.ProxySelector}
+ in the Global Lookup. However, make sure you understand that
+ the instantiation of the ProxySelector (i.e. executing its constructor)
+ is part of the main startup path of a NetBeans application. For this
+ reason the constructor better be fast. Defer as much work as possible to
+ later.
+ <api name="java.net.ProxySelector" category="standard" group="java" type="import" url="@JDK@/java/net/ProxySelector.html" />
+ </usecase>
+ <usecase name="Custom PAC Evaluator" id="custom-pac-evaluator">
+ You can plug in your own PAC evaluator if you are unhappy with
+ the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator default one}
+ provided in this module. You do this by registering your
+ own {@link org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory}
+ in the Global Lookup. This factory must then in turn return an
+ instance of your custom {@link org.netbeans.core.network.proxy.pac.PacScriptEvaluator}.
+ <api name="org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.html" />
+ <api name="org.netbeans.core.network.proxy.pac.PacScriptEvaluator" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.html" />
+ </usecase>
+ <usecase name="Customizing the default PAC Evaluator" id="custom-pac-evaluator-helpers">
+ Instead of replacing all of the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator default PAC evaluator}
+ you can replace only its implementation of the <i>PAC Helper Functions</i>.
+ However, you have to play by rules set by the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator default PacScriptEvaluator},
+ namely that all the Helper Functions are implemented in Java (as opposed to JavaScript).
+ Simply register your sub-class of {@link org.netbeans.core.network.proxy.pac.PacHelperMethods}
+ in the Global Lookup and your implementation will automatically be picked
+ up. When creating your own <code>PacHelperMethods</code> you may
+ take benefit from the PAC utility functions provided by {@link org.netbeans.core.network.proxy.pac.PacUtils}
+ and {@link org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime},
+ as well as the more general ones provided by {@link org.netbeans.core.network.utils network utils package}.
+ <api name="org.netbeans.core.network.proxy.pac.PacHelperMethods" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacHelperMethods.html" />
+ <api name="org.netbeans.core.network.utils.HostnameUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/HostnameUtils.html" />
+ <api name="org.netbeans.core.network.utils.IpAddressUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/IpAddressUtils.html" />
+ <api name="org.netbeans.core.network.utils.LocalAddressUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/LocalAddressUtils.html" />
+ <api name="org.netbeans.core.network.utils.SimpleObjCache" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/SimpleObjCache.html" />
+ <api name="org.netbeans.core.network.proxy.pac.PacUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacUtils.html" />
+ <api name="org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.html" />
+ </usecase>
+ </p>
+
+ </answer>
+
+
+
+<!--
+ <question id="arch-what" when="init">
+ What is this project good for?
+ <hint>
+ Please provide here a few lines describing the project,
+ what problem it should solve, provide links to documentation,
+ specifications, etc.
+ </hint>
+ </question>
+-->
+ <answer id="arch-what">
+ <p>
+ The Core Network module provide ProxySelector as well as utilities related
+ to network connections.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="arch-where" when="impl">
+ Where one can find sources for your module?
+ <hint>
+ Please provide link to the Hg web client at
+ http://hg.netbeans.org/
+ or just use tag defaultanswer generate='here'
+ </hint>
+ </question>
+-->
+ <answer id="arch-where">
+ <defaultanswer generate='here' />
+ </answer>
+
+
+
+<!--
+ <question id="compat-deprecation" when="init">
+ How the introduction of your project influences functionality
+ provided by previous version of the product?
+ <hint>
+ If you are planning to deprecate/remove/change any existing APIs,
+ list them here accompanied with the reason explaining why you
+ are doing so.
+ </hint>
+ </question>
+-->
+ <answer id="compat-deprecation">
+ <p>
+ XXX no answer for compat-deprecation
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="compat-i18n" when="impl">
+ Is your module correctly internationalized?
+ <hint>
+ Correct internationalization means that it obeys instructions
+ at <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/i18n-branding.html">
+ NetBeans I18N pages</a>.
+ </hint>
+ </question>
+-->
+ <answer id="compat-i18n">
+ <p>
+ XXX no answer for compat-i18n
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="compat-standards" when="init">
+ Does the module implement or define any standards? Is the
+ implementation exact or does it deviate somehow?
+ </question>
+-->
+ <answer id="compat-standards">
+ <p>
+ The module implements the <a href="https://en.wikipedia.org/wiki/Proxy_auto-config">Proxy-Auto Config (PAC)</a>
+ specification as first specified by Netscape and then later amended by Microsoft.
+ The PAC script is a small piece of JavaScript, typically downloaded from the network.
+ The purpose of the PAC script is to determine which proxy to use for a
+ given URL.
+ </p>
+ <p>
+ In particular the specification from Netscape was very vaque. This has led to various
+ inconsistencies between how browsers (or other software) has implemented
+ this. The implementation in this module aims to support just about
+ every corner case and to be as compatible as possible with how the PAC
+ script is interpreted by various browsers. Whenever the standard is
+ ambigious, the implementation provided by the module deliberately
+ interprets the specification in the widest possible sense. More information in the Javadoc for
+ {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator}.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="compat-version" when="impl">
+ Can your module coexist with earlier and future
+ versions of itself? Can you correctly read all old settings? Will future
+ versions be able to read your current settings? Can you read
+ or politely ignore settings stored by a future version?
+
+ <hint>
+ Very helpful for reading settings is to store version number
+ there, so future versions can decide whether how to read/convert
+ the settings and older versions can ignore the new ones.
+ </hint>
+ </question>
+-->
+ <answer id="compat-version">
+ <p>
+ XXX no answer for compat-version
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="dep-jre" when="final">
+ Which version of JRE do you need (1.2, 1.3, 1.4, etc.)?
+ <hint>
+ It is expected that if your module runs on 1.x that it will run
+ on 1.x+1 if no, state that please. Also describe here cases where
+ you run different code on different versions of JRE and why.
+ </hint>
+ </question>
+-->
+ <answer id="dep-jre">
+ <p>
+ Java 1.8 or later is required.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="dep-jrejdk" when="final">
+ Do you require the JDK or is the JRE enough?
+ </question>
+-->
+ <answer id="dep-jrejdk">
+ <p>
+ Only JRE is needed.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="dep-nb" when="init">
+ What other NetBeans projects and modules does this one depend on?
+ <hint>
+ Depending on other NetBeans projects influnces the ability of
+ users of your work to customize their own branded version of
+ NetBeans by enabling and disabling some modules. Too
+ much dependencies restrict this kind of customization. If that
+ is your case, then you may want to split your functionality into
+ pieces of autoload, eager and regular modules which can be
+ enabled independently. Usually the answer to this question
+ is generated from your <code>project.xml</code> file, but
+ if it is not guessed correctly, you can suppress it by
+ specifying <defaultanswer generate="none"/> and
+ write here your own. Please describe such projects as imported APIs using
+ the <code><api name="identification" type="import or export" category="stable" url="where is the description" /></code>.
+ By doing this information gets listed in the summary page of your
+ javadoc.
+ </hint>
+ </question>
+-->
+ <answer id="dep-nb">
+ <defaultanswer generate='here' />
+ </answer>
+
+
+
+<!--
+ <question id="dep-non-nb" when="init">
+ What other projects outside NetBeans does this one depend on?
+
+ <hint>
+ Depending on 3rd party libraries is always problematic,
+ especially if they are not open source, as that complicates
+ the licensing scheme of NetBeans. Please enumerate your
+ external dependencies here, so it is correctly understood since
+ the begining what are the legal implications of your project.
+ Also please note that
+ some non-NetBeans projects are packaged as NetBeans modules
+ (see <a href="http://libs.netbeans.org/">libraries</a>) and
+ it is preferred to use this approach when more modules may
+ depend and share such third-party libraries.
+ </hint>
+ </question>
+-->
+ <answer id="dep-non-nb">
+ <p>
+ XXX no answer for dep-non-nb
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="dep-platform" when="init">
+ On which platforms does your module run? Does it run in the same
+ way on each?
+ <hint>
+ If you plan any dependency on OS or any usage of native code,
+ please describe why you are doing so and describe how you envision
+ to enforce the portability of your code.
+ Please note that there is a support for <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#how-os-specific">OS conditionally
+ enabled modules</a> which together with autoload/eager modules
+ can allow you to enable to provide the best OS aware support
+ on certain OSes while providing compatibility bridge on the not
+ supported ones.
+ Also please list the supported
+ OSes/HW platforms and mentioned the lovest version of JDK required
+ for your project to run on. Also state whether JRE is enough or
+ you really need JDK.
+ </hint>
+ </question>
+-->
+ <answer id="dep-platform">
+ <p>
+ The module uses JNA to look up various information about the host.
+ Currently there's support for Windows dektops, Mac OS X desktops,
+ KDE desktops and Gnome desktops.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="deploy-dependencies" when="final">
+ What do other modules need to do to declare a dependency on this one,
+ in addition to or instead of the normal module dependency declaration
+ (e.g. tokens to require)?
+ <hint>
+ Provide a sample of the actual lines you would add to a module manifest
+ to declare a dependency, for example OpenIDE-Module-Requires: some.token.
+ If other modules should not depend on this module, or should just use a
+ simple regular module dependency, you can just answer "nothing". If you
+ intentionally expose a semistable API to clients using implementation
+ dependencies, you should mention that here (but there is no need to give
+ an example of usage).
+ </hint>
+ </question>
+-->
+ <answer id="deploy-dependencies">
+ <p>
+ XXX no answer for deploy-dependencies
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="deploy-jar" when="impl">
+ Do you deploy just module JAR file(s) or other files as well?
+ <hint>
+ Usually a module consist of one JAR file (perhaps with Class-Path
+ extensions) and also a configuration file that enables it. If you
+ have any other files, use
+ <api group="java.io.File" name="yourname" type="export" category="friend">...</api>
+ to define the location, name and stability of your files (of course
+ changing "yourname" and "friend" to suit your needs).
+
+ If it uses more than one JAR, describe where they are located, how
+ they refer to each other.
+ If it consist of module JAR(s) and other files, please describe
+ what is their purpose, why other files are necessary. Please
+ make sure that installation/uninstallation leaves the system
+ in state as it was before installation.
+ </hint>
+ </question>
+-->
+ <answer id="deploy-jar">
+ <p>
+ XXX no answer for deploy-jar
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="deploy-nbm" when="impl">
+ Can you deploy an NBM via the Update Center?
+ <hint>
+ If not why?
+ </hint>
+ </question>
+-->
+ <answer id="deploy-nbm">
+ <p>
+ Yes
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="deploy-packages" when="init">
+ Are packages of your module made inaccessible by not declaring them
+ public?
+
+ <hint>
+ By default NetBeans build harness treats all packages are private.
+ If you export some of them - either as public or friend packages,
+ you should have a reason. If the reason is described elsewhere
+ in this document, you can ignore this question.
+ </hint>
+ </question>
+-->
+ <answer id="deploy-packages">
+ <p>
+ XXX no answer for deploy-packages
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="deploy-shared" when="final">
+ Do you need to be installed in the shared location only, or in the user directory only,
+ or can your module be installed anywhere?
+ <hint>
+ Installation location shall not matter, if it does explain why.
+ Consider also whether <code>InstalledFileLocator</code> can help.
+ </hint>
+ </question>
+-->
+ <answer id="deploy-shared">
+ <p>
+ Can be installed anywhere
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-ant-tasks" when="impl">
+ Do you define or register any ant tasks that other can use?
+
+ <hint>
+ If you provide an ant task that users can use, you need to be very
+ careful about its syntax and behaviour, as it most likely forms an
+ API for end users and as there is a lot of end users, their reaction
+ when such API gets broken can be pretty strong.
+ </hint>
+ </question>
+-->
+ <answer id="exec-ant-tasks">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-classloader" when="impl">
+ Does your code create its own class loader(s)?
+ <hint>
+ A bit unusual. Please explain why and what for.
+ </hint>
+ </question>
+-->
+ <answer id="exec-classloader">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-component" when="impl">
+ Is execution of your code influenced by any (string) property
+ of any of your components?
+
+ <hint>
+ Often <code>JComponent.getClientProperty</code>, <code>Action.getValue</code>
+ or <code>PropertyDescriptor.getValue</code>, etc. are used to influence
+ a behavior of some code. This of course forms an interface that should
+ be documented. Also if one depends on some interface that an object
+ implements (<code>component instanceof Runnable</code>) that forms an
+ API as well.
+ </hint>
+ </question>
+-->
+ <answer id="exec-component">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-introspection" when="impl">
+ Does your module use any kind of runtime type information (<code>instanceof</code>,
+ work with <code>java.lang.Class</code>, etc.)?
+ <hint>
+ Check for cases when you have an object of type A and you also
+ expect it to (possibly) be of type B and do some special action. That
+ should be documented. The same applies on operations in meta-level
+ (Class.isInstance(...), Class.isAssignableFrom(...), etc.).
+ </hint>
+ </question>
+-->
+ <answer id="exec-introspection">
+ <p>
+ Yes: Distinguishing between IPv4 and IPv6 instances is by
+ doing (example) : <code>inetAddress instanceof Inet6Address</code>.
+ This seems to be the generally accepted way of doing it.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-privateaccess" when="final">
+ Are you aware of any other parts of the system calling some of
+ your methods by reflection?
+ <hint>
+ If so, describe the "contract" as an API. Likely private or friend one, but
+ still API and consider rewrite of it.
+ </hint>
+ </question>
+-->
+ <answer id="exec-privateaccess">
+ <p>
+ No.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-process" when="impl">
+ Do you execute an external process from your module? How do you ensure
+ that the result is the same on different platforms? Do you parse output?
+ Do you depend on result code?
+ <hint>
+ If you feed an input, parse the output please declare that as an API.
+ </hint>
+ </question>
+-->
+ <answer id="exec-process">
+ <p>
+ XXX no answer for exec-process
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-property" when="impl">
+ Is execution of your code influenced by any environment or
+ Java system (<code>System.getProperty</code>) property?
+ On a similar note, is there something interesting that you
+ pass to <code>java.util.logging.Logger</code>? Or do you observe
+ what others log?
+ <hint>
+ If there is a property that can change the behavior of your
+ code, somebody will likely use it. You should describe what it does
+ and the <a href="http://wiki.netbeans.org/API_Stability">stability category</a>
+ of this API. You may use
+ <pre>
+ <api type="export" group="property" name="id" category="private" url="http://...">
+ description of the property, where it is used, what it influence, etc.
+ </api>
+ </pre>
+ </hint>
+ </question>
+-->
+ <answer id="exec-property">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-reflection" when="impl">
+ Does your code use Java Reflection to execute other code?
+ <hint>
+ This usually indicates a missing or insufficient API in the other
+ part of the system. If the other side is not aware of your dependency
+ this contract can be easily broken.
+ </hint>
+ </question>
+-->
+ <answer id="exec-reflection">
+ <p>
+ Yes. It detects the presence of Nashorn (as opposed to say Rhino) by way
+ of reflection. In particular it needs to know if the Java version is
+ Java 8u40 as Nashorn was greatly enhanced in that update and was more
+ or less useless (for our purpose) before this time. The use of reflection
+ means the code will gracefully 'degrade' to whatever script engine is
+ available if we are not on Java 8u40 or later.
+ </p>
+ <p>
+ For testing only a dirty hack is used in our <code>FakeDns</code> class.
+ This installs itself as a preferred name service in Java. This is done
+ via reflection. It also uses proprietary <code>sun.*</code> package.
+ However, it is only used for unit testing.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="exec-threading" when="init">
+ What threading models, if any, does your module adhere to? How the
+ project behaves with respect to threading?
+ <hint>
+ Is your API threadsafe? Can it be accessed from any threads or
+ just from some dedicated ones? Any special relation to AWT and
+ its Event Dispatch thread? Also
+ if your module calls foreign APIs which have a specific threading model,
+ indicate how you comply with the requirements for multithreaded access
+ (synchronization, mutexes, etc.) applicable to those APIs.
+ If your module defines any APIs, or has complex internal structures
+ that might be used from multiple threads, declare how you protect
+ data against concurrent access, race conditions, deadlocks, etc.,
+ and whether such rules are enforced by runtime warnings, errors, assertions, etc.
+ Examples: a class might be non-thread-safe (like Java Collections); might
+ be fully thread-safe (internal locking); might require access through a mutex
+ (and may or may not automatically acquire that mutex on behalf of a client method);
+ might be able to run only in the event queue; etc.
+ Also describe when any events are fired: synchronously, asynchronously, etc.
+ Ideas: <a href="http://core.netbeans.org/proposals/threading/index.html#recommendations">Threading Recommendations</a> (in progress)
+ </hint>
+ </question>
+-->
+ <answer id="exec-threading">
+ <p>
+ Documented in Javadoc where relevant.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="format-clipboard" when="impl">
+ Which data flavors (if any) does your code read from or insert to
+ the clipboard (by access to clipboard on means calling methods on <code>java.awt.datatransfer.Transferable</code>?
+
+ <hint>
+ Often Node's deal with clipboard by usage of <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>.
+ Check your code for overriding these methods.
+ </hint>
+ </question>
+-->
+ <answer id="format-clipboard">
+ <p>
+ Not applicable.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="format-dnd" when="impl">
+ Which protocols (if any) does your code understand during Drag & Drop?
+ <hint>
+ Often Node's deal with clipboard by usage of <code>Node.drag, Node.getDropType</code>.
+ Check your code for overriding these methods. Btw. if they are not overridden, they
+ by default delegate to <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>.
+ </hint>
+ </question>
+-->
+ <answer id="format-dnd">
+ <p>
+ Not applicable.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="format-types" when="impl">
+ Which protocols and file formats (if any) does your module read or write on disk,
+ or transmit or receive over the network? Do you generate an ant build script?
+ Can it be edited and modified?
+
+ <hint>
+ <p>
+ Files can be read and written by other programs, modules and users. If they influence
+ your behaviour, make sure you either document the format or claim that it is a private
+ api (using the <api> tag).
+ </p>
+
+ <p>
+ If you generate an ant build file, this is very likely going to be seen by end users and
+ they will be attempted to edit it. You should be ready for that and provide here a link
+ to documentation that you have for such purposes and also describe how you are going to
+ understand such files during next release, when you (very likely) slightly change the
+ format.
+ </p>
+ </hint>
+ </question>
+-->
+ <answer id="format-types">
+ <p>
+ Not applicable.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="lookup-lookup" when="init">
+ Does your module use <code>org.openide.util.Lookup</code>
+ or any similar technology to find any components to communicate with? Which ones?
+
+ <hint>
+ NetBeans is build around a generic registry of services called
+ lookup. It is preferable to use it for registration and discovery
+ if possible. See
+ <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/lookup/doc-files/index.html">
+ The Solution to Comunication Between Components
+ </a>. If you do not plan to use lookup and insist usage
+ of other solution, then please describe why it is not working for
+ you.
+ <br/>
+ When filling the final version of your arch document, please
+ describe the interfaces you are searching for, where
+ are defined, whether you are searching for just one or more of them,
+ if the order is important, etc. Also classify the stability of such
+ API contract. Use <api group=&lookup& /> tag, so
+ your information gets listed in the summary page of your javadoc.
+ </hint>
+ </question>
+-->
+ <answer id="lookup-lookup">
+ <p>
+ XXX no answer for lookup-lookup
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="lookup-register" when="final">
+ Do you register anything into lookup for other code to find?
+ <hint>
+ Do you register using layer file or using a declarative annotation such as <code>@ServiceProvider</code>?
+ Who is supposed to find your component?
+ </hint>
+ </question>
+-->
+ <answer id="lookup-register">
+ <p>
+ The, this module registers a NetBeans <code>ProxySelector</code> into
+ the Global Lookup. It also registers a <code>PacScriptEvaluatorFactory</code>
+ which is used by the NetBeans ProxySelector itself.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="lookup-remove" when="final">
+ Do you remove entries of other modules from lookup?
+ <hint>
+ Why? Of course, that is possible, but it can be dangerous. Is the module
+ your are masking resource from aware of what you are doing?
+ </hint>
+ </question>
+-->
+ <answer id="lookup-remove">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-exit" when="final">
+ Does your module run any code on exit?
+ </question>
+-->
+ <answer id="perf-exit">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-huge_dialogs" when="final">
+ Does your module contain any dialogs or wizards with a large number of
+ GUI controls such as combo boxes, lists, trees, or text areas?
+ </question>
+-->
+ <answer id="perf-huge_dialogs">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-limit" when="init">
+ Are there any hard-coded or practical limits in the number or size of
+ elements your code can handle?
+ <hint>
+ Most of algorithms have increasing memory and speed complexity
+ with respect to size of data they operate on. What is the critical
+ part of your project that can be seen as a bottleneck with
+ respect to speed or required memory? What are the practical
+ sizes of data you tested your project with? What is your estimate
+ of potential size of data that would cause visible performance
+ problems? Is there some kind of check to detect such situation
+ and prevent "hard" crashes - for example the CloneableEditorSupport
+ checks for size of a file to be opened in editor
+ and if it is larger than 1Mb it shows a dialog giving the
+ user the right to decide - e.g. to cancel or commit suicide.
+ </hint>
+ </question>
+-->
+ <answer id="perf-limit">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-mem" when="final">
+ How much memory does your component consume? Estimate
+ with a relation to the number of windows, etc.
+ </question>
+-->
+ <answer id="perf-mem">
+ <p>
+ XXX no answer for perf-mem
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-menus" when="final">
+ Does your module use dynamically updated context menus, or
+ context-sensitive actions with complicated and slow enablement logic?
+ <hint>
+ If you do a lot of tricks when adding actions to regular or context menus, you can significantly
+ slow down display of the menu, even when the user is not using your action. Pay attention to
+ actions you add to the main menu bar, and to context menus of foreign nodes or components. If
+ the action is conditionally enabled, or changes its display dynamically, you need to check the
+ impact on performance. In some cases it may be more appropriate to make a simple action that is
+ always enabled but does more detailed checks in a dialog if it is actually run.
+ </hint>
+ </question>
+-->
+ <answer id="perf-menus">
+ <p>
+ Not applicable. There's no GUI in this module.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-progress" when="final">
+ Does your module execute any long-running tasks?
+
+ <hint>Long running tasks should never block
+ AWT thread as it badly hurts the UI
+ <a href="http://performance.netbeans.org/responsiveness/issues.html">
+ responsiveness</a>.
+ Tasks like connecting over
+ network, computing huge amount of data, compilation
+ be done asynchronously (for example
+ using <code>RequestProcessor</code>), definitively it should
+ not block AWT thread.
+ </hint>
+ </question>
+-->
+ <answer id="perf-progress">
+ <p>
+ Yes. It executes tasks such as downloading PAC script from
+ network. This is done in a RequestProcessor.
+ It also provides utility methods for doing name lookup with
+ a timeout.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-scale" when="init">
+ Which external criteria influence the performance of your
+ program (size of file in editor, number of files in menu,
+ in source directory, etc.) and how well your code scales?
+ <hint>
+ Please include some estimates, there are other more detailed
+ questions to answer in later phases of implementation.
+ </hint>
+ </question>
+-->
+ <answer id="perf-scale">
+ <p>
+ XXX no answer for perf-scale
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-spi" when="init">
+ How the performance of the plugged in code will be enforced?
+ <hint>
+ If you allow foreign code to be plugged into your own module, how
+ do you enforce that it will behave correctly and quickly and will not
+ negatively influence the performance of your own module?
+ </hint>
+ </question>
+-->
+ <answer id="perf-spi">
+ <p>
+ XXX no answer for perf-spi
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-startup" when="final">
+ Does your module run any code on startup?
+ </question>
+-->
+ <answer id="perf-startup">
+ <p>
+ Yes. The initialization of the ProxySelector (i.e. calling its constructor)
+ is done as part of the platform startup process in module Startup. Moreso,
+ it is done on the platform main thread. In other words: The constructor needs
+ to be quick as it influences platform startup time. At the moment
+ this constructor is fairly lazy. For example it postpones download of
+ PAC file. However, the constructor can be further improved in terms
+ of startup speed.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="perf-wakeup" when="final">
+ Does any piece of your code wake up periodically and do something
+ even when the system is otherwise idle (no user interaction)?
+ </question>
+-->
+ <answer id="perf-wakeup">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="resources-file" when="final">
+ Does your module use <code>java.io.File</code> directly?
+
+ <hint>
+ NetBeans provide a logical wrapper over plain files called
+ <code>org.openide.filesystems.FileObject</code> that
+ provides uniform access to such resources and is the preferred
+ way that should be used. But of course there can be situations when
+ this is not suitable.
+ </hint>
+ </question>
+-->
+ <answer id="resources-file">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="resources-layer" when="final">
+ Does your module provide own layer? Does it create any files or
+ folders in it? What it is trying to communicate by that and with which
+ components?
+
+ <hint>
+ NetBeans allows automatic and declarative installation of resources
+ by module layers. Module register files into appropriate places
+ and other components use that information to perform their task
+ (build menu, toolbar, window layout, list of templates, set of
+ options, etc.).
+ </hint>
+ </question>
+-->
+ <answer id="resources-layer">
+ <p>
+ XXX no answer for resources-layer
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="resources-mask" when="final">
+ Does your module mask/hide/override any resources provided by other modules in
+ their layers?
+
+ <hint>
+ If you mask a file provided by another module, you probably depend
+ on that and do not want the other module to (for example) change
+ the file's name. That module shall thus make that file available as an API
+ of some stability category.
+ </hint>
+ </question>
+-->
+ <answer id="resources-mask">
+ <p>
+ No
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="resources-preferences" when="final">
+ Does your module uses preferences via Preferences API? Does your module use NbPreferences or
+ or regular JDK Preferences ? Does it read, write or both ?
+ Does it share preferences with other modules ? If so, then why ?
+ <hint>
+ You may use
+ <api type="export" group="preferences"
+ name="preference node name" category="private">
+ description of individual keys, where it is used, what it
+ influences, whether the module reads/write it, etc.
+ </api>
+ Due to XML ID restrictions, rather than /org/netbeans/modules/foo give the "name" as org.netbeans.modules.foo.
+ Note that if you use NbPreferences this name will then be the same as the code name base of the module.
+ </hint>
+ </question>
+-->
+ <answer id="resources-preferences">
+ <p>
+ The module reads Proxy Preferences using <code>NbPreferences</code>.
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="resources-read" when="final">
+ Does your module read any resources from layers? For what purpose?
+
+ <hint>
+ As this is some kind of intermodule dependency, it is a kind of API.
+ Please describe it and classify according to
+ <a href="http://wiki.netbeans.org/API_Design#What_is_an_API.3F">
+ common stability categories</a>.
+ </hint>
+ </question>
+-->
+ <answer id="resources-read">
+ <p>
+ XXX no answer for resources-read
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="security-grant" when="final">
+ Does your code grant additional rights to some other code?
+ <hint>Avoid using a class loader that adds extra
+ permissions to loaded code unless really necessary.
+ Also note that your API implementation
+ can also expose unneeded permissions to enemy code by
+ calling AccessController.doPrivileged().</hint>
+ </question>
+-->
+ <answer id="security-grant">
+ <p>
+ XXX no answer for security-grant
+ </p>
+ </answer>
+
+
+
+<!--
+ <question id="security-policy" when="final">
+ Does your functionality require modifications to the standard policy file?
+ <hint>Your code might pass control to third-party code not
+ coming from trusted domains. This could be code downloaded over the
+ network or code coming from libraries that are not bundled
+ with NetBeans. Which permissions need to be granted to which domains?</hint>
+ </question>
+-->
+ <answer id="security-policy">
+ <p>
+ No
+ </p>
+ </answer>
+
+</api-answers>
diff --git a/core.network/external/binaries-list b/core.network/external/binaries-list
deleted file mode 100644
index 87372f1..0000000
--- a/core.network/external/binaries-list
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-22C41D62B7BD70C00603B2CAE75406414224CF9F nsProxyAutoConfig-mozilla2.0.js
diff --git a/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt b/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt
deleted file mode 100644
index ac0e2db..0000000
--- a/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt
+++ /dev/null
@@ -1,478 +0,0 @@
-Name: nsProxyAutoConfig.js
-Version: mozilla2.0
-Description: Implementation of PAC Utils API used by proxy auto configuration (PAC) files. Required for resolution of proxies defined using these APIs.
-License: MPL-1.1
-Origin: Mozilla
-Files: nsProxyAutoConfig-mozilla2.0.js
-URL: http://mxr.mozilla.org/mozilla2.0/source/netwerk/base/src/nsProxyAutoConfig.js
-
- MOZILLA PUBLIC LICENSE
- Version 1.1
-
- ---------------
-
-1. Definitions.
-
- 1.0.1. "Commercial Use" means distribution or otherwise making the
- Covered Code available to a third party.
-
- 1.1. "Contributor" means each entity that creates or contributes to
- the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original
- Code, prior Modifications used by a Contributor, and the Modifications
- made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the
- combination of the Original Code and Modifications, in each case
- including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally
- accepted in the software development community for the electronic
- transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source
- Code.
-
- 1.6. "Initial Developer" means the individual or entity identified
- as the Initial Developer in the Source Code notice required by Exhibit
- A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or
- portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum
- extent possible, whether at the time of the initial grant or
- subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the
- substance or structure of either the Original Code or any previous
- Modifications. When Covered Code is released as a series of files, a
- Modification is:
- A. Any addition to or deletion from the contents of a file
- containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or
- previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code
- which is described in the Source Code notice required by Exhibit A as
- Original Code, and which, at the time of its release under this
- License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or
- hereafter acquired, including without limitation, method, process,
- and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for
- making modifications to it, including all modules it contains, plus
- any associated interface definition files, scripts used to control
- compilation and installation of an Executable, or source code
- differential comparisons against either the Original Code or another
- well known, available Covered Code of the Contributor's choice. The
- Source Code can be in a compressed or archival form, provided the
- appropriate decompression or de-archiving software is widely available
- for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity
- exercising rights under, and complying with all of the terms of, this
- License or a future version of this License issued under Section 6.1.
- For legal entities, "You" includes any entity which controls, is
- controlled by, or is under common control with You. For purposes of
- this definition, "control" means (a) the power, direct or indirect,
- to cause the direction or management of such entity, whether by
- contract or otherwise, or (b) ownership of more than fifty percent
- (50%) of the outstanding shares or beneficial ownership of such
- entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant.
- The Initial Developer hereby grants You a world-wide, royalty-free,
- non-exclusive license, subject to third party intellectual property
- claims:
- (a) under intellectual property rights (other than patent or
- trademark) Licensable by Initial Developer to use, reproduce,
- modify, display, perform, sublicense and distribute the Original
- Code (or portions thereof) with or without Modifications, and/or
- as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or
- selling of Original Code, to make, have made, use, practice,
- sell, and offer for sale, and/or otherwise dispose of the
- Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are
- effective on the date Initial Developer first distributes
- Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is
- granted: 1) for code that You delete from the Original Code; 2)
- separate from the Original Code; or 3) for infringements caused
- by: i) the modification of the Original Code or ii) the
- combination of the Original Code with other software or devices.
-
- 2.2. Contributor Grant.
- Subject to third party intellectual property claims, each Contributor
- hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (a) under intellectual property rights (other than patent or
- trademark) Licensable by Contributor, to use, reproduce, modify,
- display, perform, sublicense and distribute the Modifications
- created by such Contributor (or portions thereof) either on an
- unmodified basis, with other Modifications, as Covered Code
- and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or
- selling of Modifications made by that Contributor either alone
- and/or in combination with its Contributor Version (or portions
- of such combination), to make, use, sell, offer for sale, have
- made, and/or otherwise dispose of: 1) Modifications made by that
- Contributor (or portions thereof); and 2) the combination of
- Modifications made by that Contributor with its Contributor
- Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
- effective on the date Contributor first makes Commercial Use of
- the Covered Code.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is
- granted: 1) for any code that Contributor has deleted from the
- Contributor Version; 2) separate from the Contributor Version;
- 3) for infringements caused by: i) third party modifications of
- Contributor Version or ii) the combination of Modifications made
- by that Contributor with other software (except as part of the
- Contributor Version) or other devices; or 4) under Patent Claims
- infringed by Covered Code in the absence of Modifications made by
- that Contributor.
-
-3. Distribution Obligations.
-
- 3.1. Application of License.
- The Modifications which You create or to which You contribute are
- governed by the terms of this License, including without limitation
- Section 2.2. The Source Code version of Covered Code may be
- distributed only under the terms of this License or a future version
- of this License released under Section 6.1, and You must include a
- copy of this License with every copy of the Source Code You
- distribute. You may not offer or impose any terms on any Source Code
- version that alters or restricts the applicable version of this
- License or the recipients' rights hereunder. However, You may include
- an additional document offering the additional rights described in
- Section 3.5.
-
- 3.2. Availability of Source Code.
- Any Modification which You create or to which You contribute must be
- made available in Source Code form under the terms of this License
- either on the same media as an Executable version or via an accepted
- Electronic Distribution Mechanism to anyone to whom you made an
- Executable version available; and if made available via Electronic
- Distribution Mechanism, must remain available for at least twelve (12)
- months after the date it initially became available, or at least six
- (6) months after a subsequent version of that particular Modification
- has been made available to such recipients. You are responsible for
- ensuring that the Source Code version remains available even if the
- Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications.
- You must cause all Covered Code to which You contribute to contain a
- file documenting the changes You made to create that Covered Code and
- the date of any change. You must include a prominent statement that
- the Modification is derived, directly or indirectly, from Original
- Code provided by the Initial Developer and including the name of the
- Initial Developer in (a) the Source Code, and (b) in any notice in an
- Executable version or related documentation in which You describe the
- origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
- (a) Third Party Claims.
- If Contributor has knowledge that a license under a third party's
- intellectual property rights is required to exercise the rights
- granted by such Contributor under Sections 2.1 or 2.2,
- Contributor must include a text file with the Source Code
- distribution titled "LEGAL" which describes the claim and the
- party making the claim in sufficient detail that a recipient will
- know whom to contact. If Contributor obtains such knowledge after
- the Modification is made available as described in Section 3.2,
- Contributor shall promptly modify the LEGAL file in all copies
- Contributor makes available thereafter and shall take other steps
- (such as notifying appropriate mailing lists or newsgroups)
- reasonably calculated to inform those who received the Covered
- Code that new knowledge has been obtained.
-
- (b) Contributor APIs.
- If Contributor's Modifications include an application programming
- interface and Contributor has knowledge of patent licenses which
- are reasonably necessary to implement that API, Contributor must
- also include this information in the LEGAL file.
-
- (c) Representations.
- Contributor represents that, except as disclosed pursuant to
- Section 3.4(a) above, Contributor believes that Contributor's
- Modifications are Contributor's original creation(s) and/or
- Contributor has sufficient rights to grant the rights conveyed by
- this License.
-
- 3.5. Required Notices.
- You must duplicate the notice in Exhibit A in each file of the Source
- Code. If it is not possible to put such notice in a particular Source
- Code file due to its structure, then You must include such notice in a
- location (such as a relevant directory) where a user would be likely
- to look for such a notice. If You created one or more Modification(s)
- You may add your name as a Contributor to the notice described in
- Exhibit A. You must also duplicate this License in any documentation
- for the Source Code where You describe recipients' rights or ownership
- rights relating to Covered Code. You may choose to offer, and to
- charge a fee for, warranty, support, indemnity or liability
- obligations to one or more recipients of Covered Code. However, You
- may do so only on Your own behalf, and not on behalf of the Initial
- Developer or any Contributor. You must make it absolutely clear than
- any such warranty, support, indemnity or liability obligation is
- offered by You alone, and You hereby agree to indemnify the Initial
- Developer and every Contributor for any liability incurred by the
- Initial Developer or such Contributor as a result of warranty,
- support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions.
- You may distribute Covered Code in Executable form only if the
- requirements of Section 3.1-3.5 have been met for that Covered Code,
- and if You include a notice stating that the Source Code version of
- the Covered Code is available under the terms of this License,
- including a description of how and where You have fulfilled the
- obligations of Section 3.2. The notice must be conspicuously included
- in any notice in an Executable version, related documentation or
- collateral in which You describe recipients' rights relating to the
- Covered Code. You may distribute the Executable version of Covered
- Code or ownership rights under a license of Your choice, which may
- contain terms different from this License, provided that You are in
- compliance with the terms of this License and that the license for the
- Executable version does not attempt to limit or alter the recipient's
- rights in the Source Code version from the rights set forth in this
- License. If You distribute the Executable version under a different
- license You must make it absolutely clear that any terms which differ
- from this License are offered by You alone, not by the Initial
- Developer or any Contributor. You hereby agree to indemnify the
- Initial Developer and every Contributor for any liability incurred by
- the Initial Developer or such Contributor as a result of any such
- terms You offer.
-
- 3.7. Larger Works.
- You may create a Larger Work by combining Covered Code with other code
- not governed by the terms of this License and distribute the Larger
- Work as a single product. In such a case, You must make sure the
- requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
- If it is impossible for You to comply with any of the terms of this
- License with respect to some or all of the Covered Code due to
- statute, judicial order, or regulation then You must: (a) comply with
- the terms of this License to the maximum extent possible; and (b)
- describe the limitations and the code they affect. Such description
- must be included in the LEGAL file described in Section 3.4 and must
- be included with all distributions of the Source Code. Except to the
- extent prohibited by statute or regulation, such description must be
- sufficiently detailed for a recipient of ordinary skill to be able to
- understand it.
-
-5. Application of this License.
-
- This License applies to code to which the Initial Developer has
- attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions.
- Netscape Communications Corporation ("Netscape") may publish revised
- and/or new versions of the License from time to time. Each version
- will be given a distinguishing version number.
-
- 6.2. Effect of New Versions.
- Once Covered Code has been published under a particular version of the
- License, You may always continue to use it under the terms of that
- version. You may also choose to use such Covered Code under the terms
- of any subsequent version of the License published by Netscape. No one
- other than Netscape has the right to modify the terms applicable to
- Covered Code created under this License.
-
- 6.3. Derivative Works.
- If You create or use a modified version of this License (which you may
- only do in order to apply it to code which is not already Covered Code
- governed by this License), You must (a) rename Your license so that
- the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
- "MPL", "NPL" or any confusingly similar phrase do not appear in your
- license (except to note that your license differs from this License)
- and (b) otherwise make it clear that Your version of the license
- contains terms which differ from the Mozilla Public License and
- Netscape Public License. (Filling in the name of the Initial
- Developer, Original Code or Contributor in the notice described in
- Exhibit A shall not of themselves be deemed to be modifications of
- this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
- COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
- WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
- WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
- DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
- THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
- IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
- YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
- COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
- OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
- ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
- 8.1. This License and the rights granted hereunder will terminate
- automatically if You fail to comply with terms herein and fail to cure
- such breach within 30 days of becoming aware of the breach. All
- sublicenses to the Covered Code which are properly granted shall
- survive any termination of this License. Provisions which, by their
- nature, must remain in effect beyond the termination of this License
- shall survive.
-
- 8.2. If You initiate litigation by asserting a patent infringement
- claim (excluding declatory judgment actions) against Initial Developer
- or a Contributor (the Initial Developer or Contributor against whom
- You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly
- infringes any patent, then any and all rights granted by such
- Participant to You under Sections 2.1 and/or 2.2 of this License
- shall, upon 60 days notice from Participant terminate prospectively,
- unless if within 60 days after receipt of notice You either: (i)
- agree in writing to pay Participant a mutually agreeable reasonable
- royalty for Your past and future use of Modifications made by such
- Participant, or (ii) withdraw Your litigation claim with respect to
- the Contributor Version against such Participant. If within 60 days
- of notice, a reasonable royalty and payment arrangement are not
- mutually agreed upon in writing by the parties or the litigation claim
- is not withdrawn, the rights granted by Participant to You under
- Sections 2.1 and/or 2.2 automatically terminate at the expiration of
- the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's
- Contributor Version, directly or indirectly infringes any patent, then
- any rights granted to You by such Participant under Sections 2.1(b)
- and 2.2(b) are revoked effective as of the date You first made, used,
- sold, distributed, or had made, Modifications made by that
- Participant.
-
- 8.3. If You assert a patent infringement claim against Participant
- alleging that such Participant's Contributor Version directly or
- indirectly infringes any patent where such claim is resolved (such as
- by license or settlement) prior to the initiation of patent
- infringement litigation, then the reasonable value of the licenses
- granted by such Participant under Sections 2.1 or 2.2 shall be taken
- into account in determining the amount or value of any payment or
- license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above,
- all end user license agreements (excluding distributors and resellers)
- which have been validly granted by You or any distributor hereunder
- prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
- UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
- (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
- DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
- OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
- ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
- CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
- WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
- COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
- INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
- LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
- RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
- PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
- EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
- THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
- The Covered Code is a "commercial item," as that term is defined in
- 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
- software" and "commercial computer software documentation," as such
- terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
- C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
- all U.S. Government End Users acquire Covered Code with only those
- rights set forth herein.
-
-11. MISCELLANEOUS.
-
- This License represents the complete agreement concerning subject
- matter hereof. If any provision of this License is held to be
- unenforceable, such provision shall be reformed only to the extent
- necessary to make it enforceable. This License shall be governed by
- California law provisions (except to the extent applicable law, if
- any, provides otherwise), excluding its conflict-of-law provisions.
- With respect to disputes in which at least one party is a citizen of,
- or an entity chartered or registered to do business in the United
- States of America, any litigation relating to this License shall be
- subject to the jurisdiction of the Federal Courts of the Northern
- District of California, with venue lying in Santa Clara County,
- California, with the losing party responsible for costs, including
- without limitation, court costs and reasonable attorneys' fees and
- expenses. The application of the United Nations Convention on
- Contracts for the International Sale of Goods is expressly excluded.
- Any law or regulation which provides that the language of a contract
- shall be construed against the drafter shall not apply to this
- License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
- As between Initial Developer and the Contributors, each party is
- responsible for claims and damages arising, directly or indirectly,
- out of its utilization of rights under this License and You agree to
- work with Initial Developer and Contributors to distribute such
- responsibility on an equitable basis. Nothing herein is intended or
- shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
- Initial Developer may designate portions of the Covered Code as
- "Multiple-Licensed". "Multiple-Licensed" means that the Initial
- Developer permits you to utilize portions of the Covered Code under
- Your choice of the NPL or the alternative licenses, if any, specified
- by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A -Mozilla Public License.
-
- ``The contents of this file are subject to the Mozilla Public License
- Version 1.1 (the "License"); you may not use this file except in
- compliance with the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
- License for the specific language governing rights and limitations
- under the License.
-
- The Original Code is ______________________________________.
-
- The Initial Developer of the Original Code is ________________________.
- Portions created by ______________________ are Copyright (C) ______
- _______________________. All Rights Reserved.
-
- Contributor(s): ______________________________________.
-
- Alternatively, the contents of this file may be used under the terms
- of the _____ license (the "[___] License"), in which case the
- provisions of [______] License are applicable instead of those
- above. If you wish to allow use of your version of this file only
- under the terms of the [____] License and not to allow others to use
- your version of this file under the MPL, indicate your decision by
- deleting the provisions above and replace them with the notice and
- other provisions required by the [___] License. If you do not delete
- the provisions above, a recipient may use your version of this file
- under either the MPL or the [___] License."
-
- [NOTE: The text of this Exhibit A may differ slightly from the text of
- the notices in the Source Code files of the Original Code. You should
- use the text of this Exhibit A rather than the text found in the
- Original Code Source Code for Your Modifications.]
-
diff --git a/core.network/manifest.mf b/core.network/manifest.mf
index 41db36c..c9b89a7 100644
--- a/core.network/manifest.mf
+++ b/core.network/manifest.mf
@@ -2,5 +2,5 @@ Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.core.network
OpenIDE-Module-Localizing-Bundle: org/netbeans/core/network/proxy/Bundle.properties
OpenIDE-Module-Provides: org.netbeans.core.ProxySettings.Reloader
-OpenIDE-Module-Specification-Version: 1.11
+OpenIDE-Module-Specification-Version: 1.12
diff --git a/core.network/nbproject/project.properties b/core.network/nbproject/project.properties
index 6e6e301..a19bad1 100644
--- a/core.network/nbproject/project.properties
+++ b/core.network/nbproject/project.properties
@@ -16,7 +16,7 @@
# under the License.
is.autoload=true
-javac.source=1.6
+javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
-release.external/nsProxyAutoConfig-mozilla2.0.js=modules/ext/nsProxyAutoConfig.js
-jnlp.indirect.files=modules/ext/nsProxyAutoConfig.js
+javadoc.arch=${basedir}/arch.xml
+javadoc.apichanges=${basedir}/apichanges.xml
diff --git a/core.network/nbproject/project.xml b/core.network/nbproject/project.xml
index d89cca4..0fe2742 100644
--- a/core.network/nbproject/project.xml
+++ b/core.network/nbproject/project.xml
@@ -26,6 +26,15 @@
<code-name-base>org.netbeans.core.network</code-name-base>
<module-dependencies>
<dependency>
+ <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.28</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.core</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -44,6 +53,15 @@
</run-dependency>
</dependency>
<dependency>
+ <code-name-base>org.netbeans.libs.jna.platform</code-name-base>
+ <build-prerequisite/>
+ <compile-dependency/>
+ <run-dependency>
+ <release-version>1</release-version>
+ <specification-version>1.16</specification-version>
+ </run-dependency>
+ </dependency>
+ <dependency>
<code-name-base>org.netbeans.modules.keyring</code-name-base>
<build-prerequisite/>
<compile-dependency/>
@@ -163,7 +181,14 @@
</test-dependency>
</test-type>
</test-dependencies>
- <public-packages/>
+ <friend-packages>
+ <friend>org.netbeans.core</friend>
+ <package>org.netbeans.core.network.proxy.pac</package>
+ <package>org.netbeans.core.network.proxy.pac.datetime</package>
+ <package>org.netbeans.core.network.proxy.pac.impl</package>
+ <package>org.netbeans.core.network.utils</package>
+ <package>org.netbeans.core.network.utils.hname</package>
+ </friend-packages>
</data>
</configuration>
</project>
diff --git a/core.network/src/org/netbeans/core/network/proxy/Bundle.properties b/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
index 9c2bd8f..bee29a1 100644
--- a/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
+++ b/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
@@ -14,4 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-OpenIDE-Module-Name=Core Network Settings
+OpenIDE-Module-Display-Category=Infrastructure
+OpenIDE-Module-Name=Core Network
+OpenIDE-Module-Short-Description=Support for outgoing network connections (ProxySelector, ...)
diff --git a/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java b/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
index 2a6db70..008d80b 100644
--- a/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
+++ b/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
@@ -18,19 +18,19 @@
*/
package org.netbeans.core.network.proxy;
-import org.netbeans.core.ProxySettings;
import java.io.*;
import java.net.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileUtil;
-import org.openide.filesystems.URLMapper;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorNoProxy;
+import org.netbeans.core.network.proxy.pac.PacValidationException;
+import org.openide.util.Lookup;
import org.openide.util.RequestProcessor;
import org.openide.util.RequestProcessor.Task;
import org.openide.util.Utilities;
@@ -43,7 +43,6 @@ public class ProxyAutoConfig {
private static final Map<String, ProxyAutoConfig> file2pac = new HashMap<String, ProxyAutoConfig>(2);
private static final RequestProcessor RP = new RequestProcessor(ProxyAutoConfig.class);
- private static final String NS_PROXY_AUTO_CONFIG_URL = "nbinst://org.netbeans.core/modules/ext/nsProxyAutoConfig.js"; // NOI18N
private static final String PROTO_FILE = "file://";
/**
@@ -66,7 +65,7 @@ public class ProxyAutoConfig {
return file2pac.get(pacFile);
}
private static final Logger LOGGER = Logger.getLogger(ProxyAutoConfig.class.getName());
- private Invocable inv = null;
+ private PacScriptEvaluator evaluator;
private final Task initTask;
private final URI pacURI;
@@ -88,45 +87,36 @@ public class ProxyAutoConfig {
}
private void initEngine() {
- InputStream pacIS;
- try {
- if (pacURI.isAbsolute()) {
- pacIS = downloadPAC(pacURI.toURL());
- } else {
- pacIS = null;
+ String pacSource = null;
+ if (pacURI.isAbsolute()) {
+ try (InputStream is = downloadPAC(pacURI.toURL())) {
+ pacSource = convertInputStreamToString(is, 8192, StandardCharsets.UTF_8);
+ } catch (MalformedURLException ex) {
+ LOGGER.log(Level.INFO, "PAC URL is malformed : ", ex);
+ return;
+ } catch (IOException ex) {
+ LOGGER.log(Level.INFO, "InputStream for " + pacURI + " throws ", ex);
+ return;
}
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, "InputStream for " + pacURI + " throws " + ex, ex);
- return;
- }
- if (pacIS == null) {
- return ;
}
- String utils = downloadUtils();
- ScriptEngine eng;
- try {
- eng = evalPAC(pacIS, utils);
- } catch (FileNotFoundException ex) {
- LOGGER.log(Level.FINE, "While constructing ProxyAutoConfig thrown " + ex, ex);
- return ;
- } catch (ScriptException ex) {
- LOGGER.log(Level.FINE, "While constructing ProxyAutoConfig thrown " + ex, ex);
- return ;
- } finally {
- if (pacIS != null) {
- try {
- pacIS.close();
- } catch (IOException ex) {
- LOGGER.log(Level.FINE, "While closing PAC input stream thrown " + ex, ex);
- }
+
+ PacScriptEvaluatorFactory factory = Lookup.getDefault().lookup(PacScriptEvaluatorFactory.class);
+ if (factory == null) {
+ LOGGER.log(Level.WARNING, "No PAC Script Evaluator factory found. Will use dummy evaluator instead.");
+ evaluator = new PacScriptEvaluatorNoProxy();
+ } else {
+ try {
+ evaluator = factory.createPacScriptEvaluator(pacSource);
+ } catch (PacParsingException ex) {
+ LOGGER.log(Level.WARNING, "There was a catastrophic error with the PAC script downloaded from " + pacURI + ". Will use dummy instead. Error was : ", ex);
+ evaluator = factory.getNoOpEvaluator();
}
}
- assert eng != null : "JavaScri5pt engine cannot be null";
- if (eng == null) {
- LOGGER.log(Level.WARNING, "JavaScript engine cannot be null");
- return ;
+
+ assert evaluator != null : "JavaScript evaluator cannot be null";
+ if (evaluator == null) {
+ LOGGER.log(Level.WARNING, "JavaScript evaluator cannot be null");
}
- inv = (Invocable) eng;
}
@SuppressWarnings("unchecked")
@@ -146,24 +136,12 @@ public class ProxyAutoConfig {
}
}
}
- if (inv == null) {
- return Collections.singletonList(Proxy.NO_PROXY);
- }
- Object proxies = null;
try {
- proxies = inv.invokeFunction("FindProxyForURL", u.toString(), u.getHost()); // NOI18N
- } catch (ScriptException ex) {
- LOGGER.log(Level.FINE, "While invoking FindProxyForURL(" + u + ", " + u.getHost() + " thrown " + ex, ex);
- } catch (NoSuchMethodException ex) {
- LOGGER.log(Level.FINE, "While invoking FindProxyForURL(" + u + ", " + u.getHost() + " thrown " + ex, ex);
- }
- List<Proxy> res = analyzeResult(u, proxies);
- if (res == null) {
- LOGGER.info("findProxyForURL(" + u + ") returns null.");
- res = Collections.emptyList();
+ return evaluator.findProxyForURL(u);
+ } catch (PacValidationException ex) {
+ LOGGER.log(Level.WARNING, "Incorrect answer from PAC script : ", ex);
+ return Collections.singletonList(Proxy.NO_PROXY);
}
- LOGGER.fine("findProxyForURL(" + u + ") returns " + Arrays.asList(res));
- return res;
}
private static InputStream downloadPAC (URL pacURL) throws IOException {
@@ -173,142 +151,27 @@ public class ProxyAutoConfig {
return is;
}
- private static ScriptEngine evalPAC(InputStream is, String utils) throws FileNotFoundException, ScriptException {
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngine engine = factory.getEngineByName("JavaScript");
- Reader pacReader = new InputStreamReader(is);
- engine.eval(pacReader);
- engine.eval(utils);
- return engine;
- }
- private List<Proxy> analyzeResult(URI uri, Object proxiesString) {
- if (proxiesString == null) {
- LOGGER.fine("Null result for " + uri);
- return null;
- }
- Proxy.Type proxyType;
- String protocol = uri.getScheme();
- assert protocol != null : "Invalid scheme of uri " + uri + ". Scheme cannot be null!";
- if (protocol == null) {
- return null;
- } else {
- if (("http".equals(protocol)) || ("https".equals(protocol))) { // NOI18N
- proxyType = Proxy.Type.HTTP;
- } else {
- proxyType = Proxy.Type.SOCKS;
- }
- }
- StringTokenizer st = new StringTokenizer(proxiesString.toString(), ";"); //NOI18N
- List<Proxy> proxies = new LinkedList<Proxy>();
- while (st.hasMoreTokens()) {
- String proxy = st.nextToken();
- if (ProxySettings.DIRECT.equals(proxy.trim())) {
- proxies.add(Proxy.NO_PROXY);
- } else {
- String host = getHost(proxy);
- Integer port = getPort(proxy);
- if (host != null && port != null) {
- proxies.add(new Proxy(proxyType, new InetSocketAddress(host, port)));
- }
- }
- }
- return proxies;
- }
- private static String getHost(String proxy) {
- if (proxy.startsWith("PROXY ")) {
- proxy = proxy.substring(6);
+ protected static String convertInputStreamToString(InputStream in, int initSize, Charset charset) throws IOException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream(initSize);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = in.read(buffer)) != -1) {
+ buf.write(buffer, 0, length);
}
- int i = proxy.lastIndexOf(":"); // NOI18N
- if (i <= 0 || i >= proxy.length() - 1) {
- LOGGER.info("No port in " + proxy);
- return null;
- }
-
- String host = proxy.substring(0, i);
-
- return ProxySettings.normalizeProxyHost(host);
+ return buf.toString(charset.name());
}
- private static Integer getPort(String proxy) {
- if (proxy.startsWith("PROXY ")) {
- proxy = proxy.substring(6);
- }
- int i = proxy.lastIndexOf(":"); // NOI18N
- if (i <= 0 || i >= proxy.length() - 1) {
- LOGGER.info("No port in " + proxy);
- return null;
- }
-
- String port = proxy.substring(i + 1);
- if (port.indexOf('/') >= 0) {
- port = port.substring(0, port.indexOf('/'));
- }
-
- try {
- return Integer.parseInt(port);
- } catch (NumberFormatException ex) {
- LOGGER.log(Level.INFO, ex.getLocalizedMessage(), ex);
- return null;
- }
- }
-
- private static String downloadUtils() {
- StringBuilder builder = new StringBuilder();
- BufferedReader reader = null;
- // XXX why is the below not more simply:
- // reader = new BufferedReader(new URL(NS_PROXY_AUTO_CONFIG_URL).openStream());
- FileObject fo = null;
- try {
- try {
- fo = URLMapper.findFileObject(new URL(NS_PROXY_AUTO_CONFIG_URL));
- } catch (MalformedURLException ex) {
- LOGGER.log(Level.INFO, ex.getMessage(), ex);
- return "";
- }
- reader = new BufferedReader(new java.io.FileReader(FileUtil.toFile(fo)));
- } catch (FileNotFoundException ex) {
- LOGGER.log(Level.INFO, ex.getMessage(), ex);
- }
- try {
- String line;
- boolean doAppend = false;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if( line.startsWith("var pacUtils =") ) { //NOI18N
- doAppend = true;
- continue;
- }
- if( !doAppend )
- continue;
- if (line.endsWith("+")) { // NOI18N
- line = line.substring(0, line.length() - 1);
- }
- builder.append(line.replaceAll("\"", "").replaceAll("\\\\n", "").replaceAll("\\\\\\\\", "\\\\")); // NOI18N
- builder.append(System.getProperty("line.separator")); // NOI18N
- }
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, "While downloading nsProxyAutoConfig.js thrown " + ex.getMessage(), ex);
- } finally {
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException ex) {
- LOGGER.log(Level.FINE, ex.getMessage(), ex);
- }
- }
- }
- return builder.toString();
- }
private String normalizePAC(String pacURL) {
int index;
- if ((index = pacURL.indexOf("\n")) != -1) { // NOI18N
- pacURL = pacURL.substring(0, index);
+ String inputSanitized = pacURL;
+ if ((index = inputSanitized.indexOf("\n")) != -1) { // NOI18N
+ inputSanitized = inputSanitized.substring(0, index);
}
- if ((index = pacURL.indexOf("\r")) != -1) { // NOI18N
- pacURL = pacURL.substring(0, index);
+ if ((index = inputSanitized.indexOf("\r")) != -1) { // NOI18N
+ inputSanitized = inputSanitized.substring(0, index);
}
String fileLocation = pacURL;
if (fileLocation.startsWith(PROTO_FILE)) {
@@ -316,13 +179,13 @@ public class ProxyAutoConfig {
}
File f = new File(fileLocation);
if (f.canRead()) {
- pacURL = Utilities.toURI(f).toString();
+ inputSanitized = Utilities.toURI(f).toString();
} else {
- pacURL = pacURL.replaceAll("\\\\", "/"); //NOI18N
+ inputSanitized = inputSanitized.replaceAll("\\\\", "/"); //NOI18N
}
- if ((index = pacURL.indexOf(" ")) != -1) { // NOI18N
- pacURL = pacURL.substring(0, index);
+ if ((index = inputSanitized.indexOf(" ")) != -1) { // NOI18N
+ inputSanitized = inputSanitized.substring(0, index);
}
- return pacURL.trim();
+ return inputSanitized.trim();
}
}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java
new file mode 100644
index 0000000..eb08624
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+
+
+/**
+ * Stub for PAC Helpers.
+ *
+ * <p>
+ * The 'Helpers' are utility functions that the PAC script can make use of.
+ *
+ * @author lbruun
+ */
+public abstract class PacHelperMethods implements PacHelperMethodsNetscape, PacHelperMethodsMicrosoft {
+
+
+ /**
+ * Writes something to log or elsewhere. This allows the PAC script to do
+ * simple logging for debugging purpose. It overwrites the standard
+ * JavaScript {@code alert()} function.
+ *
+ * <p>
+ * Note that the JavaScript {@code alert()} function should not be used in
+ * a production PAC script. It is only intended for debugging and
+ * unit test purpose.
+ *
+ * <p>
+ * The default implementation simply writes to stderr.
+ *
+ * @param message text to log
+ */
+ public void alert(String message) {
+ System.err.println(message);
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java
new file mode 100644
index 0000000..f9cef13
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Microsoft's extensions to complement Netscape's original PAC script
+ * helper functions. (as defined in {@link PacHelperMethodsNetscape}).
+ *
+ * <p>
+ * The JavaScript helper functions are defined in Java terms by this interface.
+ * The Java methods are named exactly as their JavaScript counterparts.
+ *
+ * <p>
+ * Microsoft has added these methods in recognition that the original
+ * Netscape functions were defined at a time before IPv6 became widely used
+ * or was even finalized. The original Netscape functions simply didn't take
+ * IPv6 into account and should therefore be interpreted in an IPv4-only context.
+ * Support for these new functions were first introduced in Internet Explorer v7.
+ * Other browsers, e.g. Chrome, have since then implemented them too.
+ *
+ * <p>
+ * Microsoft also defined a new entry-point function, {@code FindProxyForURLEx(url, host)},
+ * which should replace the legacy {@code FindProxyForURL(url, host)} function.
+ * Microsoft only makes the new helper functions available from this new entry
+ * function. However, Chrome does it differently in that the new helper
+ * functions are indeed available, but there's no support for the new entry function,
+ * {@code FindProxyForURLEx}, meaning the new helper functions are
+ * available from the old entry method, {@code FindProxyForURL}.
+ * An implementation of {@link PacScriptEvaluator} should strive for maximum
+ * compatibility meaning all helper functions should be available regardless
+ * of entry point.
+ *
+ * @author lbruun
+ */
+public interface PacHelperMethodsMicrosoft {
+
+ /**
+ * Tries to resolve the hostname. Returns true if succeeds. Unlike the
+ * original Netscape function, {@code isResolvable()}, this function supports
+ * both IPv4 and IPv6 addresses, meaning it should return {@code true} if the
+ * input can be resolved to any type of IP address.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * isResolvable("www.netscape.com")
+ * is true (unless DNS fails to resolve it due to a firewall or some other reason).
+ * isResolvable("bogus.domain.foobar")
+ * is false.
+ * </pre>
+ * @param host
+ * @return
+ */
+ public boolean isResolvableEx(String host);
+
+ /**
+ * Resolves the given host name into its address or addresses.
+ *
+ * <p>
+ * Example of usage from JavaScript:
+ * <pre>
+ * dnsResolveEx("testmachine1");
+ * returns the string "2001:4898:28:7:982d:a3b3:97ad:7dd0;192.168.1.99"
+ * </pre>
+ * @param host
+ * @return a semi-colon delimited string containing (potentially both)
+ * IPv6 and IPv4 addresses or an empty string if host is not resolvable
+ */
+ public String dnsResolveEx(String host);
+
+ /**
+ * Returns the IP addresses the local host is known by.
+ *
+ * <p>
+ * Example of usage from JavaScript:
+ * <pre>
+ * myIpAddressEx()
+ * would return the string "2001:4898:28:7:982d:a3b3:97ad:7dd0;198.95.249.79"
+ * if you were running the application on that host.
+ * </pre>
+ *
+ * @return a semi-colon delimited string containing (potentially both)
+ * IPv6 and IPv4 addresses.
+ */
+ public String myIpAddressEx();
+
+ /**
+ * Sorts a list of IP addresses in ascending order. If both IPv6 and IPv4
+ * addresses are passed as input to this function, then the sorted IPv6
+ * addresses are followed by the sorted IPv4 addresses.
+ *
+ * <p>
+ * Example of usage from JavaScript:
+ * <pre>
+ * sortIpAddressList("10.2.3.9;2001:4898:28:3:201:2ff:feea:fc14;::1;127.0.0.1;::9");
+ * returns "::1;::9;2001:4898:28:3:201:2ff:feea:fc14;10.2.3.9;127.0.0.1";
+ * </pre>
+ *
+ * <p>
+ * NOTE: Microsoft Internet Explorer 11 (tested on 11.608.15063.0) doesn't
+ * give correct results on this function. For example the result of
+ * {@code sortIpAddressList("10.2.3.9;2001:4898:28:3:201:2ff:feea:fc14;::1;127.0.0.1;::9")}
+ * is
+ * {@code "[::1];10.2.3.9;127.0.0.1;[2001:4898:28:3:201:2ff:feea:fc14];[::9]"}
+ * which is incorrect in more ways than one. In contrast, Chrome does it as
+ * expected and as in the example above. In summary the following problems
+ * are seen with the IE 11 implementation of this function:
+ * <ul>
+ * <li>IPv4 addresses are not in sort order <i>after</i> after
+ * the IPv6 addresses as the example shows. The documentation
+ * clearly states the opposite.
+ * </li>
+ * <li>IPv4-mapped IPv6 address (e.g. {@code fe80::5efe:157.59.139.22}
+ * doesn't seem to be working. This is strange as it is used in the
+ * example in <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/gg308482(v=vs.85).aspx">Microsoft's own documentation</a>.
+ * Such an IP literal seems to throw an error which means
+ * the PAC script will not return a result, which means IE will choose
+ * its default option, namely "DIRECT".
+ * </li>
+ * <li>IPv6 literals in the output will be enclosed in [], e.g.
+ * {@code "1080:0:0:0:8:800:200c:417a"} in the input will become
+ * {@code "[1080:0:0:0:8:800:200c:417a]"} in the output.
+ * This too is in contrast to the documentation.
+ * </li>
+ * </ul>
+ * <br>
+ *
+ * @param ipAddressList a semi-colon delimited string containing IP addresses
+ * @return a list of sorted semi-colon delimited IP addresses or an empty
+ * string if unable to sort the IP address list.
+ */
+ public String sortIpAddressList(String ipAddressList);
+
+ /**
+ * Gets the version of the PAC script processing engine.
+ *
+ * <p>
+ * Microsoft added this function to allow IT administrators to update
+ * their PAC scripts to use different versions of the PAC processing engine
+ * without causing breaks to their existing deployment.
+ * For example, if Microsoft added a function to the 2.0 version of the the
+ * Microsoft PAC processing engine, then administrators can check the
+ * version before attempting to call that function. This allows their
+ * script to work with client running versions 1.0 and 2.0 of the engine.
+ *
+ * <p>
+ * Note to implementers: Currently this function should simply return "1.0"
+ * in order retain compatibility with Microsoft world.
+ *
+ * @return
+ */
+ public String getClientVersion();
+
+ /**
+ * True if the IP address of the host matches the specified IP address
+ * pattern.
+ *
+ * <p>
+ * Microsoft's documentation is vague / contradictory on what the pattern
+ * argument, {@code ipPrefix}, is, meaning is it a <i>list</i> of patterns
+ * or is it a singular pattern?. We err on the side of caution and decide that
+ * the {@code ipPrefix} is a semi-colon separated list of patterns and that
+ * the method must return {@code true} if the supplied {@code ipAddress}
+ * matches <i>any</i> of the patterns in the list.
+ *
+ * <p>
+ * Similarly, the interpretation of the first argument, {@code host},
+ * is open for discussion. In the sibling function, {@code isInNet}, it
+ * is clearly stated that this argument can be either a host name or a
+ * literal IP address. But Microsoft's documentation on {@code isInNetEx}
+ * limits the argument to only being a literal IP address. Again we err
+ * on the side of caution and decide that this argument can be either. If
+ * it is a host name then the method must do a name service lookup first
+ * (to convert to IP), before the actual comparison can begin.
+ *
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * isInNetEx(ipAddress, "198.95.249.79/32");
+ * true if the IP address of host matches exactly 198.95.249.79
+ * isInNetEx(ipAddress, "198.95.0.0/16");
+ * true if the IP address of the host matches 198.95.*.*
+ * isInNetEx(ipAddress, "3ffe:8311:ffff/48");
+ * true if the IP address of the host matches 3ffe:8311:fff:*:*:*:*:*
+ * isInNetEx(ipAddress, "198.95.249.0/24;198.122.0.0/16");
+ * true if the IP address of host matches 198.95.249.* or matches 198.122.*.*
+ * </pre>
+ *
+ * @param host a string containing an IPv6/IPv4 addresses or a host name.
+ * @param ipPrefix a string containing semi-colon delimited IP prefixes with
+ * top n bits specified in the bit field
+ * (i.e. 3ffe:8311:ffff::/48 or 123.112.0.0/16).
+ * @return {@code true} if the IP address matches the specified IP address
+ * pattern ({code ipPrefix}). Also returns {@code false} if the prefix
+ * is not in the correct format or if addresses and prefixes of different
+ * types are used in the comparison (i.e. IPv4 prefix and an IPv6 address).
+ */
+ public boolean isInNetEx(String host, String ipPrefix);
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java
new file mode 100644
index 0000000..8531e98
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Netscape's original PAC script helper functions. In their original
+ * document, Netscape defined 12 helper functions which should be available
+ * to the JavaScript PAC. The first browser to implement these were - unsurpringsly -
+ * the Netscape Navigator.
+ *
+ * <p>
+ * The JavaScript helper functions are defined in Java terms by this interface.
+ * The Java methods are named exactly as their JavaScript counterparts.
+ *
+ * <p>
+ * The documentation on each of the methods in the interface is more
+ * or less copied verbatim from Netscape's original documentation.
+ *
+ * <p>
+ * Beware that at the time when Netscape defined these functions IPv6
+ * wasn't on the radar. Therefore the concensus among browser makers
+ * is that IPv4 is implied in all these functions.
+ *
+ * @author lbruun
+ */
+public interface PacHelperMethodsNetscape {
+
+ /**
+ * True if there is no domain name in the hostname (no dots).
+ *
+ * <p>
+ * Examples:
+ * <pre>
+ * isPlainHostName("www")
+ * is true.
+ * isPlainHostName("www.netscape.com")
+ * is false.
+ * </pre>
+ * @param host host name
+ * @return
+ */
+ public boolean isPlainHostName(String host);
+
+ /**
+ * Returns true if the domain of hostname matches.
+ *
+ * <p>
+ * Examples:
+ * <pre>
+ * dnsDomainIs("www.netscape.com", ".netscape.com")
+ * is true.
+ * dnsDomainIs("www", ".netscape.com")
+ * is false.
+ * dnsDomainIs("www.mcom.com", ".netscape.com")
+ * is false.
+ * </pre>
+ * @param host the host name from the URL.
+ * @param domain the domain name to test the host name against.
+ * @return
+ */
+ public boolean dnsDomainIs(String host, String domain);
+
+ /**
+ * Is true if the hostname matches exactly the specified hostname, or if
+ * there is no domain name part in the hostname, but the unqualified
+ * hostname matches.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * localHostOrDomainIs("www.netscape.com", "www.netscape.com")
+ * is true (exact match).
+ * localHostOrDomainIs("www", "www.netscape.com")
+ * is true (hostname match, domain not specified).
+ * localHostOrDomainIs("www.mcom.com", "www.netscape.com")
+ * is false (domain name mismatch).
+ * localHostOrDomainIs("home.netscape.com", "www.netscape.com")
+ * is false (hostname mismatch).
+ * </pre>
+ *
+ * @param host the hostname from the URL.
+ * @param hostdom fully qualified hostname to match against.
+ * @return
+ */
+ public boolean localHostOrDomainIs(String host, String hostdom);
+
+ /**
+ * Tries to resolve the hostname. Returns true if succeeds. Strictly
+ * speaking - and in the spirit of the original Netscape specification -
+ * this method should only return {@code true} if the argument can be
+ * resolved into an IPv4 address.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * isResolvable("www.netscape.com")
+ * is true (unless DNS fails to resolve it due to a firewall or some other reason).
+ * isResolvable("bogus.domain.foobar")
+ * is false.
+ * </pre>
+ * @param host
+ * @return
+ */
+ public boolean isResolvable(String host);
+
+ /**
+ * Resolves the given DNS hostname into an IPv4 address,
+ * and returns it in the dot separated format as a string.
+ *
+ * <p>
+ * Example of usage from JavaScript:
+ * <pre>
+ * dnsResolve("home.netscape.com")
+ * returns the string "198.95.249.79".
+ * </pre>
+ * @param host
+ * @return
+ */
+ public String dnsResolve(String host);
+
+ /**
+ * Returns the IPv4 address of the host that the application is running on,
+ * as a string in the dot-separated integer format.
+ *
+ * <p>
+ * Example of usage from JavaScript:
+ * <pre>
+ * myIpAddress()
+ * would return the string "198.95.249.79" if you were running the
+ * the application on that host.
+ * </pre>
+ *
+ * @return IPv4 address in textual form
+ */
+ public String myIpAddress();
+
+ /**
+ * True if the IP address of the host matches the specified IP address
+ * pattern.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * isInNet(host, "198.95.249.79", "255.255.255.255")
+ * is true if the IP address of host matches exactly 198.95.249.79.
+ * isInNet(host, "198.95.0.0", "255.255.0.0")
+ * is true if the IP address of the host matches 198.95.*.*.
+ * </pre>
+ *
+ * @param host a DNS hostname or IPv4 address. If a hostname is passed, it
+ * will be resolved into an IP address by this function.
+ * @param pattern an IPv4 address pattern in the dot-separated format.
+ * @param mask mask for the IP address pattern informing which parts of the
+ * IP address should be matched against. 0 means ignore, 255 means match.
+ * @return
+ */
+ public boolean isInNet(String host, String pattern, String mask);
+
+ /**
+ * Returns the number of DNS domain levels (number of dots)
+ * in the hostname.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * dnsDomainLevels("www")
+ * returns 0.
+ * dnsDomainLevels("www.netscape.com")
+ * returns 2.
+ * </pre>
+ *
+ * @param host hostname
+ * @return
+ */
+ public int dnsDomainLevels(String host);
+
+ /**
+ * Returns true if the string matches the specified shell expression.
+ * Note that the argument is a <i>shell expression</i>, not a
+ * regular expression.
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * shExpMatch("http://home.netscape.com/people/ari/index.html", "*/ari/*")
+ * is true.
+ * shExpMatch("http://home.netscape.com/people/montulli/index.html", "*/ari/*")
+ * is false.
+ * </pre>
+ * @param str
+ * @param shexp shell expression
+ * @return
+ */
+ public boolean shExpMatch(String str, String shexp);
+
+ /**
+ * Tests if current day is within a day-of-week range.
+ *
+ * <p>
+ * There are several forms of this method from JavaScript:
+ * <pre>
+ * weekdayRange(wd1)
+ * weekdayRange(wd1, gmt)
+ * weekdayRange(wd1, wd2)
+ * weekdayRange(wd1, wd2, gmt)
+ * </pre>
+ *
+ * Parameters:
+ * <ul>
+ * <li>{@code wd1} and {@code wd2} are weekday specifications. </li>
+ * <li>{@code gmt} is either the string "GMT", which makes time comparison
+ * occur in GMT timezone; or if not present, times are taken to
+ * be in the local timezone. If this parameter exists it
+ * must always be the last parameter.
+ * </li>
+ * </ul>
+ *
+ * <p>If only {@code wd1} is present, the function yields a true value on the
+ * weekday that the parameter represents. If both {@code wd1} and
+ * {@code wd2} are specified, the condition is true if the current weekday
+ * is in between those two weekdays. Bounds are inclusive.
+ *
+ * <p>The weekday abbreviations used in {@code wd1} and {@code wd2} must be
+ * one of the following:
+ * <pre>
+ * MON TUE WED THU FRI SAT SUN
+ * </pre>
+ *
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * weekdayRange("MON", "FRI")
+ * Returns true Monday through Friday (local time zone).
+ * weekdayRange("MON", "FRI", "GMT")
+ * Returns true Monday through Friday, in Greenwich Mean Time.
+ * weekdayRange("SAT")
+ * Returns true on Saturdays, local time.
+ * weekdayRange("SAT", "GMT")
+ * Returns true on Saturdays, in Greenwich Mean Time.
+ * weekdayRange("FRI", "MON")
+ * Returns true Friday through Monday (the order is important)
+ * weekdayRange("WED", "TUE")
+ * Returns true always, all 7 days a week (although an alien way of specifying it)
+ * </pre>
+ * <br>
+ * @param args up to max 3 arguments
+ * @return
+ * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime#WEEKDAY_NAMES
+ */
+ public boolean weekdayRange(Object... args);
+
+ /**
+ * Tests if current date is within a date range.
+ *
+ * <p>There are several forms of this method from JavaScript:
+ * <pre>
+ * dateRange(day)
+ * dateRange(day1, day2)
+ * dateRange(mon)
+ * 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)
+ * dateRange(day1, month1, year1, day2, month2, year2, gmt)
+ * </pre>
+ * Even if not shown above, the {@code gmt} parameter can always be
+ * added as an (optional) last parameter.
+ *
+ * <p>
+ * <ul>
+ * <li>{@code day} is the day of month between 1 and 31 (as an integer).
+ * </li>
+ * <li>{@code month} is one of the month strings: {@code JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC}
+ * </li>
+ * <li>{@code year} is the full year number with 4 digits, for example 1995. (as an integer)
+ * <li>{@code gmt} is either the string "GMT", which makes time comparison
+ * occur in GMT timezone; or if not present, times are taken to
+ * be in the local timezone. If this parameter exists it
+ * must always be the last parameter.
+ * </li>
+ * </ul>
+ * <br>
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * dateRange(1)
+ * true on the first day of each month, local timezone.
+ * dateRange(1, "GMT")
+ * true on the first day of each month, GMT timezone.
+ * dateRange(1, 15)
+ * true on the first half of each month.
+ * dateRange(24, "DEC")
+ * true on 24th of December each year.
+ * dateRange(24, "DEC", 1995)
+ * true on 24th of December, 1995.
+ * dateRange("JAN", "MAR")
+ * true on the first quarter of the year.
+ * dateRange(1, "JUN", 15, "AUG")
+ * true from June 1st until August 15th, each year (including June 1st and August 15th).
+ * dateRange(1, "JUN", 15, 1995, "AUG", 1995)
+ * true from June 1st, 1995, until August 15th, same year.
+ * dateRange("OCT", 1995, "MAR", 1996)
+ * true from October 1995 until March 1996 (including the entire month of October 1995 and March 1996).
+ * dateRange(1995)
+ * true during the entire year 1995.
+ * dateRange(1995, 1997)
+ * true from beginning of year 1995 until the end of year 1997.
+ * </pre>
+ *
+ * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime#MONTH_NAMES
+ * @param args
+ * @return
+ */
+ public boolean dateRange(Object... args);
+
+ /**
+ * Tests if current time is within a time range.
+ *
+ * <p>There are several forms of this method from JavaScript:
+ * <pre>
+ * timeRange(hour)
+ * timeRange(hour1, hour2)
+ * timeRange(hour1, min1, hour2, min2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+ * </pre>
+ * Even if not shown above, the {@code gmt} parameter can always be
+ * added as an (optional) last parameter.
+ * <p>
+ * <ul>
+ * <li>{@code hour} is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * </li>
+ * <li>{@code min} minutes from 0 to 59.
+ * </li>
+ * <li>{@code sec} seconds from 0 to 59.
+ * <li>{@code gmt} is either the string "GMT", which makes time comparison
+ * occur in GMT timezone; or if not present, times are taken to
+ * be in the local timezone. If this parameter exists it
+ * must always be the last parameter.
+ * </li>
+ * </ul>
+ * <br>
+ * <p>
+ * Examples of usage from JavaScript:
+ * <pre>
+ * timeRange(12, 13)
+ * This statement is true from noon to 1:00 p.m.
+ * timeRange(12, "GMT")
+ * This statement is true noon to 12:59 p.m. GMT.
+ * timeRange(9, 17)
+ * This statement is true from 9:00 a.m. to 5:00 p.m.
+ * timeRange(0, 0, 0, 0, 0, 30)
+ * true between midnight and 30 seconds past midnight.
+ * </pre>
+ *
+ * @param args anywhere between 1 and 7 arguments
+ * @return
+ */
+ public boolean timeRange(Object... args);
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java
new file mode 100644
index 0000000..6a4da3b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Entry points for PAC script.
+ *
+ * @author lbruun
+ */
+public enum PacJsEntryFunction {
+
+ /**
+ * Main entry point to JavaScript PAC script as defined by Netscape.
+ * This is JavaScript function name {@code FindProxyForURL()}.
+ */
+ STANDARD("FindProxyForURL"),
+
+ /**
+ * Main entry point to JavaScript PAC script for IPv6 support,
+ * as defined by Microsoft.
+ * This is JavaScript function name {@code FindProxyForURLEx()}.
+ */
+ IPV6_AWARE("FindProxyForURLEx");
+
+ private final String jsFunctionName;
+
+ PacJsEntryFunction(String jsFunctionName) {
+ this.jsFunctionName = jsFunctionName;
+ }
+
+ /**
+ * Gets name of JavaScript function.
+ * @return
+ */
+ public String getJsFunctionName() {
+ return jsFunctionName;
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java
new file mode 100644
index 0000000..d7b480b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Thrown when there are syntactical errors in the PAC script.
+ *
+ * @see PacValidationException
+ *
+ * @author lbruun
+ */
+public class PacParsingException extends Exception {
+
+ public PacParsingException(Exception ex) {
+ super(ex);
+ }
+
+ public PacParsingException(String message, Exception ex) {
+ super(message, ex);
+ }
+
+ public PacParsingException(String message) {
+ super(message);
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java
new file mode 100644
index 0000000..ad3d755
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.Proxy;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * PAC Script evaluator.
+ *
+ * <p>This is a bridge between Java and the PAC script, which is implemented in
+ * JavaScript. {@code PacScriptEvaluator}s are created via
+ * {@link PacScriptEvaluatorFactory}.
+ *
+ * @author lbruun
+ */
+public interface PacScriptEvaluator {
+
+ /**
+ * Returns the proxy/proxies appropriate to use for the given URI.
+ *
+ * <p>
+ * The method calls the JavaScript {@code FindProxyForURL(url, host)}
+ * function in the PAC script (or alternatively the
+ * {@code FindProxyForURLEx(url, host)} function), parses the result and
+ * returns it as a prioritized list of proxies.
+ *
+ * @param uri URI to get proxies for.
+ * @return
+ * @throws PacValidationException when the result from the JavaScript function
+ * cannot be interpreted.
+ */
+ public List<Proxy> findProxyForURL(URI uri) throws PacValidationException ;
+
+ /**
+ * Returns if the Evaluator uses result caching or not. Result caching speeds
+ * up execution as a call to Java method {@link #findProxyForURL(java.net.URI)} will
+ * not result in a call to JavaScript function {@code FindProxyForURL}
+ * if the URL has been resolved previously. But the Evaluator cannot use
+ * caching if the PAC Script uses methods which depends on time and the Evaluator
+ * may therefore have decided to turn off result caching.
+ *
+ * <p>
+ * If an implementation of {@code PacScriptEvaluator} never uses result caching
+ * (because it simply isn't implemented) then this method will always return
+ * {@code false}.
+ *
+ * @return
+ */
+ public boolean usesCaching();
+
+
+ /**
+ * Gets the entry function to the PAC script which the engine uses.
+ * @return name of JavaScript function
+ */
+ public String getJsEntryFunction();
+
+ /**
+ * Gets relevant information about the engine, typically the name
+ * of the JavaScript engine (e.g. 'Nashorn'), version number, etc.
+ * @return info
+ */
+ public String getEngineInfo();
+
+ /**
+ * Gets the JavaScript source code of the PAC script.
+ * @return source code
+ */
+ public String getPacScriptSource();
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java
new file mode 100644
index 0000000..d7a703f
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Creates PAC Script evaluator.
+ *
+ * @author lbruun
+ */
+public interface PacScriptEvaluatorFactory {
+
+ /**
+ * Creates a PAC evaluator based on the given JavaScript source code.
+ *
+ * <p>
+ * The evaluator must treat the {@code pacSource} as untrusted and evaluate
+ * it in a sandbox.
+ *
+ * <p>
+ * The method will throw {@link PacParsingException} if the JavaScript input
+ * cannot be parsed/interpreted. In this case you may opt to either do
+ * nothing, report to logging system or use a
+ * {@link #getNoOpEvaluator() no-op evaluator} as an alternative.
+ *
+ * @param pacSource The JavaScript source code of the PAC script, as a string.
+ * To be a correct PAC script, it must implement the
+ * {@code FindProxyForURL(url, host)} function. Most often the PAC script
+ * is downloaded from a network location.
+ * @return
+ * @throws PacParsingException if the source code cannot be parsed
+ */
+ public PacScriptEvaluator createPacScriptEvaluator(String pacSource) throws PacParsingException;
+
+ /**
+ * Gets a no-op PAC evaluator, meaning one which always returns {@code Proxy.NO_PROXY}
+ * for any call to {@link PacScriptEvaluator#findProxyForURL(java.net.URI)}
+ *
+ * @return
+ */
+ public PacScriptEvaluator getNoOpEvaluator();
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java
new file mode 100644
index 0000000..047db8c
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.Proxy;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * PAC Script Evaluator which can be used as a last resort.
+ * Always returns {@code NO_PROXY}.
+ *
+ * @author lbruun
+ */
+public class PacScriptEvaluatorNoProxy implements PacScriptEvaluator {
+
+ @Override
+ public List<Proxy> findProxyForURL(URI u) {
+ return Collections.singletonList(Proxy.NO_PROXY);
+ }
+
+ @Override
+ public boolean usesCaching() {
+ return false;
+ }
+
+ @Override
+ public String getJsEntryFunction() {
+ return "NONE";
+ }
+
+ @Override
+ public String getEngineInfo() {
+ return "Dummy engine. Always returns NO_PROXY";
+ }
+
+ @Override
+ public String getPacScriptSource() {
+ return "function FindProxyForURL(url, host) {\n"
+ + " return \"DIRECT\";\n"
+ + "};";
+ }
+
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java
new file mode 100644
index 0000000..c7b8a2f
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.SimpleObjCache;
+
+/**
+ * Methods and constants useful in PAC script evaluation.
+ *
+ * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime
+ *
+ * @author lbruun
+ */
+public class PacUtils {
+
+ /**
+ * Size of the cache used for precompiled GLOBs.
+ */
+ public static final int PRECOMPILED_GLOB_CACHE_MAX_ITEMS = 10;
+ private static final SimpleObjCache<String, Pattern> PRECOMPILED_GLOB_CACHE
+ = new SimpleObjCache<>(PRECOMPILED_GLOB_CACHE_MAX_ITEMS);
+
+
+
+
+ /**
+ * Translate a GLOB pattern into a RegExp pattern. GLOB patterns originate
+ * from Unix hosts where they are primarily used for file pattern matching.
+ * In the original PAC specification from Netscape a GLOB pattern is
+ * referred to as a 'shell expression'.
+ *
+ * <p>
+ * This method supports all GLOB wildcards, such as
+ * <table border="0" style="order-collapse: separate;border-spacing: 50px 0;" summary="">
+ * <tr align="left"><td>{@code *}</td><td>matches any number of any
+ * characters including none</td>
+ * <tr align="left"><td>{@code ?}</td><td>matches any single character</td>
+ * <tr align="left"><td>{@code [abc]}</td><td>matches one character given in
+ * the bracket</td>
+ * <tr align="left"><td>{@code [a-z]}</td><td>matches one character from the
+ * range given in the bracket</td>
+ * <tr align="left"><td>{@code [!abc]}</td><td>matches one character
+ * <i>not</i> given in the bracket</td>
+ * <tr align="left"><td>{@code [!a-z]}</td><td>matches one character
+ * <i>not</i> from the range given in the bracket</td>
+ * </table>
+ *
+ * <p>
+ * A small cache is used so that if a glob pattern has already been
+ * translated previously, the result from the cache will be returned.
+ *
+ * @param glob
+ * @return
+ */
+ public static Pattern createRegexPatternFromGlob(String glob) {
+
+ // First try the cache
+ Pattern pattern = PRECOMPILED_GLOB_CACHE.get(glob);
+ if (pattern != null) {
+ return pattern;
+ }
+
+ StringBuilder out = new StringBuilder();
+ out.append("^");
+ for (int i = 0; i < glob.length(); ++i) {
+ final char c = glob.charAt(i);
+ switch (c) {
+ case '*':
+ out.append(".*?");
+ break;
+ case '?':
+ out.append(".{1}");
+ break;
+ case '.':
+ out.append("\\.");
+ break;
+ case '\\':
+ out.append("\\\\");
+ break;
+ case '!':
+ if (i > 0 && glob.charAt(i - 1) == '[') {
+ out.append('^');
+ } else {
+ out.append(c);
+ }
+ break;
+ default:
+ out.append(c);
+ }
+ }
+ out.append("$");
+ pattern = Pattern.compile(out.toString());
+ PRECOMPILED_GLOB_CACHE.put(glob, pattern);
+ return pattern;
+ }
+
+
+
+ /**
+ * Converts list into semi-colon separated string where each element
+ * is represented by the result of the {@code fn} function.
+ *
+ * @param <T>
+ * @param list list of objects
+ * @param fn function which returns string
+ * @return string with elements separated by semi-colon
+ */
+ public static <T> String toSemiColonList(List<T> list, Function<T,String> fn) {
+ return list.stream()
+ .map(i -> fn.apply(i))
+ .collect(Collectors.joining(";"));
+ }
+
+ /**
+ * Converts list into semi-colon separated string where each element
+ * is represented by the result of {@link Object#toString()}.
+ *
+ * @see #toSemiColonList(java.util.List, java.util.function.Function)
+ * @param <T>
+ * @param list list of objects
+ * @return string with elements separated by semi-colon
+ */
+ public static <T> String toSemiColonList(List<T> list) {
+ return toSemiColonList(list, Object::toString);
+ }
+
+ /**
+ * Converts a list of {@code InetAddress} into a semi-colon
+ * separated string. Each address is represented by the result
+ * of {@link InetAddress#getHostAddress()}.
+ *
+ * @see #toSemiColonList(java.util.List, java.util.function.Function)
+ * @param addresses
+ * @return semi-colon separated string of addresses in literal form
+ */
+ public static String toSemiColonListInetAddress(InetAddress[] addresses) {
+ return toSemiColonList(Arrays.asList(addresses), InetAddress::getHostAddress);
+ }
+
+ /**
+ * Converts an array of {@code InetAddress} into a semi-colon
+ * separated string. Each address is represented by the result
+ * of {@link InetAddress#getHostAddress()}.
+ *
+ * @see #toSemiColonList(java.util.List, java.util.function.Function)
+ * @param addresses
+ * @return semi-colon separated string of addresses in literal form
+ */
+ public static String toSemiColonListInetAddress(List<InetAddress> addresses) {
+ return toSemiColonList(addresses, InetAddress::getHostAddress);
+ }
+
+
+ /**
+ * Checks if an IP address matches a given CIDR pattern.
+ *
+ * The pattern must use the format:
+ * <pre>
+ * ipLiteral/bitField
+ * </pre>
+ *
+ * Examples of valid patterns:
+ * <pre>
+ * 198.95.249.79/32
+ * 198.95.0.0/16
+ * 3ffe:8311:ffff/48
+ * </pre>
+ *
+ * <p>
+ * For IPv6 the {@code ipLiteral} is allowed to be incomplete at the end. If
+ * not complete, then a suffix of "::" is appended to the literal, e.g. the
+ * method will translate {@code "3ffe:8311:ffff/48"} to
+ * {@code "3ffe:8311:ffff::/48"}.
+ *
+ * <p>
+ * A number of validation checks are carried out on the {@code ipPrefix}
+ * argument and {@code false} will be returned if these checks fails:
+ * <ul>
+ * <li>If {@code ipAddress} is IPv4 then {@code bitField}
+ * must be between 8 and 32.
+ * </li>
+ * <li>If {@code ipAddress} is IPv6 then {@code bitField}
+ * must be between 8 and 128.
+ * </li>
+ * <li>The {@code ipLiteral} value must be a valid IPv4 literal or IPv6 literal.
+ * </li>
+ * <li>The IP protocol type of {@code ipLiteral} value must match the IP protocol
+ * type of {@code ipAddress}.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * Note: The method was developed for the purpose of supporting the
+ * {@link PacHelperMethodsMicrosoft#isInNetEx(java.lang.String, java.lang.String)
+ * Microsoft isInNetEx()} extension to PAC scripting, but the method may
+ * have an appeal broader than this particular use case.
+ *
+ * <br><br>
+ *
+ * @param ipAddress address
+ * @param ipPrefix pattern
+ * @return true if the address, {@code ipAddress}, match the pattern, {@code ipPrefix}.
+ */
+ public static boolean ipPrefixMatch(InetAddress ipAddress, String ipPrefix) {
+ if (ipPrefix.indexOf('/') == -1) {
+ return false;
+ }
+ String[] parts = ipPrefix.trim().split("\\/");
+
+ int bitField = 0;
+ try {
+ bitField = Integer.parseInt(parts[1].trim());
+ if (!(bitField >= 0 && bitField <= 128)) {
+ return false;
+ }
+ if (ipAddress instanceof Inet4Address) {
+ if (bitField > 32) {
+ return false;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ return false;
+ }
+ String ipPatternStr = parts[0].trim();
+
+ InetAddress ipPattern = null;
+ if (ipAddress instanceof Inet4Address) {
+ if (IpAddressUtils.looksLikeIpv4Literal(ipPatternStr)) {
+ try {
+ ipPattern = InetAddress.getByName(ipPatternStr);
+ } catch (UnknownHostException ex) {
+ return false;
+ }
+ }
+ }
+ if (ipAddress instanceof Inet6Address) {
+ String ipPatternStrC = correctIPv6Str(ipPatternStr);
+ if (IpAddressUtils.looksLikeIpv6Literal(ipPatternStrC)) {
+ try {
+ ipPattern = InetAddress.getByName(ipPatternStrC);
+ } catch (UnknownHostException ex) {
+ return false;
+ }
+ }
+ }
+ if (ipPattern == null) {
+ return false;
+ }
+ if (!ipAddress.getClass().equals(ipPattern.getClass())) {
+ return false;
+ }
+ BigInteger mask = BigInteger.valueOf(-1).shiftLeft((ipPattern.getAddress().length * 8) - bitField); // mask = -1<<32-bits or mask = -1<<128-bits
+ BigInteger subnetMask = new BigInteger(ipPattern.getAddress()).and(mask);
+
+ return (new BigInteger(ipAddress.getAddress()))
+ .and(mask).equals(subnetMask);
+ }
+
+ /**
+ * Completes an IPv6 literal address if it is incomplete at the end.
+ * This is done by appending "::" if needed.
+ * @param s
+ * @return
+ */
+ private static String correctIPv6Str(String s) {
+ int counter = 0;
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == ':') {
+ counter++;
+ if (i > 0) {
+ if (s.charAt(i - 1) == ':') {
+ return s;
+ }
+ }
+ }
+ }
+ if (counter != 7) {
+ return s + "::";
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Cleans a URI into a format suitable for passing to the PAC script.
+ * (meaning suitable for passing as {@code url} argument to
+ * {@code FindProxyForURL(url, host)} or {@code FindProxyForURLEx(url, host)}
+ * functions).
+ * <p>
+ * Because a PAC script is downloaded from a potentially malicious source it
+ * may contain harmful code. Therefore, the amount of information passed to
+ * the script should be limited to what is strictly necessary for the script
+ * to make decisions about choice of proxy. Anything in the URL which can
+ * potentially identity the user or which may contain session specific
+ * information should be removed before passing to script.
+ *
+ * <p>
+ * The following is removed:
+ * <ul>
+ * <li><i>{@code user-info}</i></li>
+ * <li><i>{@code path}</i> and everything that follows after</li>
+ * </ul>
+ *
+ * <p>
+ * Example:
+ * <pre>
+ * https://mary@netbeans.apache.org:8081/path/to/something?x1=Christmas&user=unknown
+ * becomes
+ * https://netbeans.apache.org:8081/
+ * </pre>
+ *
+ * <p>
+ * Note that the majority of PAC scripts out there do not make use of the
+ * {@code url} parameter at all. Instead they only use the {@code host}
+ * parameter. The stripping of information means that the {@code url}
+ * parameter only has two pieces of information that the {@code host}
+ * parameter doesn't have: protocol and port number.
+ * <br>
+ *
+ * @param uri URL to be cleansed
+ * @return stripped URL string
+ */
+ public static String toStrippedURLStr(URI uri) {
+
+ String portStr = (uri.getPort() == -1) ? "" : ":" + uri.getPort();
+ return
+ uri.getScheme()
+ + "://"
+ + uri.getHost()
+ + portStr
+ + "/"; // Chrome seems to always append the slash so we do it too
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java
new file mode 100644
index 0000000..b96d17d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Thrown whenever there are errors in the evaluation of the result
+ * returned from the {@code FindProxyForURL(url, host)} function
+ * in the PAC JavaScript.
+ *
+ * <p>
+ * In NetScape's original paper for the PAC script they define that
+ * the result of the function must be a string, made up of the following
+ * building blocks, separated by a semicolon:
+ * <pre>
+ * DIRECT|PROXY <host>:<port>|SOCKS <host>:<port>
+ * </pre>
+ *
+ * For example, a legal return value might be:
+ * <pre>
+ * PROXY anaconda:8099; PROXY 192.168.1.42:8100; DIRECT
+ * </pre>
+ *
+ *
+ * @author lbruun
+ */
+public class PacValidationException extends Exception {
+
+ public PacValidationException(String value, String msg) {
+ super("Cannot interpret value \"" + value + "\" : " + msg);
+ }
+
+ public PacValidationException(String msg) {
+ super(msg);
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java
new file mode 100644
index 0000000..782e1aa
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Calendar;
+
+/**
+ * Evaluates a date against a date range.
+ *
+ * - Years are 4-digit.
+ * - Months are zero-based (January = 0).
+ * - day of month is 1-31. The range for this value is deliberately not
+ * validated against date, meaning 31st of February is valid. This
+ * creates a lenient evaluation.
+ *
+ * Handles correctly the situation where a date range passes into next period.
+ * Examples:
+ *
+ * - Only month range is defined. monthMin=11 monthMax=02. The period from
+ * December 1st to end of February.
+ * - Only day of month range is defined. dateMin=27 dateMax=5. The days from the
+ * 27th of each month to the 5th of the next month.
+ *
+ * @author lbruun
+ */
+class DateRange {
+
+ private final static int UNDEFINED = -1;
+ private final int yearMin;
+ private final int yearMax;
+ private final int monthMin;
+ private final int monthMax;
+ private final int dateMin;
+ private final int dateMax;
+
+ public DateRange(int yearMin, int yearMax, int monthMin, int monthMax, int dateMin, int dateMax) {
+ this.yearMin = yearMin;
+ this.yearMax = ((yearMax == UNDEFINED) && (yearMin != UNDEFINED)) ? yearMin : yearMax;
+ this.monthMin = monthMin;
+ this.monthMax = ((monthMax == UNDEFINED) && (monthMin != UNDEFINED)) ? monthMin : monthMax;
+ this.dateMin = dateMin;
+ this.dateMax = ((dateMax == UNDEFINED) && (dateMin != UNDEFINED)) ? dateMin : dateMax;
+ }
+
+ public boolean isInRange(Calendar cal) {
+
+ int year = cal.get(Calendar.YEAR);
+ int month = cal.get(Calendar.MONTH);
+ int date = cal.get(Calendar.DATE);
+ if (yearDefined()) {
+ if (!(year >= yearMin && year <= yearMax)) {
+ return false;
+ }
+ }
+ if (monthDefined()) {
+ if (yearDefined()) {
+ if (year == yearMin) {
+ if (!(month >= monthMin)) {
+ return false;
+ }
+ }
+ if (year == yearMax) {
+ if (!(month <= monthMax)) {
+ return false;
+ }
+ }
+ } else {
+ if (monthMin <= monthMax) { // month range is fully within a year
+ if (!(month >= monthMin && month<= monthMax)) {
+ return false;
+ }
+ } else { // month range is rolling over into next year
+ if (!(month >= monthMin || month<= monthMax)) {
+ return false;
+ }
+ }
+ }
+ }
+ if (dateDefined()) {
+ boolean checkDateRange = false;
+ if (monthDefined()) {
+ if (yearDefined()) {
+ if ((year == yearMin && month == monthMin) || (year == yearMax && month == monthMax)) {
+ checkDateRange = true;
+ }
+ } else {
+ if (month == monthMin || month == monthMax) {
+ checkDateRange = true;
+ }
+ }
+ } else {
+ checkDateRange = true;
+ }
+ if (checkDateRange) {
+ if (dateMin <= dateMax) { // date range is fully within a month
+ if (!(date >= dateMin && date <= dateMax)) {
+ return false;
+ }
+ } else { // date range is rolling over into next month
+ if (!(date >= dateMin || date <= dateMax)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean yearDefined() {
+ return (!(yearMin == UNDEFINED && yearMax == UNDEFINED));
+ }
+ private boolean monthDefined() {
+ return (!(monthMin == UNDEFINED && monthMax == UNDEFINED));
+ }
+ private boolean dateDefined() {
+ return (!(dateMin == UNDEFINED && dateMax == UNDEFINED));
+ }
+
+
+ public static DateRangeBuilder getBuilder() {
+ return new DateRangeBuilder();
+ }
+
+ public static class DateRangeBuilder {
+ private int yearMin = UNDEFINED;
+ private int yearMax = UNDEFINED;
+ private int monthMin = UNDEFINED;
+ private int monthMax = UNDEFINED;
+ private int dateMin = UNDEFINED;
+ private int dateMax = UNDEFINED;
+
+ private DateRangeBuilder() {
+ }
+
+ public DateRangeBuilder setYear(int yearMin, int yearMax) {
+ this.yearMin = yearMin;
+ this.yearMax = yearMax;
+ return this;
+ }
+
+ public DateRangeBuilder setMonth(int monthMin, int monthMax) {
+ this.monthMin = monthMin;
+ this.monthMax = monthMax;
+ return this;
+ }
+
+ /**
+ * Sets the day of month restrictions. These values are generally
+ * between 1 and 31 but are not enforced by this class.
+ */
+ public DateRangeBuilder setDate(int dateMin, int dateMax) {
+ this.dateMin = dateMin;
+ this.dateMax = dateMax;
+ return this;
+ }
+
+ public DateRange createDateRange() {
+ return new DateRange(yearMin, yearMax, monthMin, monthMax, dateMin, dateMax);
+ }
+
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java
new file mode 100644
index 0000000..e08d2ce
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import org.netbeans.core.network.proxy.pac.PacHelperMethodsNetscape;
+
+/**
+ * Methods and constants useful in PAC script evaluation, specifically
+ * date/time related.
+ *
+ * @author lbruun
+ */
+public class PacUtilsDateTime {
+
+ /**
+ * List of valid weekday names as used in the Netscape specification.
+ * <p>
+ * Content: {@code SUN MON TUE WED THU FRI SAT}
+ *
+ */
+ public final static List<String> WEEKDAY_NAMES = Collections.unmodifiableList(
+ Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"));
+
+ /**
+ * List of valid month names as used in the Netscape specification.
+ * <p>
+ * Content: {@code JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC}
+ *
+ */
+ public final static List<String> MONTH_NAMES = Collections.unmodifiableList(
+ Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
+
+ private static final TimeZone UTC_TIME = TimeZone.getTimeZone("UTC");
+
+
+ /**
+ * Evaluates if now is within a weekday range. Method arguments are as described
+ * for {@link PacHelperMethodsNetscape#weekdayRange(Object...) }
+ *
+ * @param now
+ * @param args
+ * @return true if within range
+ * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+ */
+ public static boolean isInWeekdayRange(Date now, Object... args) throws PacDateTimeInputException {
+ int params = getNoOfParams(args);
+ boolean gmt = usesGMT(args);
+ Calendar cal = getCalendar(now, gmt);
+ if (gmt) {
+ params--;
+ }
+ if (!(params >=1 && params <=2)) {
+ throw new PacDateTimeInputException("invalid number of arguments");
+ }
+ final int wdNumMin;
+ final int wdNumMax;
+ if (params == 1) {
+ wdNumMin = getWeekday(args[0].toString());
+ wdNumMax = wdNumMin;
+ } else {
+ wdNumMin = getWeekday(args[0].toString());
+ wdNumMax = getWeekday(args[1].toString());
+ }
+
+ int wdNum = cal.get(Calendar.DAY_OF_WEEK);
+
+ if (wdNumMin <= wdNumMax) {
+ return ( wdNum >= wdNumMin && wdNum <= wdNumMax);
+ } else {
+ return ( wdNum >= wdNumMin || wdNum <= wdNumMax);
+ }
+ }
+
+
+ /**
+ * Evaluates if now is within a time range. Method arguments are as described
+ * for {@link PacHelperMethodsNetscape#timeRange(Object...) }
+ *
+ * @param now
+ * @param args
+ * @return true if within range
+ * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+ */
+ public static boolean isInTimeRange(Date now, Object... args) throws PacDateTimeInputException {
+ int params = getNoOfParams(args);
+ boolean gmt = usesGMT(args);
+ Calendar cal = getCalendar(now, gmt);
+ if (gmt) {
+ params--;
+ }
+ if (!(params >= 1 && params <= 6) || params == 5 || params == 3) {
+ throw new PacDateTimeInputException("invalid number of arguments");
+ }
+
+ TimeRange.TimeRangeBuilder trBld = TimeRange.getBuilder();
+
+ if (params == 1) {
+ trBld .setHourMinMax(getHour(args[0]), getHour(args[0]));
+ }
+ if (params == 2) {
+ trBld .setHourMinMax(getHour(args[0]),getHour(args[1]));
+ if (getHour(args[0]) != getHour(args[1])) {
+ trBld.setMinuteMinMax(0, 0);
+ }
+ }
+ if (params == 4) {
+ trBld .setHourMinMax(getHour(args[0]), getHour(args[2]))
+ .setMinuteMinMax(getMinute(args[1]),getMinute(args[3]))
+ .setSecondMinMax(0, 0);
+ }
+ if (params == 6) {
+ trBld .setHourMinMax(getHour(args[0]),getHour(args[3]))
+ .setMinuteMinMax(getMinute(args[1]),getMinute(args[4]))
+ .setSecondMinMax(getSecond(args[2]),getSecond(args[5]));
+ }
+ TimeRange timeRange = trBld.createTimeRange();
+
+ return timeRange.isInRange(cal);
+ }
+
+
+ /**
+ * Evaluates if now is within a date range. Method arguments are as described
+ * for {@link PacHelperMethodsNetscape#dateRange(Object...) }
+ *
+ * @param now
+ * @param args arguments
+ * @return true if within range
+ * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+ */
+ public static boolean isInDateRange(Date now, Object... args) throws PacDateTimeInputException {
+ int params = getNoOfParams(args);
+ boolean gmt = usesGMT(args);
+ Calendar cal = getCalendar(now, gmt);
+ if (gmt) {
+ params--;
+ }
+ if (!(params >= 1 && params <= 6) || params == 5 || params == 3) {
+ throw new PacDateTimeInputException("invalid number of arguments");
+ }
+
+ DateRange.DateRangeBuilder drBld = DateRange.getBuilder();
+ if (params == 1) {
+ if (isYear(args[0])) {
+ int y = getYear(args[0]);
+ drBld.setYear(y, y);
+ } else if (isMonth(args[0])) {
+ int m = getMonth(args[0].toString());
+ drBld.setMonth(m, m);
+ } else if (isDate(args[0])) {
+ int d = getDate(args[0]);
+ drBld.setDate(d, d);
+ } else {
+ throw new PacDateTimeInputException("invalid argument : " + args[0].toString());
+ }
+ }
+ if (params == 2) {
+ if (isYear(args[0])) {
+ drBld.setYear(getYear(args[0]), getYear(args[1]));
+ } else if (isMonth(args[0])) {
+ drBld.setMonth(getMonth(args[0].toString()), getMonth(args[1].toString()));
+ } else if (isDate(args[0])) {
+ drBld.setDate(getDate(args[0]), getDate(args[1]));
+ } else {
+ throw new PacDateTimeInputException("invalid argument : " + args[0].toString());
+ }
+ }
+ if (params == 4) {
+ if (isMonth(args[0])) {
+ drBld .setYear(getYear(args[1]), getYear(args[3]))
+ .setMonth(getMonth(args[0].toString()), getMonth(args[2].toString()));
+ } else if (isDate(args[0])) {
+ drBld .setMonth(getMonth(args[1].toString()), getMonth(args[3].toString()))
+ .setDate(getDate(args[0]), getDate(args[2]));
+ } else {
+ throw new PacDateTimeInputException("invalid argument : " + args[0].toString());
+ }
+ }
+ if (params == 6) {
+ drBld .setYear(getYear(args[2]), getYear(args[5]))
+ .setMonth(getMonth(args[1].toString()), getMonth(args[4].toString()))
+ .setDate(getDate(args[0]), getDate(args[3]));
+ }
+
+ DateRange dateRange = drBld.createDateRange();
+
+ return dateRange.isInRange(cal);
+ }
+
+
+
+ private static boolean isMonth(Object obj) {
+ return (obj instanceof CharSequence);
+ }
+
+ private static boolean isYear(Object obj) {
+ try {
+ int val = getInteger(obj);
+ return (val >= 1000);
+ } catch (PacDateTimeInputException ex) {
+ return false;
+ }
+ }
+
+ private static boolean isDate(Object obj) {
+ try {
+ int val = getInteger(obj);
+ return (val >= 1 && val <= 31);
+ } catch (PacDateTimeInputException ex) {
+ return false;
+ }
+ }
+
+ private static int getDate(Object obj) throws PacDateTimeInputException {
+ if (!isDate(obj)) {
+ throw new PacDateTimeInputException("value " + obj.toString() + " is not a valid day of month");
+ }
+ return getInteger(obj);
+ }
+
+ private static int getYear(Object obj) throws PacDateTimeInputException {
+ if (!isYear(obj)) {
+ throw new PacDateTimeInputException("value " + obj.toString() + " is not a valid year");
+ }
+ return getInteger(obj);
+ }
+
+ private static int getWeekday(String wd) throws PacDateTimeInputException {
+ int indexOf = WEEKDAY_NAMES.indexOf(wd);
+ if (indexOf == -1) {
+ throw new PacDateTimeInputException("Unknown weekday name : \"" + wd + "\"");
+ }
+ return indexOf+1; // In Calendar, the first weekday (Sunday) is 1
+ }
+
+ private static int getMonth(String month) throws PacDateTimeInputException {
+ int indexOf = MONTH_NAMES.indexOf(month);
+ if (indexOf == -1) {
+ throw new PacDateTimeInputException("Unknown month name : \"" + month + "\"");
+ }
+ return indexOf; // In Calendar, January is 0
+ }
+
+
+ private static int getInteger(Object obj) throws PacDateTimeInputException {
+ if (obj instanceof Integer || obj instanceof Long) {
+ return ((Number) obj).intValue();
+ }
+ if (obj instanceof String) {
+ try {
+ return Integer.parseInt((String) obj);
+ } catch (NumberFormatException ex) {
+ }
+ }
+ throw new PacDateTimeInputException("value is " + obj + " is not an integer");
+ }
+
+ private static int getHour(Object obj) throws PacDateTimeInputException {
+ int hour = getInteger(obj);
+ if (!(hour >= 0 && hour <= 23)) {
+ throw new PacDateTimeInputException("value is " + hour + " is not a valid hour of day (0-23)");
+ }
+ return hour;
+ }
+
+ private static int getMinute(Object obj) throws PacDateTimeInputException {
+ int min = getInteger(obj);
+ if (!(min >= 0 && min <= 59)) {
+ throw new PacDateTimeInputException("value is " + min + " is not a valid minute (0-59)");
+ }
+ return min;
+ }
+
+ private static int getSecond(Object obj) throws PacDateTimeInputException {
+ int sec = getInteger(obj);
+ if (!(sec >= 0 && sec <= 59)) {
+ throw new PacDateTimeInputException("value is " + sec + " is not a valid second (0-59)");
+ }
+ return sec;
+ }
+
+ private static Calendar getCalendar(Date now, boolean useGMT) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(now);
+ if (useGMT) {
+ cal.setTimeZone(UTC_TIME);
+ }
+ return cal;
+ }
+
+
+ /**
+ * Gets the number of true arguments passed to a JavaScript
+ * function. This is done by counting the number of arguments of type
+ * {@code Number} or {@code CharSequence}.
+ *
+ * <p>
+ * This is a convenience method useful when implementing
+ * {@link PacHelperMethodsNetscape#dateRange(java.lang.Object...) dateRange()}
+ * ,
+ * {@link PacHelperMethodsNetscape#timeRange(java.lang.Object...) timeRange()}
+ * or
+ * {@link PacHelperMethodsNetscape#weekdayRange(java.lang.Object...) weekdayRange()}
+ *
+ * <p>
+ * Note: In Nashorn, JavaScript function arguments that are not used in the
+ * call will have a type of {@code Undefined}.
+ *
+ * @param objs
+ * @return
+ */
+ public static int getNoOfParams(Object... objs) {
+ int params=0;
+ for(Object obj : objs) {
+ if (obj == null) { // don't really know if parameters
+ // will ever be null when the Java method is
+ // is invoked from JavaScript. I believe not, i.e
+ // they will be of class Undefined rather than null.
+ continue;
+ }
+ // Only parameters of type CharSequence (String) and
+ // Number (Integer, Long, etc) are relevant.
+ if ((obj instanceof Number) || (obj instanceof CharSequence)) {
+ params++;
+ }
+ }
+ return params;
+ }
+
+ /**
+ * Evaluates if the last parameter of a parameter array is "GMT".
+ * @param args parameters
+ * @return
+ */
+ public static boolean usesGMT(Object... args) {
+ int params = getNoOfParams(args);
+ if (args[params - 1] instanceof CharSequence) {
+ String p = args[params - 1].toString();
+ if (p.equals("GMT")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Validation errors on input to {@code weekdayRange()},
+ * {@code timeRange()} and {@code dateRange()}.
+ */
+ public static class PacDateTimeInputException extends Exception {
+ public PacDateTimeInputException(String msg) {
+ super(msg);
+ }
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java
new file mode 100644
index 0000000..c603ad0
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Calendar;
+
+/**
+ * Evaluates a time of day against a time range.
+ *
+ * - hour of day is 24 h based.
+ * - minute is 0-59.
+ * - second is 0-59.
+ *
+ * Handles correctly the situation where a time range passes into next period.
+ * Examples:
+ *
+ * - Only minute range is defined. minuteMin=55 minuteMax=05. This means
+ * a 10 minute interval around every hour change from 5 minutes before
+ * the hour, to 5 minutes after the hour.
+ * - Only second range is defined. secondMin=55 secondMax=05. This means
+ * a 10 second interval around every minute change, from 5 seconds before
+ * the minute change, to 5 seconds after the minute change.
+ * - Interval 23:35:00 to 00:25:00. This means a 55 minute interval around
+ * every day change.
+ *
+ * @author lbruun
+ */
+class TimeRange {
+
+ private final static int UNDEFINED = -1;
+ private final int hourMin;
+ private final int hourMax;
+ private final int minuteMin;
+ private final int minuteMax;
+ private final int secondMin;
+ private final int secondMax;
+
+ public TimeRange(int hourMin, int hourMax, int minuteMin, int minuteMax, int secondMin, int secondMax) {
+ this.hourMin = hourMin;
+ this.hourMax = ((hourMax == UNDEFINED) && (hourMin != UNDEFINED)) ? hourMin : hourMax;
+ this.minuteMin = minuteMin;
+ this.minuteMax = ((minuteMax == UNDEFINED) && (minuteMin != UNDEFINED)) ? minuteMin : minuteMax;
+ this.secondMin = secondMin;
+ this.secondMax = ((secondMax == UNDEFINED) && (secondMin != UNDEFINED)) ? secondMin : secondMax;
+ }
+
+ public boolean isInRange(Calendar cal) {
+
+ int hour = cal.get(Calendar.HOUR_OF_DAY);
+ int minute = cal.get(Calendar.MINUTE);
+ int second = cal.get(Calendar.SECOND);
+ if (hourDefined()) {
+ if (hourMin <= hourMax) {
+ if (!(hour >= hourMin && hour <= hourMax)) {
+ return false;
+ }
+ } else {
+ if (!(hour >= hourMin || hour <= hourMax)) {
+ return false;
+ }
+ }
+ }
+ if (minuteDefined()) {
+ if (hourDefined()) {
+ if (hour == hourMin) {
+ if (!(minute >= minuteMin)) {
+ return false;
+ }
+ }
+ if (hour == hourMax) {
+ if (!(minute <= minuteMax)) {
+ return false;
+ }
+ }
+ } else {
+ if (minuteMin <= minuteMax) { // minute range is fully within an hour
+ if (!(minute >= minuteMin && minute <= minuteMax)) {
+ return false;
+ }
+ } else { // minute range is passing into new hour
+ if (!(minute >= minuteMin || minute <= minuteMax)) {
+ return false;
+ }
+ }
+ }
+ }
+ if (secondDefined()) {
+ if (minuteDefined()) {
+ if (hourDefined()) {
+ if (hour == hourMin && minute == minuteMin) {
+ if (!(second >= secondMin)) {
+ return false;
+ }
+ }
+ if (hour == hourMax && minute == minuteMax) {
+ if (!(second <= secondMax)) {
+ return false;
+ }
+ }
+ } else {
+ if (minute == minuteMin) {
+ if (!(second >= secondMin)) {
+ return false;
+ }
+ }
+ if (minute == minuteMax) {
+ if (!(second <= secondMax)) {
+ return false;
+ }
+ }
+ }
+ } else { // only secondMin and secondMax are defined
+ if (secondMin <= secondMax) { // second range is fully within a minute
+ if (!(second >= secondMin && second <= secondMax)) {
+ return false;
+ }
+ } else { // second range is rolling over into next minute
+ if (!(second >= secondMin || second <= secondMax)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean hourDefined() {
+ return (!(hourMin == UNDEFINED && hourMax == UNDEFINED));
+ }
+ private boolean minuteDefined() {
+ return (!(minuteMin == UNDEFINED && minuteMax == UNDEFINED));
+ }
+ private boolean secondDefined() {
+ return (!(secondMin == UNDEFINED && secondMax == UNDEFINED));
+ }
+
+
+ public static TimeRangeBuilder getBuilder() {
+ return new TimeRangeBuilder();
+ }
+
+ public static class TimeRangeBuilder {
+ private int hourMin = UNDEFINED;
+ private int hourMax = UNDEFINED;
+ private int minuteMin = UNDEFINED;
+ private int minuteMax = UNDEFINED;
+ private int secondMin = UNDEFINED;
+ private int secondMax = UNDEFINED;
+
+ private TimeRangeBuilder() {}
+
+ public TimeRangeBuilder setHourMinMax(int hourMin, int hourMax) {
+ this.hourMin = hourMin;
+ this.hourMax = hourMax;
+ return this;
+ }
+
+ public TimeRangeBuilder setMinuteMinMax(int minuteMin, int minuteMax) {
+ this.minuteMin = minuteMin;
+ this.minuteMax = minuteMax;
+ return this;
+ }
+
+ public TimeRangeBuilder setSecondMinMax(int secondMin, int secondMax) {
+ this.secondMin = secondMin;
+ this.secondMax = secondMax;
+ return this;
+ }
+
+ public TimeRange createTimeRange() {
+ return new TimeRange(hourMin, hourMax, minuteMin, minuteMax, secondMin, secondMax);
+ }
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java
new file mode 100644
index 0000000..a84e812
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Proxy Auto-Config (PAC) script related, specifically around
+ * Java support for the JavaScript helper functions {@code dateRange()},
+ * {@code timeRange()} and {@code weekdayRange()}.
+ *
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java
new file mode 100644
index 0000000..04b2d4d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import jdk.nashorn.api.scripting.ClassFilter;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+
+/**
+ * Nashorn class filter which helps us create a sandboxed JavaScript execution
+ * environment which only has access to the Helper methods, nothing more.
+ *
+ * <p>Note that the ClassFilter feature is specific to Nashorn (Rhino had the
+ * {@code ClassShutter} class for this purpose), but the feature did not appear
+ * until Java 8u40.
+ *
+ * @author lbruun
+ */
+class ClassFilterPacHelpers implements ClassFilter {
+
+ @Override
+ public boolean exposeToScripts(String string) {
+ // The only Java class the PAC script is allowed to
+ // make use of is the PAC Helpers, nothing more.
+ return string.equals(PacHelperMethods.class.getName());
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java
new file mode 100644
index 0000000..4346a26
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+/**
+ * Auto-generates JavaScript source for declaration of
+ * helper functions.
+ *
+ * @author lbruun
+ */
+class HelperScriptFactory {
+
+ // Netscape functions
+ private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_NS = new JsHelperFunction[]{
+ new JsHelperFunction("isPlainHostName", new String[]{"host"}, Boolean.class),
+ new JsHelperFunction("dnsDomainIs", new String[]{"host", "domain"}, Boolean.class),
+ new JsHelperFunction("localHostOrDomainIs", new String[]{"host", "hostdom"}, Boolean.class),
+ new JsHelperFunction("isResolvable", new String[]{"host"}, Boolean.class),
+ new JsHelperFunction("isInNet", new String[]{"host", "pattern", "mask"}, Boolean.class),
+ new JsHelperFunction("dnsResolve", new String[]{"host"}, String.class),
+ new JsHelperFunction("myIpAddress", new String[]{}, String.class),
+ new JsHelperFunction("dnsDomainLevels", new String[]{"host"}, Integer.class),
+ new JsHelperFunction("shExpMatch", new String[]{"str", "shexp"}, Boolean.class),
+ new JsHelperFunction("weekdayRange", new String[]{"wd1", "wd2", "gmt"}, Boolean.class),
+ new JsHelperFunction("dateRange", new String[]{"day1", "month1", "year1", "day2", "month2", "year2", "gmt"}, Boolean.class),
+ new JsHelperFunction("timeRange", new String[]{"hour1", "min1", "sec1", "hour2", "min2", "sec2", "gmt"}, Boolean.class),
+ };
+
+ // Microsoft functions
+ private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_MS = new JsHelperFunction[]{
+ new JsHelperFunction("isResolvableEx", new String[]{"host"}, Boolean.class),
+ new JsHelperFunction("isInNetEx", new String[]{"host", "ipPrefix"}, Boolean.class),
+ new JsHelperFunction("dnsResolveEx", new String[]{"host"}, String.class),
+ new JsHelperFunction("myIpAddressEx", new String[]{}, String.class),
+ new JsHelperFunction("sortIpAddressList", new String[]{"ipAddressList"}, String.class),
+ new JsHelperFunction("getClientVersion", new String[]{}, String.class)
+ };
+
+ // Debug functions (not part of any spec)
+ private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_DEBUG = new JsHelperFunction[]{
+ new JsHelperFunction("alert", new String[]{"txt"}, Void.class)
+ };
+
+ private HelperScriptFactory() {
+ }
+
+ /**
+ * Gets JavaScript source with PAC Helper function declarations.
+ *
+ * @param bridgeObjectName name of Java object which contains Java methods,
+ * named similarly to the JavaScript PAC helper functions and with
+ * similar arg list. This Java object acts as the bridge between the
+ * JavaScript world and the Java world and must be an instance
+ * of {@link org.netbeans.network.proxy.pac.PacHelperMethods PacHelperMethods}.
+ *
+ * @return JavaScript source code
+ */
+ public static String getPacHelperSource(String bridgeObjectName) {
+ StringBuilder sb = new StringBuilder(2000);
+ addFunctionDecls(sb, JS_HELPER_FUNCTIONS_NS, bridgeObjectName);
+ addFunctionDecls(sb, JS_HELPER_FUNCTIONS_MS, bridgeObjectName);
+ addFunctionDecls(sb, JS_HELPER_FUNCTIONS_DEBUG, bridgeObjectName);
+ return sb.toString();
+ }
+
+
+ private static void addFunctionDecls(StringBuilder sb, JsHelperFunction[] jsHelperFunctions, String bridgeObjectName) {
+ for (JsHelperFunction f : jsHelperFunctions) {
+ sb.append("function ");
+ sb.append(f.functionName);
+ sb.append('(');
+ addArgList(sb, f.argList);
+ sb.append(") {");
+ sb.append('\n');
+ sb.append(" return ");
+ boolean encloseReturnValue = false;
+ if (Number.class.isAssignableFrom(f.getClass())) {
+ encloseReturnValue = true;
+ sb.append("Number(");
+ }
+ if (f.returnType == String.class) {
+ encloseReturnValue = true;
+ sb.append("String(");
+ }
+ sb.append(bridgeObjectName);
+ sb.append('.');
+ sb.append(f.functionName);
+ sb.append('(');
+ addArgList(sb, f.argList);
+ sb.append(')');
+ if (encloseReturnValue) {
+ sb.append(')');
+ }
+ sb.append(";\n");
+ sb.append("}\n\n");
+ }
+ }
+
+ private static void addArgList(StringBuilder sb, String[] argList) {
+ if (argList != null && argList.length > 0) {
+ for (int i = 0; i < argList.length; i++) {
+ sb.append(argList[i]);
+ if (i < argList.length - 1) {
+ sb.append(", ");
+ }
+ }
+ }
+ }
+
+ private static class JsHelperFunction {
+ String functionName;
+ String[] argList;
+ Class returnType;
+
+ public JsHelperFunction(String functionName, String[] argList, Class returnType) {
+ this.functionName = functionName;
+ this.argList = argList;
+ this.returnType = returnType;
+ }
+
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java
new file mode 100644
index 0000000..874f2d4
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java
@@ -0,0 +1,343 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.IpAddressUtils.IpTypePreference;
+import org.netbeans.core.network.utils.LocalAddressUtils;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+import org.netbeans.core.network.proxy.pac.PacUtils;
+import org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * NetBeans default implementation of a the PAC 'helper functions'.
+ *
+ * <p>
+ * This implementation aims to be as complete and compatible as possible. It
+ * implements both the original specification from Netscape, plus the additions
+ * from Microsoft.
+ *
+ * <ul>
+ * <li>Complete. Implements all methods from original Netscape specification
+ * as well as all IPv6 aware additions from Microsoft.</li>
+ * <li>Compatible. Aims for maximum compatibility with all browser
+ * implementations and aims to never fail and never stall.</li>
+ * </ul>
+ *
+ * @author lbruun
+ */
+@ServiceProvider(service=PacHelperMethods.class)
+public class NbPacHelperMethods extends PacHelperMethods {
+
+ private static final Logger LOGGER = Logger.getLogger(NbPacHelperMethods.class.getName());
+
+ /**
+ * Timeout (milliseconds) used for name service lookups.
+ */
+ public static final int DNS_TIMEOUT_MS = 4000;
+
+
+ // *************************************************************
+ // Official helper functions.
+ //
+ // These are pure-Java implementations of the JavaScript
+ // helper functions defined in Netscape's original document.
+ // *************************************************************
+
+ @Override
+ public boolean isPlainHostName(String host) {
+ return !host.contains(".");
+ }
+
+ @Override
+ public boolean dnsDomainIs(String host, String domain) {
+ int dotPos = host.indexOf(".");
+ if (dotPos != -1 && (dotPos < host.length()-1)) {
+ if (host.substring(dotPos).equals(domain)) {
+ return true;
+ }
+ if (host.substring(dotPos+1).equals(domain)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ @Override
+ public boolean localHostOrDomainIs(String host, String hostdom) {
+ if (host.equals(hostdom)) {
+ return true;
+ }
+ return host.equals(getDomains(hostdom)[0]);
+ }
+
+ @Override
+ public boolean isResolvable(String host) {
+ try {
+ IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.IPV4_ONLY);
+ return true;
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public String dnsResolve(String host) {
+ try {
+ return IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.IPV4_ONLY)
+ .getHostAddress();
+
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ // Returning null is what Chrome and Firefox do in this situation
+ return null;
+ }
+ }
+
+ @Override
+ public String myIpAddress() {
+ return LocalAddressUtils.getMostLikelyLocalInetAddress(IpTypePreference.IPV4_ONLY).getHostAddress();
+ }
+
+
+ @Override
+ public boolean isInNet(String host, String pattern, String mask) {
+ try {
+ String hostIP = IpAddressUtils.nameResolve(host, 4000, IpTypePreference.IPV4_ONLY).getHostAddress();
+ String[] hostIPElements = hostIP.split("\\.");
+ String[] patternElements = pattern.split("\\.");
+ String[] maskElements = mask.split("\\.");
+
+ for (int i = 0; i < hostIPElements.length; i++) {
+ if (i < maskElements.length && maskElements[i].trim().equals("255")) {
+ if (!hostIPElements[i].trim().equals(patternElements[i].trim())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public int dnsDomainLevels(String host) {
+ if (host == null) {
+ return 0;
+ }
+ return getNoOfOccurrences(host, '.');
+ }
+
+ @Override
+ public boolean shExpMatch(String str, String shexp) {
+ Pattern pattern = PacUtils.createRegexPatternFromGlob(shexp);
+ return pattern.matcher(str).matches();
+ }
+
+
+ @Override
+ public boolean weekdayRange(Object... args) {
+ try {
+ return PacUtilsDateTime.isInWeekdayRange(new Date(), args);
+ } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+ LOGGER.log(Level.WARNING, "PAC script error : arguments passed to weekdayRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+ return false;
+ }
+ }
+
+ @Override
+ public boolean dateRange(Object... args) {
+ try {
+ return PacUtilsDateTime.isInDateRange(new Date(), args);
+ } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+ LOGGER.log(Level.WARNING, "PAC script error : arguments passed to dateRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+ return false;
+ }
+ }
+
+ @Override
+ public boolean timeRange(Object... args) {
+ try {
+ return PacUtilsDateTime.isInTimeRange(new Date(), args);
+ } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+ LOGGER.log(Level.WARNING, "PAC script error : arguments passed to timeRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+ return false;
+ }
+ }
+
+
+ // *************************************************************
+ // Microsoft extensions
+ //
+ // *************************************************************
+
+
+ @Override
+ public boolean isResolvableEx(String host) {
+ try {
+ IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF)
+ .getHostAddress();
+ return true;
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public String dnsResolveEx(String host) {
+ try {
+ return IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF)
+ .getHostAddress();
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ return "";
+ }
+ }
+
+ @Override
+ public String myIpAddressEx() {
+ InetAddress[] addresses = LocalAddressUtils.getMostLikelyLocalInetAddresses(IpTypePreference.ANY_JDK_PREF);
+ return PacUtils.toSemiColonListInetAddress(addresses);
+ }
+
+ @Override
+ public String sortIpAddressList(String ipAddressList) {
+ if (ipAddressList == null) {
+ return "";
+ }
+
+ // We convert to InetAddress (because we know how to sort
+ // those) but at the same time we have to preserve the way
+ // the original input was represented and return in the same
+ // format.
+ String[] strComps = ipAddressList.split(";");
+ List<InetAddress> addressesB = new ArrayList<>();
+ for(String s : strComps) {
+ try {
+ addressesB.add(InetAddress.getByName(s.trim()));
+ } catch (UnknownHostException ex) {
+ return "";
+ }
+ }
+ List<InetAddress> addressesS = new ArrayList<>(addressesB);
+ IpAddressUtils.sortIpAddresses(addressesS, false);
+
+ List<String> addressesStr = new ArrayList<>(addressesS.size());
+ for (InetAddress addr : addressesS) {
+ addressesStr.add(strComps[addressesB.indexOf(addr)].trim());
+ }
+
+ return PacUtils.toSemiColonList(addressesStr);
+ }
+
+ @Override
+ public String getClientVersion() {
+ return "1.0";
+ }
+
+ @Override
+ public boolean isInNetEx(String ipAddress, String ipPrefix) {
+ if (ipAddress == null) {
+ return false;
+ }
+ if (ipPrefix == null) {
+ return false;
+ }
+
+ InetAddress iAddr;
+ try {
+ iAddr = IpAddressUtils.nameResolve(ipAddress, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF);
+ } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+ return false;
+ }
+
+ String[] ipPrefixes = ipPrefix.split(";");
+ for(String ipP : ipPrefixes) {
+ if (PacUtils.ipPrefixMatch(iAddr, ipP)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+ // *************************************************************
+ // Utility functions.
+ //
+ // Other functions - not defined in PAC spec, but still
+ // exposed to the JavaScript engine.
+ // *************************************************************
+
+ @Override
+ public void alert(String message) {
+ LOGGER.log(Level.INFO, "PAC script says : {0}", message);
+ }
+
+
+ // *************************************************************
+ //
+ // Other methods.
+ // Not exposed to JavaScript engine.
+ //
+ // *************************************************************
+
+
+
+ private static String[] getDomains(String host) {
+ String[] doms = host.split("\\.");
+ List<String> domsList = new ArrayList<>(doms.length);
+ for (String dom : doms) {
+ if (dom != null && (!dom.isEmpty())) {
+ domsList.add(dom);
+ }
+ }
+ return domsList.toArray(new String[domsList.size()]);
+ }
+
+
+ /**
+ * Count no of occurrences of ch in str.
+ * @param str
+ * @param ch
+ * @return
+ */
+ private static int getNoOfOccurrences(String str, char ch) {
+ int counter = 0;
+ for (int i = 0; i < str.length(); i++) {
+ if (str.charAt(i) == ch) {
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java
new file mode 100644
index 0000000..c8bcfdd
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java
@@ -0,0 +1,531 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.script.Bindings;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.netbeans.core.network.utils.SimpleObjCache;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+import org.netbeans.core.network.proxy.pac.PacJsEntryFunction;
+import org.netbeans.core.network.proxy.pac.PacValidationException;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.openide.util.Exceptions;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacUtils;
+import org.openide.util.Lookup;
+
+/**
+ * NetBeans implementation of a PAC script evaluator. This implementation
+ * is the one returned by {@link NbPacScriptEvaluatorFactory}.
+ *
+ * <h3>Features comparison</h3>
+ * There are differences between how browsers have implemented the PAC
+ * evaluation functionality. In the following the Apache NetBeans implementation
+ * (this class) is pitched against some of the major browsers.<br><br>
+ *
+ * <table summary="" style="table-layout: fixed; width:100%;" border="1" cellpadding="10" cellspacing="0">
+ * <tr><th class="tablersh">Behavior</th>
+ * <th>Apache<br>NetBeans</th>
+ * <th>Chrome<br>{@code 61.0.3163.100}</th>
+ * <th>Firefox<br>{@code 56.0.1}</th>
+ * <th>IE 11<br>{@code 11.608.15063.0}</th>
+ * <th>Edge<br>{@code 40.15063.0.0}</th>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Entry point functions supported:<br>{@code FindProxyForURL()} and {@code FindProxyForURLEx()}</td>
+ * <td>Both are supported. If both exist in the same PAC script then {@code FindProxyForURLEx()} is used.</td>
+ * <td>Only {@code FindProxyForURL()}</td>
+ * <td>Only {@code FindProxyForURL()}</td>
+ * <td>Both.<br>Only one of them may be present.</td>
+ * <td>Both.<br>Only one of them may be present.</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Security: Sandboxed execution of PAC script</td>
+ * <td>Yes</td>
+ * <td>???</td>
+ * <td>???</td>
+ * <td>Yes</td>
+ * <td>Yes</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Security: Stripped URL<br>(Value passed in {@code url} parameter)</td>
+ * <td>Yes<sup>1</sup></td>
+ * <td>Yes<sup>1</sup><br>(but strangely not for HTTP, only for HTTPS)</td>
+ * <td>Yes<sup>2</sup></td>
+ * <td>Yes</td>
+ * <td>Yes</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Extended return value support<br>Netscape spec only allowed:<ul><li>{@code DIRECT}</li><li>{@code PROXY host:port}</li><li>{@code SOCKS host:port}</li></ul></td>
+ * <td>In addition:<sup>3</sup><ul><li>{@code HTTP host:port}</li><li>{@code HTTPS host:port}</li><li>{@code SOCKS4 host:port}</li><li>{@code SOCKS5 host:port}</li></ul></td>
+ * <td>???</td>
+ * <td>In addition:<ul><li>{@code HTTP host:port}</li><li>{@code HTTPS host:port}</li><li>{@code SOCKS4 host:port}</li><li>{@code SOCKS5 host:port}</li></ul></td>
+ * <td>???</td>
+ * <td>???</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Uses result cache</td>
+ * <td>Yes<br>(based on {@code url} value)</td>
+ * <td>Yes</td>
+ * <td>???</td>
+ * <td>Yes<br>(based on {@code host} value)</td>
+ * <td>???</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Support for <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/gg308476(v=vs.85).aspx">getClientVersion()</a></td>
+ * <td>Yes<br>(returns "1.0")</td>
+ * <td>No</td>
+ * <td>No</td>
+ * <td>Yes<br>(returns "1.0")</td>
+ * <td>Yes<br>(returns "1.0")</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Use of result cache
+ * when script uses time sensitive functions:<br>
+ * {@code weekdayRange()}, {@code dateRange()} and {@code timeRange()}
+ * </td>
+ * <td>Result cache not used</td>
+ * <td>???</td>
+ * <td>???</td>
+ * <td>???</td>
+ * <td>???</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Name lookup timeout<sup>4</sup></td>
+ * <td>Explicit.<br>(see {@link NbPacHelperMethods#DNS_TIMEOUT_MS DNS_TIMEOUT_MS})</td>
+ * <td>???</td>
+ * <td>Yes</td>
+ * <td>???</td>
+ * <td>???</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">Support for Date/Time functions with range crossing a boundary<sup>5</sup></td>
+ * <td>Yes</td>
+ * <td>???</td>
+ * <td>Yes<br>since v49</td>
+ * <td>???</td>
+ * <td>???</td>
+ * </tr>
+ * <tr>
+ * <td class="tablersh">myIpAddress()<sup>6</sup></td>
+ * <td>Pretty good. Uses JNI. (At least far better than text book Java approach which consistently yields incorrect result)</td>
+ * <td>Uses UDP to try to find IP address. Seems to be reliable.</td>
+ * <td>???</td>
+ * <td>???</td>
+ * <td>???</td>
+ * </tr>
+ * </table>
+ * 1) The following is removed: <i>{@code user-info}</i> and everything after the
+ * host name, however the value passed to the script always ends with a '/'
+ * character.<br>
+ * 2) Same as (1), except that <i>{@code user-info}</i> is not removed.<br>
+ * 3) However, when converted to Java {@link java.net.Proxy} object, all return
+ * types must be mapped to Java Proxy types {@code DIRECT},
+ * {@code HTTP} and {@code SOCKS}, which means some finer grained information
+ * is lost. (but is irrelevant)<br>
+ * 4) An implementation not using an explicit timeout will be at the mercy of
+ * the underlying OS or runtime environment. For example, for 2 name servers,
+ * the default timeout in JRE is 30 seconds.<br>
+ * 5) If date/time functions ({@code weekdayRange()}, {@code dateRange()} and
+ * {@code timeRange()}) allow values that crosses a value boundary.
+ * For example {@code timeRange(22,3}) for the range from 10 pm to 3 am, or
+ * {@code dateRange("DEC","MAR"}) for the range from Dec 1st to March 31st.<br>
+ * 6) How good is the {@code myIpAddress()} (or {@code myIpAddressEx()}) function
+ * at finding the host's correct IP address, in particular on a multi-homed
+ * computer.
+ *
+ * <h3>Customization</h3>
+ * The implementation for
+ * {@link org.netbeans.core.network.proxy.pac.PacHelperMethods PacHelperMethods} is
+ * found via the global lookup. If you are unhappy with the implementation
+ * of the Helper Functions you can replace with your own.
+ *
+ * @author lbruun
+ */
+public class NbPacScriptEvaluator implements PacScriptEvaluator {
+
+ private static final Logger LOGGER = Logger.getLogger(NbPacScriptEvaluator.class.getName());
+
+
+ private static final String JS_HELPER_METHODS_INSTANCE_NAME = "jsPacHelpers";
+
+ private final boolean canUseURLCaching;
+ private final PacScriptEngine scriptEngine;
+ private final SimpleObjCache<URI,List<Proxy>> resultCache;
+
+ private static final String PAC_PROXY = "PROXY";
+ private static final String PAC_DIRECT = "DIRECT";
+ private static final String PAC_SOCKS = "SOCKS";
+ private static final String PAC_SOCKS4_FFEXT = "SOCKS4"; // Mozilla Firefox extension. Not part of original Netscape spec.
+ private static final String PAC_SOCKS5_FFEXT = "SOCKS5"; // Mozilla Firefox extension. Not part of original Netscape spec.
+ private static final String PAC_HTTP_FFEXT = "HTTP"; // Mozilla Firefox extension. Not part of original Netscape spec.
+ private static final String PAC_HTTPS_FFEXT = "HTTPS"; // Mozilla Firefox extension. Not part of original Netscape spec.
+ private final boolean nashornJava8u40Available;
+ private final String pacScriptSource;
+
+
+ public NbPacScriptEvaluator(String pacSourceCocde) throws PacParsingException {
+ this.pacScriptSource = pacSourceCocde;
+ nashornJava8u40Available = getNashornJava8u40Available();
+ scriptEngine = getScriptEngine(pacSourceCocde);
+ canUseURLCaching = !usesTimeDateFunctions(pacSourceCocde);
+ if (canUseURLCaching) {
+ resultCache = new SimpleObjCache<>(100);
+ } else {
+ resultCache = null;
+ }
+ }
+
+ @Override
+ public List<Proxy> findProxyForURL(URI uri) throws PacValidationException {
+
+ List<Proxy> jsResultAnalyzed;
+
+ // First try the cache
+ if (resultCache != null) {
+ jsResultAnalyzed = resultCache.get(uri);
+ if (jsResultAnalyzed != null) {
+ return jsResultAnalyzed;
+ }
+ }
+ try {
+ Object jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost());
+ jsResultAnalyzed = analyzeResult(uri, jsResult);
+ if (canUseURLCaching && (resultCache != null)) {
+ resultCache.put(uri, jsResultAnalyzed); // save the result in the cache
+ }
+ return jsResultAnalyzed;
+ } catch (NoSuchMethodException ex) {
+ // If this exception occur at this time it is really, really unexpected.
+ // We already gave the function a test spin in the constructor.
+ Exceptions.printStackTrace(ex);
+ return Collections.singletonList(Proxy.NO_PROXY);
+ } catch (ScriptException ex) {
+ LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
+ return Collections.singletonList(Proxy.NO_PROXY);
+ } catch (Exception ex) { // for runtime exceptions
+ if (ex.getCause() != null) {
+ if (ex.getCause() instanceof ClassNotFoundException) {
+ // Is someone trying to break out of the sandbox ?
+ LOGGER.log(Level.WARNING, "The downloaded PAC script is attempting to access Java class ''{0}'' which may be a sign of maliciousness. You should investigate this with your network administrator.", ex.getCause().getMessage());
+ return Collections.singletonList(Proxy.NO_PROXY);
+ }
+ }
+ // other unforseen errors
+ LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
+ return Collections.singletonList(Proxy.NO_PROXY);
+ }
+ }
+
+ @Override
+ public boolean usesCaching() {
+ return (canUseURLCaching && (resultCache != null));
+ }
+
+ @Override
+ public String getJsEntryFunction() {
+ return scriptEngine.getJsMainFunction().getJsFunctionName();
+ }
+
+ @Override
+ public String getEngineInfo() {
+ ScriptEngineFactory factory = scriptEngine.getScriptEngine().getFactory();
+ return factory.getEngineName() + " version " + factory.getEngineVersion();
+ }
+
+ @Override
+ public String getPacScriptSource() {
+ return this.pacScriptSource;
+ }
+
+
+
+ private PacScriptEngine getScriptEngine(String pacSource) throws PacParsingException {
+
+ try {
+ String helperJSScript = getHelperJsScriptSource();
+ LOGGER.log(Level.FINER, "PAC Helper JavaScript :\n{0}", helperJSScript);
+
+ ScriptEngine engine;
+ if (nashornJava8u40Available) {
+ engine = getNashornJSScriptEngine();
+ } else {
+ engine = getGenericJSScriptEngine();
+ }
+
+ LOGGER.log(Level.FINE, "PAC script evaluator using: {0}", getEngineInfo(engine));
+
+
+ PacHelperMethods pacHelpers = Lookup.getDefault().lookup(PacHelperMethods.class);
+ if (pacHelpers == null) { // this should be redundant but we take no chances
+ pacHelpers = new NbPacHelperMethods();
+ }
+ Bindings b = engine.createBindings();
+ b.put(JS_HELPER_METHODS_INSTANCE_NAME, pacHelpers);
+ engine.setBindings(b, ScriptContext.ENGINE_SCOPE);
+
+ engine.eval(pacSource);
+ engine.eval(helperJSScript);
+
+ // Do some minimal testing of the validity of the PAC Script.
+ final PacJsEntryFunction jsMainFunction;
+ if (nashornJava8u40Available) {
+ jsMainFunction = testScriptEngine(engine, true);
+ } else {
+ jsMainFunction = testScriptEngine(engine, false);
+ }
+
+ return new PacScriptEngine(engine, jsMainFunction);
+ } catch (ScriptException ex) {
+ throw new PacParsingException(ex);
+ }
+ }
+
+ private boolean getNashornJava8u40Available() {
+ try {
+ Class<?> klass = Class.forName("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ return true;
+ }
+
+ private ScriptEngine getNashornJSScriptEngine() {
+ NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+ return factory.getScriptEngine(new ClassFilterPacHelpers());
+ }
+
+ private ScriptEngine getGenericJSScriptEngine() {
+ // The result of the statements below may be Rhino, but more likely
+ // - since Java 8 - it will be a Nashorn engine.
+ ScriptEngineManager factory = new ScriptEngineManager();
+ return factory.getEngineByName("JavaScript");
+ }
+
+ /**
+ * Test if the main entry point, function FindProxyForURL()/FindProxyForURLEx(),
+ * is available.
+ */
+ private PacJsEntryFunction testScriptEngine(ScriptEngine eng, boolean doDeepTest) throws PacParsingException {
+ if (isJsFunctionAvailable(eng, PacJsEntryFunction.IPV6_AWARE.getJsFunctionName(), doDeepTest)) {
+ return PacJsEntryFunction.IPV6_AWARE;
+ }
+ if (isJsFunctionAvailable(eng, PacJsEntryFunction.STANDARD.getJsFunctionName(), doDeepTest)) {
+ return PacJsEntryFunction.STANDARD;
+ }
+ throw new PacParsingException("Function " + PacJsEntryFunction.STANDARD.getJsFunctionName() + " or " + PacJsEntryFunction.IPV6_AWARE.getJsFunctionName() + " not found in PAC Script.");
+ }
+
+ private boolean isJsFunctionAvailable(ScriptEngine eng, String functionName, boolean doDeepTest) {
+ // We want to test if the function is there, but without actually
+ // invoking it.
+ Object obj = eng.get(functionName);
+
+ if (!doDeepTest && obj != null) {
+ // Shallow test. We've established that there's
+ // "something" in the ENGINE_SCOPE with a name like
+ // functionName, and we *hope* it is a function, but we really don't
+ // know, therefore we call it a shallow test.
+ return true;
+ }
+
+ // For Nashorn post JDK8u40 we can do even deeper validation
+ // using the ScriptObjectMirror class. This will not work for Rhino.
+ if (doDeepTest && obj != null) {
+ if (obj instanceof ScriptObjectMirror) {
+ ScriptObjectMirror som = (ScriptObjectMirror) obj;
+ if (som.isFunction()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ private String getHelperJsScriptSource() throws PacParsingException {
+ return HelperScriptFactory.getPacHelperSource(JS_HELPER_METHODS_INSTANCE_NAME);
+ }
+
+
+ /**
+ * Does the script source make reference to any of the date/time functions
+ * (timeRange(), dateRange(), weekdayRange()) ?
+ *
+ * @param pacScriptSource
+ * @return
+ */
+ private static boolean usesTimeDateFunctions(String pacScriptSource) {
+ // Will be called only once so there's little to be gained by precompiling
+ // the regex statement.
+
+ Pattern pattern = Pattern.compile(".*(timeRange\\s*\\(|dateRange\\s*\\(|weekdayRange\\s*\\().*", Pattern.DOTALL);
+ Matcher matcher = pattern.matcher(pacScriptSource);
+ return matcher.matches();
+ }
+
+ private String getEngineInfo(ScriptEngine engine) {
+ StringBuilder sb = new StringBuilder();
+ ScriptEngineFactory f = engine.getFactory();
+ sb.append("LanguageName=");
+ sb.append("\"").append(f.getLanguageName()).append("\"");
+ sb.append(" ");
+ sb.append("LanguageVersion=");
+ sb.append("\"").append(f.getLanguageVersion()).append("\"");
+ sb.append(" ");
+ sb.append("EngineName=");
+ sb.append("\"").append(f.getEngineName()).append("\"");
+ sb.append(" ");
+ sb.append("EngineNameAliases=");
+ sb.append(Arrays.toString(f.getNames().toArray(new String[f.getNames().size()])));
+ sb.append(" ");
+ sb.append("EngineVersion=");
+ sb.append("\"").append(f.getEngineVersion()).append("\"");
+ return sb.toString();
+ }
+
+ /**
+ * Translates result from JavaScript into list of java proxy types.
+ *
+ * The string returned from the JavaScript function (input to this
+ * method) can contain any number of the following building blocks,
+ * separated by a semicolon:
+ *
+ * DIRECT
+ * Connections should be made directly, without any proxies.
+ *
+ * PROXY host:port
+ * The specified proxy should be used.
+ *
+ * SOCKS host:port
+ * The specified SOCKS server should be used.
+ *
+ *
+ * @param uri
+ * @param proxiesString
+ * @return
+ */
+ private List<Proxy> analyzeResult(URI uri, Object proxiesString) throws PacValidationException {
+ if (proxiesString == null) {
+ LOGGER.log(Level.FINE, "Null result for {0}", uri);
+ return null;
+ }
+
+ StringTokenizer st = new StringTokenizer(proxiesString.toString(), ";"); //NOI18N
+ List<Proxy> proxies = new LinkedList<>();
+ while (st.hasMoreTokens()) {
+ String proxySpec = st.nextToken().trim();
+ proxies.add(getProxy(proxySpec));
+ }
+ return proxies;
+ }
+
+ private static Proxy getProxy(String proxySpec) throws PacValidationException {
+ if (proxySpec.equals(PAC_DIRECT)) {
+ return Proxy.NO_PROXY;
+ }
+
+ String[] ele = proxySpec.split(" +"); // NOI18N
+ if (ele.length != 2) {
+ throw new PacValidationException("The value \"" + proxySpec + "\" has incorrect format");
+ }
+ final Proxy.Type proxyType;
+ switch(ele[0]) {
+ case PAC_PROXY:
+ case PAC_HTTP_FFEXT:
+ case PAC_HTTPS_FFEXT:
+ proxyType = Proxy.Type.HTTP;
+ break;
+ case PAC_SOCKS:
+ case PAC_SOCKS4_FFEXT:
+ case PAC_SOCKS5_FFEXT:
+ proxyType = Proxy.Type.SOCKS;
+ break;
+ default:
+ throw new PacValidationException("The value \"" + ele[0] + "\" is an unknown proxy type");
+ }
+
+ String hostAndPortNo = ele[1];
+ int i = hostAndPortNo.lastIndexOf(":"); // NOI18N
+ if (i <= 0 || i == (hostAndPortNo.length() - 1)) {
+ throw new PacValidationException("The string \"" + ele[1] + "\" has no port number");
+ }
+
+ String host = hostAndPortNo.substring(0, i);
+ String portStr = hostAndPortNo.substring(i+1);
+
+ int portNo = -1;
+ try {
+ portNo = Integer.parseInt(portStr);
+ } catch (NumberFormatException ex) {
+ throw new PacValidationException("The portno value \"" + portStr + "\" cannot be converted to an integer");
+ }
+
+ return new Proxy(proxyType, new InetSocketAddress(host, portNo));
+ }
+
+
+ private static class PacScriptEngine {
+ private final ScriptEngine scriptEngine;
+ private final PacJsEntryFunction jsMainFunction;
+ private final Invocable invocable;
+
+ public PacScriptEngine(ScriptEngine scriptEngine, PacJsEntryFunction jsMainFunction) {
+ this.scriptEngine = scriptEngine;
+ this.jsMainFunction = jsMainFunction;
+ this.invocable = (Invocable) scriptEngine;
+ }
+
+ public PacJsEntryFunction getJsMainFunction() {
+ return jsMainFunction;
+ }
+
+ public ScriptEngine getScriptEngine() {
+ return scriptEngine;
+ }
+
+ public Invocable getInvocable() {
+ return invocable;
+ }
+
+ public Object findProxyForURL(String url, String host) throws ScriptException, NoSuchMethodException {
+ return invocable.invokeFunction(jsMainFunction.getJsFunctionName(), url, host);
+ }
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java
new file mode 100644
index 0000000..585e0e4
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorNoProxy;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory;
+import org.openide.util.lookup.ServiceProvider;
+
+
+
+/**
+ * NetBeans default implementation of {@code PacScriptEvaluatorFactory}.
+ *
+ * <p>Returns an instance of {@link NbPacScriptEvaluator}.
+ *
+ * @author lbruun
+ */
+@ServiceProvider(service = PacScriptEvaluatorFactory.class)
+public class NbPacScriptEvaluatorFactory implements PacScriptEvaluatorFactory {
+
+ @Override
+ public PacScriptEvaluator createPacScriptEvaluator(String pacSource) throws PacParsingException {
+
+ if (pacSource == null || pacSource.isEmpty()) {
+ return new PacScriptEvaluatorNoProxy();
+ }
+ return new NbPacScriptEvaluator(pacSource);
+ }
+
+ @Override
+ public PacScriptEvaluator getNoOpEvaluator() {
+ return new PacScriptEvaluatorNoProxy();
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java
new file mode 100644
index 0000000..b5c69db
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/**
+ * NetBeans default implementation of a PAC script evaluation
+ * environment.
+ *
+ */
+package org.netbeans.core.network.proxy.pac.impl;
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java
new file mode 100644
index 0000000..6e76439
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Proxy Auto-Config (PAC) script related.
+ *
+ * <p>
+ * A Proxy Auto-Config (PAC) script is a piece of JavaScript code
+ * which is typically downloaded from the network. It is meant to be
+ * evaluated on the client. The role of the PAC is to determine which
+ * proxy to use for a given URL.
+ *
+ * <p>
+ * Netscape wrote the original specification for the PAC script in 1995. The
+ * specification has never been ratified as a standard but is widely implemented
+ * on corporate Local Area Networks as a way to have a centralized and
+ * configurable proxy setup.
+ */
+package org.netbeans.core.network.proxy.pac;
diff --git a/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java b/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java
new file mode 100644
index 0000000..d974944
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import com.sun.jna.Platform;
+
+/**
+ * Utility functions for finding the local computer's name.
+ *
+ *
+ * @author lbruun
+ */
+public class HostnameUtils {
+
+ private HostnameUtils() {}
+
+
+ /**
+ * Gets the name which is likely to be the local host's primary
+ * name on the network.
+ *
+ * <p>
+ * IMPLEMENTATION:
+ * <p>
+ * <ul>
+ * <li>On Unix-like OSes (incl Mac OS X) this is the value as returned from
+ * the {@code gethostname()} function from the standard C Library. </li>
+ * <li>On Windows it is the value as returned from the
+ * {@code gethostname()} function from {@code Ws2_32} library.
+ * (without domain name). Note that this Windows function will do a
+ * name service lookup and the method is therefore potentially blocking,
+ * although it is more than likely that Windows has cached this result
+ * on computer startup in its DNS Client Cache and therefore the
+ * result will be returned very fast.</li>
+ * </ul>
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing the
+ * system call.
+ */
+ public static String getNetworkHostname() throws NativeException {
+ switch(Platform.getOSType()) {
+ case Platform.WINDOWS:
+ return org.netbeans.core.network.utils.hname.win.HostnameUtilsWin.getHostName(true);
+ case Platform.MAC:
+ return org.netbeans.core.network.utils.hname.mac.HostnameUtilsMac.cLibGetHostname();
+ case Platform.LINUX:
+ return org.netbeans.core.network.utils.hname.linux.HostnameUtilsLinux.cLibGetHostname();
+ case Platform.SOLARIS:
+ return org.netbeans.core.network.utils.hname.solaris.HostnameUtilsSolaris.cLibGetHostname();
+ default:
+ return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+ }
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java b/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java
new file mode 100644
index 0000000..50c3037
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java
@@ -0,0 +1,526 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.core.network.utils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.Exceptions;
+import org.openide.util.RequestProcessor;
+
+/**
+ * IP address utilities. Mainly providing functionality
+ * for doing name resolve with explicit timeout.
+ *
+ * <p>
+ * TODO: Support for reverse lookup with timeout isn't implemented. Hasn't been
+ * any need for it.
+ *
+ * @author lbruun
+ */
+public class IpAddressUtils {
+
+ private static final Pattern IPV4_PATTERN = Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
+ private static final RequestProcessor RP = new RequestProcessor("DNSBackgroundResolvers", 10);
+
+ private IpAddressUtils() {}
+
+ /**
+ * Filters the result of a method according to IP protocol preference.
+ */
+ public enum IpTypePreference {
+ /**
+ * Only IPv4 address(es) in the returned value.
+ */
+ IPV4_ONLY,
+ /**
+ * Only IPv6 address(es) in the returned value.
+ */
+ IPV6_ONLY,
+ /**
+ * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+ * but IPv4 address is preferred over IPv6. If the method returns
+ * an array then IPv4 addresses will come before IPv6 addresses.
+ */
+ ANY_IPV4_PREF,
+ /**
+ * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+ * but IPv6 address is preferred over IPv4. If the method returns
+ * an array then IPv6 addresses will come before IPv4 addresses.
+ */
+ ANY_IPV6_PREF,
+ /**
+ * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+ * but their internal preference is determined by the setting in the
+ * JDK, namely the {@code java.net.preferIPv6Addresses} system property.
+ * If this property is {@code true} then using this preference will be
+ * exactly as {@link #ANY_IPV6_PREF}, if {@code false} it will be
+ * exactly as {@link #ANY_IPV4_PREF}.
+ */
+ ANY_JDK_PREF
+ }
+
+ /**
+ * Performs a name service lookup with a timeout.
+ *
+ * <p>This method can be used when the JRE's default DNS timeout is not
+ * acceptable. The method is essentially a wrapper around
+ * {@link java.net.InetAddress#getAllByName(java.lang.String) InetAddress.getAllByName()}.
+ *
+ * <p>A reasonable timeout value is 4 seconds (4000 ms) as this value
+ * will - with the JRE's default settings - allow each DNS server in a
+ * list of 4 servers to be queried once.
+ *
+ * <p>If the {@code host} is a literal representation
+ * of an IPv4 address (using dot notation, for example {@code "192.168.1.44"})
+ * or an IPv6 address (any form accepted by {@link java.net.Inet6Address}),
+ * then the method will convert the text into {@code InetAddress} and
+ * return immediately. The timeout does not apply to this case.
+ *
+ * <br>
+ * <br>
+ * <p>
+ * <u>Java's default DNS timeout:</u>
+ * <p>
+ * The default timeout DNS lookup is described
+ * <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html#PROP">
+ * in the documentation for JNDI</a> in properties:
+ * <p>
+ * {@code com.example.jndi.dns.timeout.initial} (defaults to 1 sec in Java 8)<br>
+ * {@code com.example.jndi.dns.timeout.retries} (defaults to 4 in Java 8)
+ * <p>
+ * The defaults mean that if the host OS has two servers defined in its DNS
+ * server search string (usually there's <i>at least</i> two) then - if both
+ * servers do not respond - the call to
+ * {@link InetAddress#getByName(java.lang.String)} will block for
+ * <b>30 seconds!</b> before it returns. With three servers it will be 45 seconds
+ * and so forth. This wait may be unacceptable in some scenarios and this is
+ * where this method is then a better alternative.
+ * <br>
+ * <br>
+ * @see #nameResolve(String, int, IpTypePreference)
+ * @param host either a host name or a literal IPv4
+ * address in dot notation.
+ * @param timeoutMs Milliseconds to wait for the result from the name service
+ * query before aborting. A value of 0 means not to use any timeout for
+ * the background resolver task. In that case only the standard timeouts
+ * as defined in the JRE will apply.
+ * @param ipTypePref IP protocol filter
+ * @return the result of the name lookup (may be more than one address)
+ * @throws InterruptedException if the background task was interrupted
+ * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+ * before a result was obtained.
+ * @throws UnknownHostException if no IP address for the host could be
+ * found.
+ */
+ public static @NonNull InetAddress[] nameResolveArr(String host, int timeoutMs, IpTypePreference ipTypePref)
+ throws InterruptedException, UnknownHostException, TimeoutException {
+
+ if (looksLikeIpv6Literal(host) || looksLikeIpv4Literal(host)) {
+ // No DNS lookup is needed in this case so we can simply
+ // call directly. It won't block.
+ InetAddress addr = InetAddress.getByName(host);
+ if (ipTypePref == IpTypePreference.IPV4_ONLY && addr instanceof Inet6Address) {
+ throw new UnknownHostException("Mismatch between supplied literal IP address \"" + host + "\" (which is IPv6) and value of ipTypePref : " + ipTypePref);
+ }
+ if (ipTypePref == IpTypePreference.IPV6_ONLY && addr instanceof Inet4Address) {
+ throw new UnknownHostException("Mismatch between supplied literal IP address \"" + host + "\" (which is IPv6) and value of ipTypePref : " + ipTypePref);
+ }
+ return new InetAddress[]{addr};
+ }
+
+ Callable<InetAddress[]> lookupTask = new DnsTimeoutTask(host);
+ Future<InetAddress[]> future = RP.submit(lookupTask);
+ try {
+ InetAddress[] ipAddresses;
+ if (timeoutMs == 0) {
+ ipAddresses = future.get();
+ } else {
+ ipAddresses = future.get(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+ List<InetAddress> resultList = IpAddressUtilsFilter.filterInetAddresses(Arrays.asList(ipAddresses), ipTypePref);
+ if (resultList.isEmpty()) {
+ throw new UnknownHostException("A positive result was returned from name lookup for \"" + host + "\" but none that matched a filter of " + ipTypePref);
+ }
+ return resultList.toArray(new InetAddress[resultList.size()]);
+
+ } catch (ExecutionException ex) {
+ Throwable cause = ex.getCause();
+ if (cause instanceof UnknownHostException) {
+ throw (UnknownHostException) cause;
+ }
+ // Unlikely
+ Exceptions.printStackTrace(cause);
+ return new InetAddress[]{};
+ } catch (TimeoutException ex) {
+ // If the wait times out then cancel the background job too.
+ // We're no longer interested in the result.
+ // The downside of not letting the task finish is that the
+ // JRE's DNS cache will then not be populated with the result
+ // - if a result is indeed obtained later than the timeout.
+ future.cancel(true);
+ throw new TimeoutException("No answer from name service within " + timeoutMs + " milliseconds when resolving \"" + host+ "\"");
+ }
+ }
+
+ /**
+ * Performs a name service lookup with a timeout. Same as
+ * {@link #nameResolveArr(java.lang.String, int, org.netbeans.network.IpAddressUtils.IpTypePreference) nameResolveArr()}
+ * but only returns a single address.
+ *
+ * @see #nameResolveArr(String, int, IpTypePreference)
+ * @param host either a host name or a literal IPv4
+ * address in dot notation.
+ * @param timeoutMs Milliseconds to wait for the result from the name service
+ * query before aborting. A value of 0 means not to use any timeout for
+ * the background resolver task. In that case only the standard timeouts
+ * as defined in the JRE will apply.
+ * @param ipTypePref IP protocol filter
+ * @return IP address
+ * @throws InterruptedException if the background task was interrupted
+ * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+ * before a result was obtained.
+ * @throws UnknownHostException if no IP address for the host could be
+ * found.
+ */
+ public static @NonNull InetAddress nameResolve(String host, int timeoutMs, IpTypePreference ipTypePref)
+ throws InterruptedException, UnknownHostException, TimeoutException {
+ InetAddress[] ipAddresses = nameResolveArr(host, timeoutMs, ipTypePref);
+ // We're guaranteed the array will have length > 0 and never null,
+ // so the following is safe.
+ return ipAddresses[0];
+ }
+
+ /**
+ * Performs a name service lookup with a timeout. Same as
+ * {@link #nameResolveArr(java.lang.String, int, org.netbeans.network.IpAddressUtils.IpTypePreference) nameResolveArr()}
+ * but only returns a single address and uses
+ * {@link IpTypePreference#ANY_JDK_PREF IpTypePreference.ANY_JDK_PREF}.
+ *
+ * <p>
+ * There are several overloaded forms of this method. If you don't need
+ * any specific filtering on the type of IP address returned then use
+ * this method!
+ *
+ * @see #nameResolveArr(String, int, IpTypePreference)
+ * @param host either a host name or a literal IPv4
+ * address in dot notation.
+ * @param timeoutMs Milliseconds to wait for the result from the name service
+ * query before aborting. A value of 0 means not to use any timeout for
+ * the background resolver task. In that case only the standard timeouts
+ * as defined in the JRE will apply.
+ * @return IP address
+ * @throws InterruptedException if the background task was interrupted
+ * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+ * before a result was obtained.
+ * @throws UnknownHostException if no IP address for the host could be
+ * found.
+ */
+ public static @NonNull InetAddress nameResolve(String host, int timeoutMs)
+ throws InterruptedException, UnknownHostException, TimeoutException {
+ return nameResolve(host, timeoutMs, IpTypePreference.ANY_JDK_PREF);
+ }
+
+ /**
+ * Validates if a string can be parsed as an IPv4 address.
+ *
+ * <p>
+ * The standard way to do this type of validation in Java is
+ * {@link java.net.InetAddress#getByName(java.lang.String)} but this method
+ * will block if the string is <i>not</i> an IP address literal, because it
+ * will query the name service. In contrast, this method relies solely on
+ * pattern matching techniques and will never block. Return value
+ * {@code true} is a guarantee that the string can be parsed as an IPv4
+ * address.
+ *
+ * @param ipAddressStr input string to be evaluated
+ * @return true if the string is a valid IPv4 literal on the
+ * form "#.#.#.#"
+ */
+ public static boolean isValidIpv4Address(String ipAddressStr) {
+ if (IPV4_PATTERN.matcher(ipAddressStr).matches()) {
+
+ String[] segments = ipAddressStr.split("\\.");
+
+ if (segments.length != 4) {
+ return false;
+ }
+
+ for (String segment : segments) {
+ if (segment == null || segment.length() == 0) {
+ return false;
+ }
+
+ // leading zeroes are not allowed within the segment.
+ if (segment.length() > 1 && segment.startsWith("0")) {
+ return false;
+ }
+
+ try {
+ int value = Integer.parseInt(segment);
+ if (value > 255) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false; // Unlikely. Already matched by regexp
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Does a shallow check if the argument looks like an IPv6 address as
+ * opposed to a host name. Note, that a return value of {@code true} doesn't
+ * guarantee that the argument is a <i>valid</i> IPv6 literal, but a return
+ * value of {@code false} is a guarantee that it is not.
+ *
+ * <p>
+ * The method is not meant as a validity check. It is mainly useful
+ * for predicting if the JDK's {@code InetAddress#get*ByName()} will block
+ * or not.
+ *
+ * @param ipAddressStr
+ * @return true if argument looks like an IPv6 literal.
+ */
+ public static boolean looksLikeIpv6Literal(String ipAddressStr) {
+ if (ipAddressStr == null) {
+ return false;
+ }
+ // "::d" is the shortest possible form of an IPv6 address.
+ // and the longest form is 39 chars.
+ if (ipAddressStr.length() < 3 || ipAddressStr.length() > 39) {
+ return false;
+ }
+ if (ipAddressStr.startsWith(":") || ipAddressStr.endsWith(":")) {
+ return true;
+ }
+ // Matches A80::8:800:200C:417A or 0A80::8:800:200C:417A
+ if (ipAddressStr.length() >= 5) {
+ if ((ipAddressStr.charAt(3) == ':') || (ipAddressStr.charAt(4) == ':')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Does a shallow check if the argument looks like an IPv4 address as
+ * opposed to a host name. Note, that a return value of {@code true} doesn't
+ * guarantee that the argument is a <i>valid</i> IPv4 literal, but a return
+ * value of {@code false} is a guarantee that it is not.
+ *
+ * <p>
+ * The method is not meant as a validity check. It is mainly useful
+ * for predicting if the JDK's {@code InetAddress#get*ByName()} will block
+ * or not.
+ *
+ * <p>
+ * Note, the method will err on the side of caution meaning return {@code
+ * false} if in doubt. Java allows "123" as a {@link java.net.Inet4Address
+ * valid IPv4 address}, but in this case it cannot be determined if the
+ * input is a hostname or an IPv4 literal and therefore {@code false} is
+ * returned.
+ *
+ * @see #isValidIpv4Address(java.lang.String)
+ * @param ipAddressStr
+ * @return true if argument looks like an IPv4 literal
+ *
+ */
+ public static boolean looksLikeIpv4Literal(String ipAddressStr) {
+ if (ipAddressStr == null || ipAddressStr.isEmpty()) {
+ return false;
+ }
+ if (!(isAsciiDigit(ipAddressStr.charAt(0)))) {
+ return false;
+ }
+ int dotPos = ipAddressStr.indexOf('.');
+ if (dotPos > 0 && dotPos < ipAddressStr.length()-1 && dotPos <=3) {
+ for(int i = 0; i < dotPos; i++) {
+ if (!(isAsciiDigit(ipAddressStr.charAt(i)))) {
+ return false;
+ }
+ }
+ if (!(isAsciiDigit(ipAddressStr.charAt(dotPos+1)))) {
+ return false;
+ }
+ } else {
+ // No dot found or nothing after dot or the part before dot
+ // it too long
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Strips the domain part from a host name. Example: for {@code "foo.bar.com"}
+ * then {@code "foo"} will be returned.
+ *
+ * <p>
+ * The method is safe to use even if the input is an IPv4 literal or IPv6
+ * literal. In this case the input will be returned unchanged.
+ *
+ * @param hostname
+ * @return hostname with domain stripped
+ */
+ public static String removeDomain(String hostname) {
+ if (hostname == null) {
+ return hostname;
+ }
+ if (looksLikeIpv4Literal(hostname)) {
+ return hostname;
+ }
+
+ int pos = hostname.indexOf('.');
+ if (pos == -1) {
+ return hostname;
+ } else {
+ int posColon = hostname.indexOf(':');
+ if (posColon >=0 && posColon < pos) { // It is an IPv6 literal with an embedded IPv4 address
+ return hostname;
+ }
+
+ return hostname.substring(0, pos);
+ }
+ }
+
+ /**
+ * Sorts a list of IP addresses.
+ *
+ * @param addresses IP addresses
+ * @param ip4BeforeIp6 if IPv4 addresses are to come before IPv6 addresses
+ * ({@code true}) or not ({@code false}).
+ */
+ public static void sortIpAddresses(List<InetAddress> addresses, boolean ip4BeforeIp6) {
+ sortIpAddresses0(addresses, ip4BeforeIp6, false);
+ }
+
+ /**
+ * Sorts a list of IP addresses, but only with respect to IPv4 vs IPv6. Addresses
+ * within the same class of protocol (e.g. IPv4 addresses) are not sorted.
+ *
+ * @param addresses IP addresses
+ * @param ip4BeforeIp6 if IPv4 addresses are to come before IPv6 addresses
+ * ({@code true}) or not ({@code false}).
+ */
+ public static void sortIpAddressesShallow(List<InetAddress> addresses, boolean ip4BeforeIp6) {
+ sortIpAddresses0(addresses, ip4BeforeIp6, true);
+ }
+
+ private static void sortIpAddresses0(List<InetAddress> addresses, boolean ip4BeforeIp6, boolean shallow) {
+ if (addresses != null && (addresses.size() > 1)) {
+ Collections.sort(addresses, new InetAddressComparator(ip4BeforeIp6, shallow));
+ }
+ }
+
+
+
+ private static class DnsTimeoutTask implements Callable<InetAddress[]> {
+
+ private final String host;
+
+ public DnsTimeoutTask(String host) {
+ this.host = host;
+ }
+
+ @Override
+ public InetAddress[] call() throws UnknownHostException {
+ return InetAddress.getAllByName(host);
+ }
+ }
+
+
+ private static class InetAddressComparator implements Comparator<InetAddress> {
+
+ private final boolean ip4BeforeIp6;
+ private final boolean shallow;
+
+ public InetAddressComparator(boolean ip4BeforeIp6, boolean shallow) {
+ this.ip4BeforeIp6 = ip4BeforeIp6;
+ this.shallow = shallow;
+ }
+
+ @Override
+ public int compare(InetAddress a1, InetAddress a2) {
+ byte[] bArr1 = a1.getAddress();
+ byte[] bArr2 = a2.getAddress();
+
+ if ((a1 instanceof Inet4Address) && (a2 instanceof Inet6Address)) {
+ return (ip4BeforeIp6) ? -1 : 1;
+ }
+ if ((a1 instanceof Inet6Address) && (a2 instanceof Inet4Address)) {
+ return (ip4BeforeIp6) ? 1 : -1;
+ }
+
+ if (bArr1.length != bArr2.length) {
+ // according to JDK spec, this shouldn't be possible
+ // but we take no risks here. This could happen if one day
+ // there are more sub-classes of InetAddress than just
+ // Inet4Address or Inet6Address.
+ if (bArr1.length < bArr2.length) {
+ return (ip4BeforeIp6) ? -1 : 1;
+ } else {
+ return (ip4BeforeIp6) ? 1 : -1;
+ }
+ }
+
+ if (shallow) {
+ return 0;
+ }
+
+ // Compare byte-by-byte.
+ for (int i = 0; i < bArr1.length; i++) {
+ int x1 = Byte.toUnsignedInt(bArr1[i]);
+ int x2 = Byte.toUnsignedInt(bArr2[i]);
+
+ if (x1 == x2) {
+ continue;
+ }
+ if (x1 < x2) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+ }
+ }
+
+ private static boolean isAsciiDigit(char c) {
+ // Why doesn't the JDK have a shorthand for this?
+ return c >= 48 && c <= 57;
+ }
+
+
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java b/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java
new file mode 100644
index 0000000..5fa0a7b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Package private methods for choosing IP addresses from a
+ * list based on a stated preference.
+ *
+ * @author lbruun
+ */
+class IpAddressUtilsFilter {
+
+ private static final boolean JDK_PREFER_IPV6_ADDRESS;
+ static {
+ JDK_PREFER_IPV6_ADDRESS = java.security.AccessController.doPrivileged(
+ new PrivilegedAction<Boolean>() {
+ @Override
+ public Boolean run() {
+ return Boolean.getBoolean("java.net.preferIPv6Addresses");
+
+ }
+ });
+ }
+ private IpAddressUtilsFilter() {}
+
+ protected static InetAddress pickInetAddress(Iterable<InetAddress> sortedList, IpAddressUtils.IpTypePreference ipTypePref) {
+ IpAddressUtils.IpTypePreference pref = getIpTypePreferenceResolved(ipTypePref);
+ for (InetAddress ipAddress : sortedList) {
+ if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF || pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+ return ipAddress;
+ }
+ if (ipAddress instanceof Inet4Address) {
+ if (pref == IpAddressUtils.IpTypePreference.IPV4_ONLY) {
+ return ipAddress;
+ }
+ }
+ if (ipAddress instanceof Inet6Address) {
+ if (pref == IpAddressUtils.IpTypePreference.IPV6_ONLY) {
+ return ipAddress;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected static @NonNull List<InetAddress> filterInetAddresses(Iterable<InetAddress> list, IpAddressUtils.IpTypePreference ipTypePref) {
+ IpAddressUtils.IpTypePreference pref = getIpTypePreferenceResolved(ipTypePref);
+ List<InetAddress> newList = new ArrayList<>();
+ if (list != null) {
+ for (InetAddress ipAddress : list) {
+ if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF || pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+ newList.add(ipAddress);
+ } else {
+ if ((ipAddress instanceof Inet4Address) && (pref == IpAddressUtils.IpTypePreference.IPV4_ONLY)) {
+ newList.add(ipAddress);
+ }
+ if ((ipAddress instanceof Inet6Address) && (pref == IpAddressUtils.IpTypePreference.IPV6_ONLY)) {
+ newList.add(ipAddress);
+ }
+ }
+ }
+ }
+ if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF) {
+ IpAddressUtils.sortIpAddressesShallow(newList,true);
+ }
+ if (pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+ IpAddressUtils.sortIpAddressesShallow(newList,false);
+ }
+ return newList;
+ }
+
+ private static IpAddressUtils.IpTypePreference getIpTypePreferenceResolved(IpAddressUtils.IpTypePreference ipTypePref) {
+ if (ipTypePref == IpAddressUtils.IpTypePreference.ANY_JDK_PREF) {
+ if (JDK_PREFER_IPV6_ADDRESS) {
+ return IpAddressUtils.IpTypePreference.ANY_IPV6_PREF;
+ } else {
+ return IpAddressUtils.IpTypePreference.ANY_IPV4_PREF;
+ }
+ } else {
+ return ipTypePref;
+ }
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java b/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java
new file mode 100644
index 0000000..0fa3713
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java
@@ -0,0 +1,536 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.core.network.utils;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.core.network.utils.IpAddressUtils.IpTypePreference;
+import org.openide.util.RequestProcessor;
+
+/**
+ * Methods for determining the local host's own address. The methods provide
+ * two benefits over the core JDK classes.:
+ * <p>
+ * <ul>
+ * <li><i>Caching</i>. Results from the methods are cached and can therefore
+ * be returned without blocking. An application should call
+ * {@link #warmUp()} during the application's startup phase
+ * so that results are readily available when needed.
+ * </li>
+ * <li><i>IP protocol preference</i>. All methods allow to state
+ * an explicit preference for IPv4 vs IPv6. (<i>without</i> relation
+ * to the JVM's overall protocol preference settings).
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * The main method is
+ * {@link #getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddress()}.
+ * The other methods essentially exist to provide input to this method but are
+ * exposed nevertheless if anyone wants them.
+ *
+ * <p>
+ * These utility methods are in particular relevant in relation to
+ * the PAC helper method {@code myIpAddress()}. However, the class may indeed
+ * be used for any use case.
+ *
+ * <p>
+ * Note, that there is no single correct answer to the question about
+ * determining the local host's IP address in an environment with multiple
+ * network interfaces or multiple addresses on each network interface.
+ *
+ * @author lbruun
+ */
+public class LocalAddressUtils {
+ private static final Logger LOG = Logger.getLogger(LocalAddressUtils.class.getName());
+ private static final RequestProcessor RP = new RequestProcessor("LocalNetworkAddressFinder", 3);
+
+ private static final byte[] LOOPBACK_IPV4_RAW = new byte[]{0x7f,0x00,0x00,0x01};
+ private static final byte[] LOOPBACK_IPV6_RAW = new byte[]{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
+ private static InetAddress LOOPBACK_IPV4;
+ private static InetAddress LOOPBACK_IPV6;
+ static {
+ try {
+ LOOPBACK_IPV4 = InetAddress.getByAddress("local-ipv4-dummy", LOOPBACK_IPV4_RAW);
+ LOOPBACK_IPV6 = InetAddress.getByAddress("local-ipv6-dummy", LOOPBACK_IPV6_RAW);
+ } catch (UnknownHostException ex) {
+ }
+ }
+
+
+ private static final Object LOCK = new Object();
+ private static Future<InetAddress> fut1;
+ private static Future<InetAddress[]> fut2;
+ private static Future<List<InetAddress>> fut3;
+
+ private static final Callable<InetAddress> C1 = new Callable(){
+ @Override
+ public InetAddress call() throws UnknownHostException {
+ return InetAddress.getLocalHost();
+ }
+ };
+ private static final Callable<InetAddress[]> C2 = new Callable(){
+ @Override
+ public InetAddress[] call() throws UnknownHostException {
+ try {
+ String hostname = HostnameUtils.getNetworkHostname();
+ return InetAddress.getAllByName(hostname);
+ } catch (NativeException ex) {
+ throw new UnknownHostException(ex.getMessage() + ", error code : " + ex.getErrorCode());
+ }
+ }
+ };
+ private static final Callable<List<InetAddress>> C3 = new Callable(){
+ @Override
+ public List<InetAddress> call() {
+ return getLocalNetworkInterfaceAddr();
+ }
+ };
+ static {
+ refreshNetworkInfo(false);
+ }
+
+
+ private LocalAddressUtils() {
+ }
+
+
+ /**
+ * Starts collecting network information in a background task. This method
+ * is ideal for calling once during application startup phase. Calling this
+ * method during startup means that later calls to methods in this class
+ * will perform very fast, because they will then return a cached result and
+ * thus will never block.
+ */
+ public static void warmUp() {
+ // Does nothing. The refreshNetworkInfo task will be fired when the class
+ // is loaded by the classloaded.
+ }
+
+ /**
+ * Refreshes the cached network information. Normally there is no reason
+ * to call this method as a computer's network information is unlikely
+ * to change during the lifetime of the application, unless the computer
+ * connects to a different network than was the case when the information
+ * was first cached.
+ *
+ * @param await if true, waits for the refresh to complete
+ */
+ public static void refreshNetworkInfo(boolean await) {
+ synchronized (LOCK) {
+ fut1 = RP.submit(C1);
+ fut2 = RP.submit(C2);
+ fut3 = RP.submit(C3);
+ if (await) {
+ try {
+ fut1.get();
+ fut2.get();
+ fut3.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the address of the local host.
+ *
+ * <p>This method returns a cached result of calling
+ * {@link InetAddress#getLocalHost()} and is therefore likely not to
+ * block (unlike the underlying method) unless this is the first time
+ * this class is being referenced.
+ *
+ * <p>Note that {@code InetAddress#getLocalHost()} is known to return
+ * unpredictable results for hosts with multiple network adapters. The
+ * {@link #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddresses()}
+ * method is much more likely to return an acceptable result.
+ *
+ * @see #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference)
+ * @return
+ * @throws UnknownHostException
+ */
+ public static @NonNull InetAddress getLocalHost() throws UnknownHostException {
+ synchronized (LOCK) {
+ if (fut1 == null) {
+ refreshNetworkInfo(false);
+ }
+ try {
+ return fut1.get();
+ } catch (ExecutionException ex) {
+ if (ex.getCause() instanceof UnknownHostException) {
+ throw (UnknownHostException) ex.getCause();
+ }
+ throw new RuntimeException(ex.getCause());
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Returns the addresses of the local host.
+ *
+ * <p>This is achieved by retrieving the
+ * {@link org.netbeans.network.hname.HostnameUtils#getNetworkHostname() name-of-the-host}
+ * from the system, then resolving that name into a list of {@code InetAddress}es.
+ *
+ * <p>This method returns a cached result and is therefore likely not to
+ * block unless this is the first time this class is being referenced.
+ *
+ * @see org.netbeans.network.hname.HostnameUtils#getNetworkHostname()
+ * @see InetAddress#getAllByName(java.lang.String)
+ * @param ipTypePref filter
+ * @return
+ * @throws UnknownHostException if no IP address for the host name could be found
+ */
+ public static @NonNull InetAddress[] getLocalHostAddresses(IpTypePreference ipTypePref) throws UnknownHostException {
+ synchronized (LOCK) {
+ if (fut2 == null) {
+ refreshNetworkInfo(false);
+ }
+ try {
+ return fut2.get();
+ } catch (ExecutionException ex) {
+ if (ex.getCause() instanceof UnknownHostException) {
+ throw (UnknownHostException) ex.getCause();
+ }
+ if (ex.getCause() instanceof SecurityException) {
+ throw (SecurityException) ex.getCause();
+ }
+ throw new RuntimeException(ex.getCause());
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Returns a prioritized list of local host addresses. The further up
+ * on this list, the more likely it is that the address is the host's IP
+ * address.
+ *
+ * <p>
+ * Prioritization is done on the following basis:
+ * <ul>
+ * <li>IPv4 addresses are prioritized higher than IPv6 addresses.</li>
+ * <li>Addresses belonging to a non-virtual interface are prioritized higher
+ * than addresses belonging to a virtual interface.</li>
+ * <li>Addresses belonging to an interface which supports multicast are
+ * prioritized higher than addresses belonging to an interface which doesn't
+ * support multicast.</li>
+ * <li>Addresses belonging to an interface which
+ * {@link #isSoftwareVirtualAdapter(java.net.NetworkInterface) look
+ * like a software virtual adapters} are prioritized lower than addresses
+ * belonging to interfaces which don't look software virtual adapters.</li>
+ * <li>Addresses with a broadcast address are prioritized higher than
+ * addresses with no broadcast address.</li>
+ * </ul>
+ *
+ * <p>
+ * The method returns a cached result and is therefore likely not to block,
+ * unless this is the first time this class is being referenced.
+ *
+ * @param ipTypePref filter
+ * @return prioritized list of addresses
+ */
+ public static @NonNull List<InetAddress> getPrioritizedLocalHostAddresses(IpAddressUtils.IpTypePreference ipTypePref) {
+ synchronized (LOCK) {
+ if (fut3 == null) {
+ refreshNetworkInfo(false);
+ }
+ try {
+ return IpAddressUtilsFilter.filterInetAddresses(fut3.get(), ipTypePref);
+ } catch (ExecutionException ex) {
+ throw new RuntimeException(ex.getCause());
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Returns the host's IP addresses. Or rather the IP addresses most likely
+ * to be the ones the host is known by. This method is much more likely
+ * to return a correct result than the JDKs {@link InetAddress#getLocalHost()},
+ * in particular on hosts with multiple network interfaces or hosts
+ * that are virtualized or operating in a PaaS environment.
+ *
+ * <p>
+ * The method uses the following prioritization for determining what
+ * to return, by continously moving to the next step if the previous
+ * step yielded an empty result:
+ * <ol>
+ * <li>
+ * The list from {@link #getLocalHostAddresses(IpAddressUtils.IpTypePreference) getLocalHostAddresses() }
+ * (List A) is compared to
+ * {@link #getPrioritizedLocalHostAddresses(IpAddressUtils.IpTypePreference) getPrioritizedLocalHostAddresses() }
+ * (List B),
+ * picking the ones from List B list which is also on List A.
+ * </li>
+ *
+ * <li>
+ * Use List B.
+ * </li>
+ *
+ * <li>
+ * Use List A.
+ * </li>
+ *
+ * <li>
+ * Use the result from {@link #getLocalHost()} if it matches the
+ * {@code ipTypePref} filter.
+ * </li>
+ *
+ * <li>
+ * Finally, if everything else fails, return the result of
+ * {@link #getLoopbackAddress(IpAddressUtils.IpTypePreference)
+ * getLoopbackAddress()}.
+ * </li>
+ * </ol>
+ *
+ * <p>
+ * The method uses the other methods in the class and is therefore
+ * likely not to block, unless this is the first time this class is being
+ * referenced.
+ *
+ * @see #getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference)
+ * @param ipTypePref IP protocol filter
+ * @return IP addresses, never null
+ */
+ public static @NonNull InetAddress[] getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference ipTypePref) {
+ List<InetAddress> filteredList = getPrioritizedLocalHostAddresses(ipTypePref);
+ try {
+ List<InetAddress> localHostAddresses = Arrays.asList(getLocalHostAddresses(ipTypePref));
+
+ if (localHostAddresses != null && !localHostAddresses.isEmpty()) {
+ List<InetAddress> tmpList = new ArrayList<>(5);
+ for (InetAddress addr : filteredList) {
+ if (localHostAddresses.contains(addr)) {
+ tmpList.add(addr);
+ }
+ }
+
+ // #1
+ if (!tmpList.isEmpty()) {
+ return tmpList.toArray(new InetAddress[tmpList.size()]);
+ }
+
+ // #2
+ return localHostAddresses.toArray(new InetAddress[localHostAddresses.size()]);
+ }
+ } catch (UnknownHostException ex) {
+ }
+
+ // #3
+ if (!filteredList.isEmpty()) {
+ return filteredList.toArray(new InetAddress[filteredList.size()]);
+ }
+
+
+ // #4
+ try {
+ InetAddress addr = IpAddressUtilsFilter.pickInetAddress(Collections.singletonList(getLocalHost()), ipTypePref);
+ if (addr != null) {
+ return new InetAddress[]{addr};
+ }
+ } catch (UnknownHostException ex) {
+ }
+
+ // #5 - last resort
+ return new InetAddress[]{getLoopbackAddress(ipTypePref)};
+ }
+
+ /**
+ * Returns the host's IP address. Same as
+ * {@link #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddresses()}
+ * but only returns a single IP address.
+ *
+ *
+ * @see #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference)
+ * @param ipTypePref IP protocol filter
+ * @return IP address, never null
+ */
+ public static @NonNull InetAddress getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference ipTypePref) {
+ InetAddress[] ipAddresses = getMostLikelyLocalInetAddresses(ipTypePref);
+ // We're guaranteed the array will have length > 0 and never null.
+ return ipAddresses[0];
+ }
+
+
+ /**
+ * Returns the loopback address.
+ *
+ * <p>This method is similar to {@link InetAddress#getLoopbackAddress()}
+ * except that the preference for IPv4 vs IPv6 can be explicitly
+ * stated.
+ *
+ * <p>For IPv4 the returned address is always {@code 127.0.0.1} and for
+ * IPv6 it is {@code ::1}.
+ *
+ * @param ipTypePref IPv4 vs IP4v6 preference
+ * @return
+ */
+ public static @NonNull InetAddress getLoopbackAddress(IpAddressUtils.IpTypePreference ipTypePref) {
+ switch(ipTypePref) {
+ case IPV4_ONLY:
+ case ANY_IPV4_PREF:
+ return LOOPBACK_IPV4;
+ case IPV6_ONLY:
+ case ANY_IPV6_PREF:
+ return LOOPBACK_IPV6;
+ default:
+ return LOOPBACK_IPV4;
+ }
+ }
+
+
+ private static @NonNull List<InetAddress> getLocalNetworkInterfaceAddr() {
+ final Map<InetAddress, Integer> mapWithScores = new HashMap<>();
+
+ // Looping through all network interfaces on the host.
+ // WARNING: On Windows this is quite slow. On my Intel Core i7 it takes
+ // approximately 1200 msecs and I only have 3 network interfaces defined.
+ // The reason is that Windows creates a lot of virtual/bogus network
+ // interfaces. (nope, you don't see them with 'ipconfig /all' command)
+ // On my Win10 host there are 46 entries returned from
+ // NetworkInterface.getNetworkInterfaces() !! (all but a few are really
+ // to be ignored). It is the actual call to NetworkInterface.getNetworkInterfaces()
+ // which consumes 95% of the total time of this method.
+ // For a GUI oriented system a call to NetworkInterface.getNetworkInterfaces()
+ // can make the application seem slow if it is done on a critical thread.
+ Enumeration<NetworkInterface> interfaces;
+ try {
+ interfaces = NetworkInterface.getNetworkInterfaces(); // expensive call
+ } catch (SocketException ex) {
+ LOG.log(Level.WARNING, "Cannot get host's network interfaces", ex);
+ return Collections.emptyList();
+ }
+
+ while (interfaces.hasMoreElements()) {
+ int ifScore = 0; // the score for the interface, higher is better
+ NetworkInterface netIf = interfaces.nextElement(); // inexpensive call
+ try {
+ if (!netIf.isUp() || netIf.isLoopback()) { // inexpensive call
+ continue; // discard
+ }
+ // Solaris note: When inside a non-global zone a network interface which
+ // is really virtual as seen from the global zone is seen as non-virtual
+ // from within the non-global zone. So, it is safe to give virtual
+ // interfaces a lower score.
+ if (netIf.isVirtual()) {
+ ifScore -= 1;
+ }
+ if (!netIf.supportsMulticast()) {
+ ifScore -= 1;
+ }
+ if (isSoftwareVirtualAdapter(netIf)) {
+ ifScore -= 1;
+ }
+
+ } catch (SocketException ex) {
+ // isUp() and isLoopback() may throw exception. Discard
+ // the interface if that's the case.
+ continue;
+ }
+ List<InterfaceAddress> interfaceAddresses = netIf.getInterfaceAddresses(); // inexpensive call
+ for(InterfaceAddress ifAddr : interfaceAddresses) {
+ int addrScore = 0; // the score for the address, higher is better
+ InetAddress address = ifAddr.getAddress();
+ if (ifAddr.getBroadcast() == null) {
+ addrScore -= 1;
+ }
+ if (address instanceof Inet6Address) {
+ addrScore -= 1;
+ }
+
+ mapWithScores.put(address, ifScore + addrScore);
+ }
+ }
+
+ List<InetAddress> list = new ArrayList<>(mapWithScores.keySet());
+
+ // Sort descending according to the scores
+ Collections.sort( list, new Comparator<InetAddress>(){
+ @Override
+ public int compare(InetAddress o1, InetAddress o2) {
+ return mapWithScores.get(o2).compareTo(mapWithScores.get(o1));
+ }
+ });
+
+ return list; // returns a prioritized list
+ }
+
+
+
+
+
+
+ /**
+ * Tries to guess if the network interface is a virtual adapter
+ * by one of the makers of virtualization solutions (e.g. VirtualBox,
+ * VMware, etc). This is by no means a bullet proof method which is
+ * why it errs on the side of caution.
+ *
+ * @param nif network interface
+ * @return
+ */
+ public static boolean isSoftwareVirtualAdapter(NetworkInterface nif) {
+
+ try {
+ // VirtualBox uses a semi-random MAC address for their adapter
+ // where the first 3 bytes are always the same:
+ // Windows, Mac OS X, Linux : begins with 0A-00-27
+ // Solaris: begins with 10-00-27
+ // (above from VirtualBox source code)
+ //
+ byte[] macAddress = nif.getHardwareAddress();
+ if (macAddress != null & macAddress.length >= 3) {
+ if ((macAddress[0] == 0x0A || macAddress[0] == 0x08) &&
+ (macAddress[1] == 0x00) &&
+ (macAddress[2] == 0x27)) {
+ return true;
+ }
+ }
+ return false;
+ } catch (SocketException ex) {
+ return false;
+ }
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/NativeException.java b/core.network/src/org/netbeans/core/network/utils/NativeException.java
new file mode 100644
index 0000000..70f29fa
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/NativeException.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+ /**
+ * A native error has occurred. (error in JNI or JNA call)
+ *
+ * @author lbruun
+ */
+ public class NativeException extends Exception {
+
+ private final int errorCode;
+
+ public NativeException(int errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public NativeException(int errorCode, String msg) {
+ super(msg);
+ this.errorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ }
diff --git a/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java b/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java
new file mode 100644
index 0000000..a7da2e6
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Simple cache with a max capacity. Encapsulates {@link java.util.Map Map}.
+ *
+ * <p>
+ * Eviction is based on usage. Every time a value is retrieved from the cache
+ * its 'usage timestamp' will change. When the cache is full, the value with the
+ * oldest usage timestamp will be evicted.
+ *
+ * <p>
+ * This class is thread-safe and is very efficient for read operations
+ * ({@code get}). The write operation ({@code put}) will - if the cache is full
+ * - have performance which degrades linearly with the size of the cache as all
+ * elements will have to be inspected to find the eviction candidate. For this
+ * reason, the class is best suited for smaller caches (say less than 1000
+ * elements).
+ *
+ *
+ * @author lbruun
+ *
+ * @param <K> key
+ * @param <V> value
+ */
+public class SimpleObjCache<K,V> {
+
+ private final int maxSize;
+ private final ConcurrentHashMap<K, ValueWrapper> map;
+
+ /**
+ * Constructs a new cache with {@code maxSize} capacity. When the
+ * cache is full, the value used/created the furthest in the past will
+ * be evicted to make room for a new element.
+ *
+ * @param maxSize capacity - any value larger than zero
+ */
+ public SimpleObjCache(int maxSize) {
+
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("Cache max size cannot be zero or less");
+ }
+ this.maxSize = maxSize;
+ this.map = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Puts a value into the cache mapped to the specified key.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with key, or null if there
+ * was no mapping for the key
+ */
+ public V put(K key, V value) {
+ evictIfFull();
+ ValueWrapper existingValue = map.put(key, new ValueWrapper(value));
+ if (existingValue != null) {
+ return existingValue.value;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped, or {@code null}
+ * if this cache contains no mapping for the key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the value to which the specified key is mapped, or {@code null}
+ * if this map contains no mapping for the key
+ */
+ public V get(K key) {
+ ValueWrapper wrapper = map.get(key);
+ if (wrapper != null) {
+ wrapper.lastUsed.set(System.currentTimeMillis());
+ return wrapper.value;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the current number of elements in the cache.
+ * @return
+ */
+ public int getCacheSize() {
+ return map.size();
+ }
+
+ /**
+ * Removes all elements from the cache. There is little reason to use
+ * this method as the cache will do its own house keeping.
+ */
+ public void clear() {
+ map.clear();
+ }
+
+ synchronized private void evictIfFull() {
+ if (map.size() >= maxSize) {
+ K toBeEvicted = findEvictionCandidate();
+ if (toBeEvicted != null) {
+ map.remove(toBeEvicted);
+ }
+ }
+ }
+
+ private K findEvictionCandidate() {
+ // Finds minimum of all timestamps of all elements in the cache.
+
+ Optional<Map.Entry<K, ValueWrapper>> minEntry = map.entrySet().stream()
+ .min(Comparator.comparingLong( e -> e.getValue().lastUsed.get()));
+
+ // Did we actually find something to delete?
+ // Theoretically this should always be so, but we are cautious
+ if (minEntry.isPresent()) {
+ return minEntry.get().getKey();
+ } else {
+ return null;
+ }
+ }
+
+ private class ValueWrapper {
+ private final AtomicLong lastUsed = new AtomicLong(System.currentTimeMillis());
+ private final V value;
+
+ public ValueWrapper(V value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java b/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java
new file mode 100644
index 0000000..a8e891e
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.linux;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for Linux.
+ */
+public class HostnameUtilsLinux {
+
+
+
+ /**
+ * Returns the result of {@code gethostname()} function
+ * from the standard Unix/Linux C Library.
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing
+ * the system call.
+ */
+ public static String cLibGetHostname() throws NativeException {
+ return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java
new file mode 100644
index 0000000..a3e017d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utility functions for finding the local computer's name (Linux).
+ */
+package org.netbeans.core.network.utils.hname.linux;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java b/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java
new file mode 100644
index 0000000..23b3b33
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.mac;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for for Mac OS X.
+ */
+public class HostnameUtilsMac {
+
+
+ /**
+ * Returns the result of {@code gethostname()} function
+ * from the standard Unix/Linux C Library.
+ *
+ * <p>
+ * Mac OS X has a "dynamic hostname" feature which means that the
+ * value returned by this method may be completely different from the
+ * value in the Mac's <i>System Preferences</i>.
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing
+ * the system call.
+ */
+ public static String cLibGetHostname() throws NativeException {
+ return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java
new file mode 100644
index 0000000..7b21e91
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utility functions for finding the local computer's name (Mac OS X).
+ */
+package org.netbeans.core.network.utils.hname.mac;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java b/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java
new file mode 100644
index 0000000..09c5237
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.solaris;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for Solaris.
+ */
+public class HostnameUtilsSolaris {
+
+
+ /**
+ * Returns the result of {@code gethostname()} function
+ * from the standard Unix/Linux C Library.
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing
+ * the system call.
+ */
+ public static String cLibGetHostname() throws NativeException {
+ return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java
new file mode 100644
index 0000000..f07919b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utility functions for finding the local computer's name (Solaris).
+ */
+package org.netbeans.core.network.utils.hname.solaris;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java
new file mode 100644
index 0000000..9419368
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.unix;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+/**
+ * Standard C Library on Unix.
+ */
+interface CLib extends Library {
+
+ CLib INSTANCE = (CLib) Native.loadLibrary("c", CLib.class);
+
+ public int gethostname(byte[] hostname, int bufferSize);
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java
new file mode 100644
index 0000000..81b2e4c
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.unix;
+
+import com.sun.jna.Native;
+import org.netbeans.core.network.utils.NativeException;
+
+/**
+ * Host name utilities for "general" Unix.
+ */
+public class HostnameUtilsUnix {
+
+ /**
+ * Max length of a Unix hostname including
+ * null terminator.
+ */
+ private static final int MAXHOSTNAMELEN = 256;
+
+
+ /**
+ * Returns the result of {@code gethostname()} function
+ * from the standard Unix/Linux C Library.
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing the
+ * system call.
+ */
+ public static String cLibGetHostname() throws NativeException {
+ byte[] buf = new byte[MAXHOSTNAMELEN];
+ int retCode = CLib.INSTANCE.gethostname(buf, buf.length);
+
+ if (retCode == 0) {
+ return Native.toString(buf);
+ }
+ throw new NativeException(retCode, "error calling 'gethostname()' function");
+ }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java
new file mode 100644
index 0000000..3383850
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utility functions for finding the local computer's name (generic Unix).
+ */
+package org.netbeans.core.network.utils.hname.unix;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java b/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java
new file mode 100644
index 0000000..340bdef
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.win;
+
+import com.sun.jna.Native;
+import com.sun.jna.platform.win32.Kernel32Util;
+import com.sun.jna.platform.win32.Win32Exception;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.NativeException;
+
+/**
+ * Hostname utilities for Microsoft Windows OS.
+ */
+public class HostnameUtilsWin {
+
+ private static final Logger LOGGER = Logger.getLogger(HostnameUtilsWin.class.getName());
+
+ /**
+ * Gets the computer name.
+ *
+ * <p>This is the also known as the NetBIOS name, although NetBIOS is
+ * hardly used anymore. It is the same value as can be seen from the
+ * {@code COMPUTERNAME} environment variable.
+ *
+ * <p>
+ * Windows API equivalent: {@code GetComputerName()} function from
+ * {@code Kernel32} library.
+ *
+ * @return computer name
+ * @throws NativeException if there was an error executing the
+ * system call.
+ */
+ public static String getComputerName() throws NativeException {
+ try {
+ return Kernel32Util.getComputerName();
+ } catch (Win32Exception ex) {
+ LOGGER.log(Level.FINE, "Kernel32.GetComputerName error : {0}", ex.getHR().intValue());
+ String env = System.getenv("COMPUTERNAME");
+ if (env != null) {
+ return env;
+ }
+ throw new NativeException(ex.getHR().intValue(), "error calling 'GetComputerName()' function");
+ }
+ }
+
+ /**
+ * Gets the local host name.
+ *
+ * <p>
+ * The underlying Windows function may return a simple host name (e.g.
+ * {@code chicago}) or it may return a fully qualified host name (e.g.
+ * {@code chicago.us.internal.net}). The {@code noQualify} parameter can
+ * remove the domain part if it exists.
+ *
+ * <p>
+ * Note that the underlying Windows function will do a name service lookup
+ * and the method is therefore potentially blocking, although it is more
+ * than likely that Windows has cached this result in the DNS Client Cache
+ * and therefore the result will be returned very fast.
+ *
+ * <p>
+ * Windows API equivalent: {@code gethostname()} function from
+ * {@code Ws2_32} library.
+ *
+ * @param noQualify if {@code true} the result is never qualified with domain,
+ * if {@code false} the result is <i>potentially</i> a fully qualified
+ * host name.
+ *
+ * @return host name
+ * @throws NativeException if there was an error executing the
+ * system call.
+ */
+ public static String getHostName(boolean noQualify) throws NativeException {
+
+ byte[] buf = new byte[256];
+
+ int returnCode = Winsock2Lib.INSTANCE.gethostname(buf, buf.length);
+ if (returnCode == 0) {
+ String result = Native.toString(buf);
+ if (noQualify) {
+ return IpAddressUtils.removeDomain(result);
+ } else {
+ return result;
+ }
+ } else {
+ throw new NativeException(returnCode, "error calling 'gethostname()' function");
+ }
+ }
+
+
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java b/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java
new file mode 100644
index 0000000..c38be7a
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.win;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+/**
+ * Winsock2 library on Windows
+ */
+public interface Winsock2Lib extends Library {
+
+ Winsock2Lib INSTANCE = (Winsock2Lib) Native.loadLibrary("ws2_32", Winsock2Lib.class);
+
+ public int gethostname(byte[] name, int namelen);
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java
new file mode 100644
index 0000000..fa4cb98
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utility functions for finding the local computer's name (Windows).
+ */
+package org.netbeans.core.network.utils.hname.win;
diff --git a/core.network/src/org/netbeans/core/network/utils/package-info.java b/core.network/src/org/netbeans/core/network/utils/package-info.java
new file mode 100644
index 0000000..dcb557e
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/package-info.java
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utilities related to network.
+ *
+ */
+package org.netbeans.core.network.utils;
diff --git a/core.network/test/unit/data/pacFiles/wpad space.dat b/core.network/test/unit/data/pacFiles/wpad space.dat
index e010448..3310e6a 100644
--- a/core.network/test/unit/data/pacFiles/wpad space.dat
+++ b/core.network/test/unit/data/pacFiles/wpad space.dat
@@ -1,3 +1,17 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
function FindProxyForURL(url, host)
{
// Make everything lower case.
diff --git a/core.network/test/unit/data/pacFiles/wpad.dat b/core.network/test/unit/data/pacFiles/wpad.dat
index e010448..3310e6a 100644
--- a/core.network/test/unit/data/pacFiles/wpad.dat
+++ b/core.network/test/unit/data/pacFiles/wpad.dat
@@ -1,3 +1,17 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
function FindProxyForURL(url, host)
{
// Make everything lower case.
diff --git a/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js b/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js
new file mode 100644
index 0000000..fb4c654
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js
@@ -0,0 +1,32 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+//
+// A PAC script which tries to break out of the sandbox and attempts
+// to do something really nasty.
+//
+
+function FindProxyForURL(url, host)
+{
+ alert("pac-test-sandbox-breakout.js");
+ var JRuntime = Java.type("java.lang.Runtime");
+ JRuntime.getRuntime().exec("dosomethingnasty.exe");
+
+
+
+ return "DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test1.js b/core.network/test/unit/data/pacFiles2/pac-test1.js
new file mode 100644
index 0000000..715e408
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test1.js
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+//
+// A very simple PAC file
+// Always returns "DIRECT".
+//
+
+function FindProxyForURL(url, host)
+{
+
+ url = url.toLowerCase();
+ host = host.toLowerCase();
+
+ alert("This is pac-test1.js");
+
+
+ return "DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test2.js b/core.network/test/unit/data/pacFiles2/pac-test2.js
new file mode 100644
index 0000000..72a3ae6
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test2.js
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+//
+// A very simple PAC file
+// Returns a list of 3 proxies.
+//
+
+function FindProxyForURL(url, host)
+{
+
+
+
+
+ url = url.toLowerCase();
+ host = host.toLowerCase();
+ alert("This is pac-test2.js");
+
+
+ return "PROXY localhost:8081; PROXY localhost:8082; DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test3.js b/core.network/test/unit/data/pacFiles2/pac-test3.js
new file mode 100644
index 0000000..d19f779
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test3.js
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+//
+// A very simple PAC file
+// Uses one of the time-sensitive functions (weekdayRange(), dateRange() or timeRange())
+// and will therefore imply no use of result caching.
+//
+
+function FindProxyForURL(url, host)
+{
+
+
+
+
+ url = url.toLowerCase();
+ host = host.toLowerCase();
+
+ alert("This is pac-test3.js");
+
+ if (weekdayRange("FRI"))
+ return "DIRECT";
+ else
+ return "PROXY localhost:8080";
+
+
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test4.js b/core.network/test/unit/data/pacFiles2/pac-test4.js
new file mode 100644
index 0000000..0848639
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test4.js
@@ -0,0 +1,242 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+//
+// PAC script which tests all helper functions.
+// All tests are "negative tests". If something fails
+// the test that fails will be returned in the proxy name.
+// If everything works as expected, the value "DIRECT" will be returned.
+//
+
+function FindProxyForURL(url, host) {
+
+ alert("This is pac-test4.js");
+
+
+//
+// isPlainHostName
+//
+
+ alert("isPlainHostName(): Doing tests...");
+
+ if (!isPlainHostName("somehost"))
+ return "PROXY isPlainHostName:1";
+ if (isPlainHostName("somehost.dom1.com"))
+ return "PROXY isPlainHostName:2";
+
+
+//
+// dnsDomainIs
+//
+ alert("dnsDomainIs(): Doing tests...");
+
+ if (!dnsDomainIs("www.netscape.com", ".netscape.com"))
+ return "PROXY dnsDomainIs:1";
+ if (!dnsDomainIs("www.netscape.com", "netscape.com"))
+ return "PROXY dnsDomainIs:2";
+ if (dnsDomainIs("www.netscape.com", ".com"))
+ return "PROXY dnsDomainIs:3";
+ if (dnsDomainIs("www.netscape.com", "somethingelse.com"))
+ return "PROXY dnsDomainIs:4";
+ if (dnsDomainIs("www.netscape.com", ""))
+ return "PROXY dnsDomainIs:5";
+ if (dnsDomainIs("www.netscape.com", null))
+ return "PROXY dnsDomainIs:6";
+
+//
+// localHostOrDomainIs
+//
+ alert("localHostOrDomainIs(): Doing tests...");
+
+ if (!localHostOrDomainIs("www.netscape.com", "www.netscape.com"))
+ return "PROXY localHostOrDomainIs:1";
+ if (!localHostOrDomainIs("www", "www.netscape.com"))
+ return "PROXY localHostOrDomainIs:2";
+ if (localHostOrDomainIs("www.netscape.com", "www.netscape.com2"))
+ return "PROXY localHostOrDomainIs:3";
+ if (localHostOrDomainIs("www1.netscape.com", "www2.netscape.com"))
+ return "PROXY localHostOrDomainIs:4";
+ if (localHostOrDomainIs("www1", "www2.netscape.com"))
+ return "PROXY localHostOrDomainIs:5";
+
+//
+// isResolvable
+//
+ alert("isResolvable(): Doing tests...");
+
+ if (!isResolvable("localhost"))
+ return "PROXY isResolvable:1";
+ if (!isResolvable("www.google.com")) // will only work if we have access to Internet during test
+ return "PROXY isResolvable:2";
+ if (isResolvable("gsd4hgbnw5xa.kd9greey934.kod82r"))
+ return "PROXY isResolvable:3";
+
+//
+// dnsResolve
+//
+ alert("dnsResolve(): Doing tests...");
+
+ if (!("127.0.0.1" === dnsResolve("localhost")))
+ return "PROXY dnsResolve:1";
+ if (!("8.8.8.8" === dnsResolve("google-public-dns-a.google.com"))) // will only work if we have access to Internet
+ return "PROXY dnsResolve:2";
+
+//
+// myIpAddress
+//
+ alert("myIpAddress(): Doing tests...");
+
+ var myIp = myIpAddress();
+ if ("127.0.0.1" === myIp)
+ return "PROXY myIpAddress:1";
+
+//
+// isInNet
+//
+ alert("isInNet(): Doing tests...");
+
+ if (!isInNet("localhost", "127.0.0.1", "255.255.255.255"))
+ return "PROXY isInNet:1";
+ if (!isInNet("google-public-dns-a.google.com", "8.8.8.8", "255.255.255.255"))
+ return "PROXY isInNet:2";
+ if (isInNet("192.168.1.3", "192.168.1.1", "255.255.255.255"))
+ return "PROXY isInNet:3";
+ if (!isInNet("192.168.1.3", "192.168.1.1", "255.255.255.0"))
+ return "PROXY isInNet:4";
+ if (!isInNet("192.168.1.1", "192.168.3.1", "255.255.0.255"))
+ return "PROXY isInNet:5";
+ if (!isInNet("10.10.10.10", "12.12.12.12", "0.0.0.0"))
+ return "PROXY isInNet:6";
+ if (isInNet("10.10.10.10", "12.12.12.12", "0.0.255.0"))
+ return "PROXY isInNet:7";
+
+//
+// dnsDomainLevels
+//
+ alert("dnsDomainLevels(): Doing tests...");
+
+ if (!(2 === dnsDomainLevels("www.netscape.com")))
+ return "PROXY dnsDomainLevels:1";
+ if (!(0 === dnsDomainLevels("www")))
+ return "PROXY dnsDomainLevels:2";
+ if (!(1 === dnsDomainLevels("www.")))
+ return "PROXY dnsDomainLevels:3";
+
+
+//
+// shExpMatch
+//
+ alert("shExpMatch(): Doing tests...");
+
+ if (!shExpMatch("www.netscape.com", "*netscape*"))
+ return "PROXY shExpMatch:1";
+ if (!shExpMatch("www.netscape.com", "*net*"))
+ return "PROXY shExpMatch:2";
+ if (shExpMatch("www.netscape.com", "*google*"))
+ return "PROXY shExpMatch:3";
+ if (!shExpMatch("www.netscape.com", "www*"))
+ return "PROXY shExpMatch:4";
+
+//
+// weekdayRange
+//
+ alert("weekdayRange(): Doing tests...");
+
+ if (!weekdayRange("MON", "SUN", "GMT"))
+ return "PROXY weekdayRange:1";
+ if (!weekdayRange("MON", "SUN", null))
+ return "PROXY weekdayRange:2";
+
+//
+// dateRange
+//
+ // Difficult to test from JavaScript side because it will depend on
+ // date when test is executed.
+ // We test if current date is between some values it will always be
+ // in between, which isn't much of a test!
+
+ alert("dateRange(): Doing tests...");
+
+ if (!(dateRange(1998, 2199)))
+ return "PROXY dateRange:1";
+ if (!(dateRange(1998, 2199, "GMT")))
+ return "PROXY dateRange:2";
+ if (!(dateRange(1, 31)))
+ return "PROXY dateRange:3";
+ if (!(dateRange(1, 31, "GMT")))
+ return "PROXY dateRange:4";
+ if (!(dateRange("JAN", "DEC")))
+ return "PROXY dateRange:5";
+ if (!(dateRange("JAN", "DEC", "GMT")))
+ return "PROXY dateRange:6";
+ if (!(dateRange(1, "JAN", 31, "DEC")))
+ return "PROXY dateRange:7";
+ if (!(dateRange(1, "JAN", 31, "DEC", "GMT")))
+ return "PROXY dateRange:8";
+ if (!(dateRange(1, "JAN", 1998, 31, "DEC", 2199)))
+ return "PROXY dateRange:9";
+ if (!(dateRange(1, "JAN", 1998, 31, "DEC", 2199, "GMT")))
+ return "PROXY dateRange:10";
+
+//
+// timeRange
+//
+ alert("timeRange(): Doing tests...");
+ // Difficult to test from JavaScript side because it will depend on
+ // time when test is executed.
+ // We test if current time is between 00:00:00 and 23:59:59, which
+ // isn't much of a test!
+
+ if (!(timeRange(0, 0, 0, 23, 59, 59)))
+ return "PROXY timeRange:1";
+ if (!(timeRange(0, 0, 0, 23, 59, 59, "GMT")))
+ return "PROXY timeRange:2";
+ if (!(timeRange(0, 0, 23, 59)))
+ return "PROXY timeRange:3";
+ if (!(timeRange(0, 0, 23, 59, "GMT")))
+ return "PROXY timeRange:4";
+
+
+//
+// isResolvableEx
+//
+ alert("isResolvableEx(): Doing tests...");
+
+ if (!(isResolvableEx("localhost")))
+ return "PROXY isResolvableEx:1";
+
+//
+// dnsResolveEx
+//
+ alert("dnsResolveEx(): Doing tests...");
+
+ if (!(dnsResolveEx("dfg3qyuaz.g4yst5.gfw58703sd") === ""))
+ return "PROXY dnsResolveEx:1";
+
+//
+// myIpAddressEx
+//
+ alert("myIpAddressEx(): Doing tests...");
+
+ var myIpEx = myIpAddressEx();
+ if ((myIpEx === "127.0.0.1") || (myIpEx === "0:0:0:0:0:0:0:1") || (myIpEx === "::1"))
+ return "PROXY myIpAddressEx:1";
+
+
+ alert("pac-test4.js: All tests passed");
+
+ return "DIRECT";
+
+}
\ No newline at end of file
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
index 728d2f1..2be4197 100644
--- a/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
@@ -88,13 +88,17 @@ public class ProxyAutoConfigTest extends NbTestCase {
URI uri = pac.getPacURI();
assertNotNull(uri);
assertNull(uri.getHost());
- List<Proxy> proxies = pac.findProxyForURL(new URI("http://netbeans.org"));
+ List<Proxy> proxies = pac.findProxyForURL(new URI("http://apache.org"));
assertEquals(1, proxies.size());
- assertTrue(pacFileLoc + ": " + proxies.get(0).toString(), proxies.get(0).toString().startsWith("HTTP @ www-proxy.us.oracle.com/"));
+ Proxy proxy = proxies.get(0);
+ assertEquals(pacFileLoc, Proxy.Type.HTTP, proxy.type());
+ assertEquals(pacFileLoc, "www-proxy.us.oracle.com:80", proxy.address().toString());
- proxies = pac.findProxyForURL(new URI("https://netbeans.org"));
+ proxies = pac.findProxyForURL(new URI("https://apache.org"));
assertEquals(1, proxies.size());
- assertTrue(pacFileLoc + ": " + proxies.get(0).toString(), proxies.get(0).toString().startsWith("HTTP @ www-proxy.us.oracle.com/"));
+ proxy = proxies.get(0);
+ assertEquals(pacFileLoc, Proxy.Type.HTTP, proxy.type());
+ assertEquals(pacFileLoc, "www-proxy.us.oracle.com:80", proxy.address().toString());
}
}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java
new file mode 100644
index 0000000..7d109a7
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluatorFactory;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ */
+public class PacEngineTest extends NbTestCase {
+
+ public PacEngineTest() {
+ super("Pac Evaluator Test");
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+
+
+
+ /**
+ * Test of toSemiColonListStr method, of class PacUtils.
+ */
+ @Test
+ public void testEngine() throws PacParsingException, IOException, URISyntaxException, PacValidationException {
+ System.out.println("toSemiColonListStr");
+
+ PacScriptEvaluatorFactory factory = new NbPacScriptEvaluatorFactory();
+
+ testPacFile("pac-test1.js", factory, 1, true);
+ testPacFile("pac-test2.js", factory, 3, true);
+ testPacFile("pac-test3.js", factory, 1, false);
+ testPacFileMalicious("pac-test-sandbox-breakout.js", factory);
+
+ testPacFile2("pac-test4.js", factory);
+ }
+
+ private String getPacSource(String pacFileName) throws IOException {
+ File pacFilesDir = new File(getDataDir(), "pacFiles2");
+ byte[] b = Files.readAllBytes((new File(pacFilesDir, pacFileName)).toPath());
+ return new String(b, StandardCharsets.UTF_8);
+ }
+
+ private void testPacFile(String pacFileName, PacScriptEvaluatorFactory factory, int expectedReturnedProxies, boolean usesCaching) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+ String pacSource = getPacSource(pacFileName);
+
+ PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+ URI testURL = new URI("http://netbeans.apache.org"); // doesn't actually matter which URL we use
+ List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+ assertEquals(expectedReturnedProxies, proxies.size());
+ assertEquals(usesCaching, pacEvaluator.usesCaching());
+ }
+
+ private void testPacFile2(String pacFileName, PacScriptEvaluatorFactory factory ) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+ String pacSource = getPacSource(pacFileName);
+
+ PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+ URI testURL = new URI("http://netbeans.apache.org"); // doesn't actually matter which URL we use
+ List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+ assertEquals(Collections.singletonList(Proxy.NO_PROXY), proxies);
+ }
+
+ private void testPacFileMalicious(String pacFileName, PacScriptEvaluatorFactory factory ) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+ String pacSource = getPacSource(pacFileName);
+
+ PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+ URI testURL = new URI("http://netbeans.apache.org"); // doesn't actually matter which URL we use
+ List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+ assertEquals(Collections.singletonList(Proxy.NO_PROXY), proxies);
+ }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java
new file mode 100644
index 0000000..3734f81
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime;
+
+/**
+ *
+ */
+public class PacUtilsDateTimeTest {
+
+ public PacUtilsDateTimeTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ /**
+ * Test of isInWeekdayRange method, of class PacUtilsDateTime.
+ */
+ @Test
+ public void testIsInWeekdayRange() throws Exception {
+ System.out.println("isInWeekdayRange");
+ Calendar cal = Calendar.getInstance();
+ int weekdayNum = cal.get(Calendar.DAY_OF_WEEK);
+ Date[] dates = new Date[7];
+ dates[weekdayNum-1] = cal.getTime();
+ for(int i=weekdayNum+1; i <= 7; i++) {
+ cal.add(Calendar.DATE, 1);
+ dates[i-1] = cal.getTime();
+ }
+ cal = Calendar.getInstance();
+ for(int i=weekdayNum-1; i >=1; i--) {
+ cal.add(Calendar.DATE, -1);
+ dates[i-1] = cal.getTime();
+ }
+
+ Date nowSUN = dates[0];
+ Date nowMON = dates[1];
+ Date nowTUE = dates[2];
+ Date nowWED = dates[3];
+ Date nowTHU = dates[4];
+ Date nowFRI = dates[5];
+ Date nowSAT = dates[6];
+
+ assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "MON"));
+ assertFalse(PacUtilsDateTime.isInWeekdayRange(nowMON, "SUN"));
+ assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "MON", "SUN"));
+ assertTrue(PacUtilsDateTime.isInWeekdayRange(nowSAT, "FRI", "TUE"));
+ assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "FRI", "TUE"));
+ assertFalse(PacUtilsDateTime.isInWeekdayRange(nowTHU, "FRI", "TUE"));
+ assertFalse(PacUtilsDateTime.isInWeekdayRange(nowTHU, "SAT", "MON"));
+ assertTrue(PacUtilsDateTime.isInWeekdayRange(nowSUN, "SAT", "MON", "GMT"));
+ assertFalse(PacUtilsDateTime.isInWeekdayRange(nowSUN, "MON", "TUE", "GMT"));
+
+ }
+
+ /**
+ * Test of isInTimeRange method, of class PacUtilsDateTime.
+ */
+ @Test
+ public void testIsInTimeRange() throws Exception {
+ System.out.println("isInTimeRange");
+
+ SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
+
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("13:00:00"), 13));
+ assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13));
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 14));
+ assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:01:00"), 13, 14));
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 14, 30));
+ assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 13, 59));
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 14, 0));
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 21));
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 20));
+ assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 19));
+ assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 19, "GMT"));
+
+ assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("23:12:20"), 22, 4));
+
+ }
+
+ /**
+ * Test of isInDateRange method, of class PacUtilsDateTime.
+ */
+ @Test
+ public void testIsInDateRange() throws Exception {
+ System.out.println("isInDateRange");
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "FEB"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "MAR", "JUN"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "DEC", "MAR"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-12-28"), "DEC", "MAR"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), "DEC", "MAR"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-11-28"), "DEC", "MAR"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), "DEC", "MAR"));
+
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 20, 30));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 28));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 27));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 27, 28));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 10, 12));
+
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2016));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2017));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2016, 2020));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2012, 2015));
+
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 15, "FEB", 31, "MAR"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), 15, "FEB", 31, "MAR"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), 15, "NOV", 31, "JAN"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-12-28"), 15, "NOV", 31, "JAN"));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-01-28"), 15, "NOV", 31, "JAN"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-02-01"), 15, "NOV", 31, "JAN"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-11-01"), 15, "NOV", 31, "JAN"));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-09-16"), 15, "NOV", 31, "JAN"));
+
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-11-01"), "NOV", 2016, "JAN", 2017));
+ assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2017-01-26"), "NOV", 2016, "JAN", 2017));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-10-30"), "NOV", 2016, "JAN", 2017));
+ assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2017-02-01"), "NOV", 2016, "JAN", 2017));
+ }
+
+
+
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java
new file mode 100644
index 0000000..26f9e8d
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java
@@ -0,0 +1,150 @@
+/*
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class PacUtilsTest {
+
+ public PacUtilsTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+
+ /**
+ * Test of toStrippedURLStr method, of class PacUtils.
+ */
+ @Test
+ public void testToStrippedURLStr() throws URISyntaxException {
+ System.out.println("toStrippedURLStr");
+
+ URI uri = new URI("https://mary@somehost.apache.org:8081/path/to/something?x1=Christmas&user=unknown");
+
+ String str = PacUtils.toStrippedURLStr(uri);
+ String expResult = "https://somehost.apache.org:8081/";
+ assertEquals(expResult, str);
+ }
+
+
+ /**
+ * Test of toSemiColonListStr method, of class PacUtils.
+ */
+ @Test
+ public void testToSemiColonListStr_InetAddressArr() {
+ System.out.println("toSemiColonListStr");
+ InetAddress[] addresses = getInetAddressListArr(
+ new String[]{
+ "192.168.1.34",
+ "192.168.1.56"
+ });
+ String expResult = "192.168.1.34;192.168.1.56";
+ String result = PacUtils.toSemiColonListInetAddress(addresses);
+ assertEquals(expResult, result);
+
+ addresses = getInetAddressListArr(
+ new String[]{
+ "192.168.1.34"
+ });
+ expResult = "192.168.1.34";
+ result = PacUtils.toSemiColonListInetAddress(addresses);
+ assertEquals(expResult, result);
+
+ addresses = getInetAddressListArr(
+ new String[]{});
+ expResult = "";
+ result = PacUtils.toSemiColonListInetAddress(addresses);
+ assertEquals(expResult, result);
+
+ }
+
+
+
+ /**
+ * Test of ipPrefixMatch method, of class PacUtils.
+ */
+ @Test
+ public void testIpPrefixMatch() {
+ System.out.println("ipPrefixMatch");
+ testIpPrefixMatch0("192.168.2.34", "192.168.2.45/32", false);
+ testIpPrefixMatch0("192.168.2.122", "192.168.2.0/25", true);
+ testIpPrefixMatch0("192.168.2.129", "192.168.2.0/25", false);
+ testIpPrefixMatch0("192.168.2.129", "192.168.0.0/16", true);
+ testIpPrefixMatch0("192.169.2.129", "192.168.0.0/16", false);
+
+ testIpPrefixMatch0("192.169.2.129", "3ffe:8311:ffff/48", false);
+ testIpPrefixMatch0("192.169.2.129", "3ffe:8311:ffff/24", false);
+ testIpPrefixMatch0("3ffe:8311:ffff:1a2b::", "3ffe:8311:ffff/24", true);
+
+ }
+
+ private void testIpPrefixMatch0(String address, String ipPrefix, boolean expResult) {
+ try {
+ InetAddress ipAddress = InetAddress.getByName(address);
+ if (PacUtils.ipPrefixMatch(ipAddress, ipPrefix) != expResult) {
+ fail(" For address=" + address + ", ipPrefix=" + ipPrefix + " the expected result was " + expResult);
+ }
+ } catch (UnknownHostException ex) {
+ fail("Error in test case : " + ex.getMessage() );
+ }
+ }
+
+
+ private InetAddress[] getInetAddressListArr(String[] ipAddresses) {
+ List<InetAddress> list = getInetAddressList(ipAddresses);
+ return list.toArray(new InetAddress[list.size()]);
+ }
+
+ private List<InetAddress> getInetAddressList(String[] ipAddresses) {
+ List<InetAddress> list = new ArrayList<>();
+ for(String s : ipAddresses) {
+ try {
+ list.add(InetAddress.getByName(s));
+ } catch (UnknownHostException ex) {
+ fail("Error in test case " + ex.getMessage() );
+ }
+ }
+ return list;
+ }
+
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java
new file mode 100644
index 0000000..806dea9
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java
@@ -0,0 +1,333 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.netbeans.core.network.utils.FakeDns;
+
+/**
+ *
+ */
+public class PacHelperMethodsImplTest {
+
+ private FakeDns fakeDns;
+ private InetAddress ipv4Addr;
+ private InetAddress ipv6Addr;
+ private final NbPacHelperMethods helpers = new NbPacHelperMethods();
+ private final static List<String> WEEKDAY_NAMES = Collections.unmodifiableList(
+ Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"));
+
+ private final static List<String> MONTH_NAMES = Collections.unmodifiableList(
+ Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
+
+ public PacHelperMethodsImplTest() {
+
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() throws UnknownHostException {
+ fakeDns = new FakeDns();
+ fakeDns.install(true);
+ ipv4Addr = InetAddress.getByName("172.217.17.36"); // just some random address
+ ipv6Addr = InetAddress.getByName("2a00:1450:400e:804::2004"); // just some random address
+ }
+
+ @After
+ public void tearDown() {
+ fakeDns.unInstall();
+ }
+
+ /**
+ * Test of isPlainHostName method, of class helpers.
+ */
+ @org.junit.Test
+ public void testIsPlainHostName() {
+ assertTrue(helpers.isPlainHostName("somehost"));
+ assertFalse(helpers.isPlainHostName("somehost.dom1.com"));
+ }
+
+ /**
+ * Test of dnsDomainIs method, of class helpers.
+ */
+ @org.junit.Test
+ public void testDnsDomainIs() {
+ assertTrue(helpers.dnsDomainIs("www.netscape.com", ".netscape.com"));
+ assertTrue(helpers.dnsDomainIs("www.netscape.com", "netscape.com"));
+ assertFalse(helpers.dnsDomainIs("www.netscape.com", ".com"));
+ assertFalse(helpers.dnsDomainIs("www.netscape.com", "somethingelse.com"));
+ assertFalse(helpers.dnsDomainIs("www.netscape.com", ""));
+ assertFalse(helpers.dnsDomainIs("www.netscape.com", null));
+ }
+
+ /**
+ * Test of localHostOrDomainIs method, of class helpers.
+ */
+ @org.junit.Test
+ public void testLocalHostOrDomainIs() {
+ assertTrue(helpers.localHostOrDomainIs("www.netscape.com", "www.netscape.com"));
+ assertTrue(helpers.localHostOrDomainIs("www", "www.netscape.com"));
+ assertFalse(helpers.localHostOrDomainIs("www.netscape.com", "www.netscape.com2"));
+ assertFalse(helpers.localHostOrDomainIs("www1.netscape.com", "www2.netscape.com"));
+ assertFalse(helpers.localHostOrDomainIs("www1", "www2.netscape.com"));
+ }
+
+ /**
+ * Test of isResolvable method, of class helpers.
+ */
+ @org.junit.Test
+ public void testIsResolvable() {
+ fakeDns.addForwardResolution("isresolvable1host", new InetAddress[]{ipv4Addr});
+ fakeDns.addForwardResolution("isresolvable2host", new InetAddress[]{ipv6Addr});
+
+ assertTrue(helpers.isResolvable("isresolvable1host"));
+ assertFalse(helpers.isResolvable("isresolvable2host")); // method should never return IPv6 addr
+ assertFalse(helpers.isResolvable("foo-bar-does-not-exist"));
+ }
+
+ /**
+ * Test of dnsResolve method, of class helpers.
+ */
+ @org.junit.Test
+ public void testDnsResolve() {
+
+ fakeDns.addForwardResolution("dnsresolve1host", new InetAddress[]{ipv4Addr});
+ fakeDns.addForwardResolution("dnsresolve2host", new InetAddress[]{ipv6Addr});
+
+ assertEquals(ipv4Addr.getHostAddress(), helpers.dnsResolve("dnsresolve1host"));
+
+ assertNull(helpers.dnsResolve("dnsresolve2host")); // should not be able to resolve IPv6 address
+
+ }
+
+ /**
+ * Test of myIpAddress method, of class helpers.
+ */
+ @org.junit.Test
+ public void testMyIpAddress() {
+ String x = helpers.myIpAddress();
+ }
+
+ /**
+ * Test of isInNet method, of class helpers.
+ */
+ @org.junit.Test
+ public void testIsInNet() throws UnknownHostException {
+ fakeDns.addForwardResolution("isinnet1host", new InetAddress[]{InetAddress.getByName("127.0.0.1")});
+ assertTrue(helpers.isInNet("isinnet1host", "127.0.0.1", "255.255.255.255"));
+
+ fakeDns.addForwardResolution("isinnet2host", new InetAddress[]{InetAddress.getByName("::1"), InetAddress.getByName("127.0.0.1")});
+ assertTrue(helpers.isInNet("isinnet2host", "127.0.0.1", "255.255.255.255"));
+
+ fakeDns.addForwardResolution("isinnet3host", new InetAddress[]{InetAddress.getByName("::1"), InetAddress.getByName("127.0.0.99")});
+ assertFalse(helpers.isInNet("isinnet3host", "127.0.0.1", "255.255.255.255"));
+
+ fakeDns.addForwardResolution("isinnet4host", new InetAddress[]{InetAddress.getByName("::1")});
+ assertFalse(helpers.isInNet("isinnet4host", "127.0.0.1", "255.255.255.255"));
+
+ fakeDns.addForwardResolution("google-public-dns-a.google.com", new InetAddress[]{InetAddress.getByName("8.8.8.8")});
+ assertTrue(helpers.isInNet("google-public-dns-a.google.com", "8.8.8.8", "255.255.255.255"));
+
+ assertFalse(helpers.isInNet("192.168.1.3", "192.168.1.1", "255.255.255.255"));
+ assertTrue(helpers.isInNet("192.168.1.3", "192.168.1.1", "255.255.255.0"));
+ assertTrue(helpers.isInNet("192.168.1.1", "192.168.3.1", "255.255.0.255"));
+ assertTrue(helpers.isInNet("10.10.10.10", "12.12.12.12", "0.0.0.0"));
+ assertFalse(helpers.isInNet("10.10.10.10", "12.12.12.12", "0.0.255.0"));
+ }
+
+ /**
+ * Test of isInNetEx method, of class helpers.
+ */
+ @org.junit.Test
+ public void testIsInNetEx() throws UnknownHostException {
+
+ assertTrue(helpers.isInNetEx("198.95.249.79", "198.95.249.79/32"));
+ assertFalse(helpers.isInNetEx("198.95.249.78", "198.95.249.79/32"));
+
+ assertTrue(helpers.isInNetEx("198.95.33.22", "198.95.0.0/16"));
+ assertFalse(helpers.isInNetEx("198.96.33.22", "198.95.0.0/16"));
+ assertTrue(helpers.isInNetEx("198.96.33.22", "198.95.0.0/16;198.96.0.0/16"));
+
+ fakeDns.addForwardResolution("isinnetex1host", new InetAddress[]{InetAddress.getByName("2a00:1450:400e:804::2004")});
+ assertTrue(helpers.isInNetEx("isinnetex1host", "2a00:1450:400e/48"));
+ assertFalse(helpers.isInNetEx("isinnetex1host", "198.95.0.0/16"));
+
+ }
+
+ /**
+ * Test of dnsDomainLevels method, of class helpers.
+ */
+ @org.junit.Test
+ public void testDnsDomainLevels() {
+ assertEquals(2, helpers.dnsDomainLevels("www.netscape.com"));
+ assertEquals(0, helpers.dnsDomainLevels("www"));
+ assertEquals(1, helpers.dnsDomainLevels("www.")); // somewhat undefined from Netscape spec what this should return, probably a corner case
+ }
+
+ /**
+ * Test of shExpMatch method, of class helpers.
+ */
+ @org.junit.Test
+ public void testShExpMatch() {
+ assertTrue(helpers.shExpMatch("www.netscape.com", "*netscape*"));
+ assertTrue(helpers.shExpMatch("www.netscape.com", "*net*"));
+ assertFalse(helpers.shExpMatch("www.netscape.com", "*google*"));
+ assertTrue(helpers.shExpMatch("www.netscape.com", "www*"));
+ }
+
+ /**
+ * Test of weekdayRange method, of class helpers.
+ */
+ @org.junit.Test
+ public void testWeekdayRange() {
+ assertTrue(helpers.weekdayRange("MON", "SUN", "GMT"));
+ assertTrue(helpers.weekdayRange("MON", "SUN", null));
+
+ Calendar now = Calendar.getInstance();
+ int weekdayNo = now.get(Calendar.DAY_OF_WEEK); // 1-7 (1=Sunday, 7=Monday)
+ int weekdayNoBefore = weekdayNo - 1;
+ if (weekdayNoBefore == 0) {
+ weekdayNoBefore = 7;
+ }
+ int weekdayNoAfter = weekdayNo + 1;
+ if (weekdayNoAfter == 8) {
+ weekdayNoAfter = 1;
+ }
+ String weekdayNow = WEEKDAY_NAMES.get(weekdayNo - 1);
+ String weekdayBefore = WEEKDAY_NAMES.get(weekdayNoBefore - 1);
+ String weekdayAfter = WEEKDAY_NAMES.get(weekdayNoAfter - 1);
+ assertTrue(helpers.weekdayRange(weekdayNow, null, null));
+ assertTrue(helpers.weekdayRange(weekdayBefore, weekdayAfter, null));
+ }
+
+ /**
+ * Test of dateRange method, of class helpers.
+ */
+ @org.junit.Test
+ public void testDateRange() {
+ System.out.println("dateRange");
+
+ Calendar now = Calendar.getInstance();
+
+ int nowDay = now.get(Calendar.DAY_OF_MONTH);
+ String nowMonth = MONTH_NAMES.get(now.get(Calendar.MONTH));
+ int nowYear = now.get(Calendar.YEAR);
+
+ assertTrue(helpers.dateRange(nowDay));
+ assertFalse(helpers.dateRange(nowDay + 1));
+ assertTrue(helpers.dateRange(nowMonth));
+ assertTrue(helpers.dateRange(nowYear));
+ assertFalse(helpers.dateRange(nowYear + 1));
+ assertTrue(helpers.dateRange(nowDay, nowMonth, nowDay, nowMonth));
+ assertTrue(helpers.dateRange(nowMonth, nowYear, nowMonth, nowYear));
+ assertFalse(helpers.dateRange(nowMonth, nowYear + 1, nowMonth, nowYear + 1));
+ }
+
+ /**
+ * Test of timeRange method, of class helpers.
+ */
+ @org.junit.Test
+ public void testTimeRange() {
+ Calendar now = Calendar.getInstance();
+
+ final int nowHour = now.get(Calendar.HOUR_OF_DAY);
+ final int nowMinute = now.get(Calendar.MINUTE);
+ final int nowSecond = now.get(Calendar.SECOND); // not used
+ int nowMinuteNext = (nowMinute == 59) ? 0 : nowMinute + 1;
+ int nowHourNext = (nowMinuteNext == 0) ? ((nowHour == 23) ? 0 : nowHour + 1) : nowHour;
+ assertTrue(helpers.timeRange(nowHour));
+ assertTrue(helpers.timeRange(nowHour, nowHour));
+ assertTrue(helpers.timeRange(nowHour, nowMinute, nowHourNext, nowMinuteNext));
+ assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 5)); // second timestamp before first - should give false result
+ assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 5, "GMT")); // second timestamp before first - should give false result
+ assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 12, "GMT")); // test will fail if we happen to run test at exactly 23:31:12 , unlikely!
+
+ // We don't dare test something with nowSecond as there's a real
+ // risk that two calendars (the one used in the actual code vs the one used in this
+ // test class) are not on the same second .. and the test will then give incorrect result.
+ }
+
+ /**
+ * Test of isResolvableEx method, of class helpers.
+ */
+ @org.junit.Test
+ public void testIsResolvableEx() {
+ fakeDns.addForwardResolution("isresolvableex1host", new InetAddress[]{ipv4Addr});
+ fakeDns.addForwardResolution("isresolvableex2host", new InetAddress[]{ipv6Addr});
+
+ assertTrue(helpers.isResolvableEx("isresolvableex1host"));
+ assertTrue(helpers.isResolvableEx("isresolvableex2host"));
+ assertFalse(helpers.isResolvableEx("foo-bar-does-not-exist"));
+ }
+
+ /**
+ * Test of dnsResolveEx method, of class helpers.
+ */
+ @org.junit.Test
+ public void testDnsResolveEx() {
+
+ fakeDns.addForwardResolution("dnsresolveex1host", new InetAddress[]{ipv4Addr});
+ fakeDns.addForwardResolution("dnsresolveex2host", new InetAddress[]{ipv6Addr});
+
+ assertEquals(ipv4Addr.getHostAddress(), helpers.dnsResolveEx("dnsresolveex1host"));
+ assertEquals(ipv6Addr.getHostAddress(), helpers.dnsResolveEx("dnsresolveex2host"));
+ }
+
+ /**
+ * Test of sortIpAddressList method, of class helpers.
+ */
+ @org.junit.Test
+ public void testSortIpAddressList() {
+
+ String input
+ = "10.2.3.9"
+ + ";" + "2001:4898:28:3:201:2ff:feea:fc14"
+ + ";" + "::1"
+ + ";" + "127.0.0.1"
+ + ";" + "::9";
+
+ String expected
+ = "::1"
+ + ";" + "::9"
+ + ";" + "2001:4898:28:3:201:2ff:feea:fc14"
+ + ";" + "10.2.3.9"
+ + ";" + "127.0.0.1";
+ assertEquals(expected, helpers.sortIpAddressList(input));
+ }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java b/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java
new file mode 100644
index 0000000..8631445
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java
@@ -0,0 +1,216 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import sun.net.spi.nameservice.NameService;
+
+/**
+ * A simple In-Memory implementation of a Name Service for Java. This
+ * is useful for unit tests.
+ *
+ * <p>
+ * Uses dirty tricks (reflection) and classes from sun.* package. For unit
+ * testing this is acceptable. For anything else: no!
+ *
+ * <p>
+ * Credit: The code is inspired by similar approach in Apache Kudo project.
+ *
+ * @author lbruun
+ */
+public class FakeDns {
+
+
+ private final Map<String, InetAddress[]> forwardResolutions = new ConcurrentHashMap<>();
+ private final Map<InetAddress, String> reverseResolutions = new ConcurrentHashMap<>();
+ private final List<NameService> orgNameServices = new ArrayList<>();
+ private volatile boolean installed = false;
+ private final AtomicLong delayAnsweringByMs = new AtomicLong(0);
+
+ public FakeDns() {
+ }
+
+
+ /**
+ * Adds a hostname-to-address(es) mapping. This will also add the
+ * corresponding reverse mapping(s).
+ * @param hostname
+ * @param ipAdresses
+ */
+ public void addForwardResolution(String hostname, InetAddress[] ipAdresses) {
+ forwardResolutions.put(hostname, ipAdresses);
+ for(InetAddress a : ipAdresses) {
+ reverseResolutions.put(a, hostname);
+ }
+ }
+
+ /**
+ * Adds an address-to-hostname mapping. The
+ * {@link #addForwardResolution(java.net.InetAddress, java.lang.String) addReverseResolution()}
+ * method will automatically add reverse mappings too, so this method is only
+ * necessary if you want to add a reverse mapping for which no forward mapping
+ * exist, i.e. a "standalone" reverse mapping.
+ *
+ * @param ipAddress
+ * @param hostname
+ */
+ public void addReverseResolution(InetAddress ipAddress, String hostname) {
+ reverseResolutions.put(ipAddress, hostname);
+ }
+
+ /**
+ * Install the fake DNS resolver into the Java runtime.
+ *
+ * <p>
+ * In Java, name services are chained. So if one name service doesn't
+ * respond (or responds with {@code UnknownHostException}) then the next
+ * name service in the chain will be queried. If you want to do negative
+ * tests, i.e. "this should give me UnknownHostException", then you'll have
+ * to remove other name services too, so that only the FakeDns name service
+ * is installed. You do this by setting {@code removeOthers} to {@code true}.
+ *
+ * @param removeOthers if other name services should be removed
+ */
+ public synchronized void install(boolean removeOthers) {
+ if (installed) {
+ return;
+ }
+ List<NameService> nameServices = getKnownNameServices();
+ if (nameServices != null) {
+
+ if (removeOthers) {
+ orgNameServices.clear();
+ orgNameServices.addAll(nameServices);
+ nameServices.clear();
+ }
+ // Install ourselves in position 0, ahead of everyone else
+ nameServices.add(0, new NameServiceInMemory());
+
+ installed = true;
+ }
+ }
+
+ /**
+ * Puts Java's internal name service chain back to its original state.
+ * Clears any forward or reverse mappings that has been put into the
+ * FakeDns.
+ */
+ public synchronized void unInstall() {
+ if (!installed) {
+ return;
+ }
+ List<NameService> nameServices = getKnownNameServices();
+ if (nameServices != null) {
+
+ NameService ns = nameServices.get(0);
+ if (ns != null) {
+ if (ns instanceof NameServiceInMemory) {
+ nameServices.remove(0);
+ installed = false;
+ }
+ }
+
+ if (!orgNameServices.isEmpty()) {
+ nameServices.addAll(orgNameServices);
+ orgNameServices.clear();
+ }
+ }
+ forwardResolutions.clear();
+ reverseResolutions.clear();
+ }
+
+ /**
+ * Sets a delay on answering, thereby simulating a DNS server which
+ * is slow to respond.
+ *
+ * <p>
+ * Set to 0 (zero) to remove the delay again.
+ *
+ * <p>
+ * Beware that Java also has a name service cache, so our FakeDns name
+ * service may not be queried at all as a we expect. Therefore, when
+ * testing for a delayed response it is best to it on a newly
+ * inserted mapping.
+ *
+ * @param msWait milliseconds to wait before answering to calls
+ */
+ public void delayAnsweringBy(long msWait ) {
+ delayAnsweringByMs.set(msWait);
+ }
+
+ private List<NameService> getKnownNameServices() {
+ try {
+ Field field = InetAddress.class.getDeclaredField("nameServices");
+ field.setAccessible(true);
+ return (List<NameService>) field.get(null);
+ } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
+ //throw Throwables.propagate(e);
+ }
+ return null;
+ }
+
+ private class NameServiceInMemory implements NameService {
+
+ @Override
+ public InetAddress[] lookupAllHostAddr(String host)
+ throws UnknownHostException {
+ sleep();
+ InetAddress[] inetAddresses = forwardResolutions.get(host);
+ if (inetAddresses != null) {
+ return inetAddresses;
+ }
+
+ // Since Java 7, JDK chains to the next name servicer provider
+ // automatically, so when we throw UnknownHostException, the
+ // resolution will not actually stop (provided there are more name
+ // service implementations in the chain), instead it will continue
+ // to the next in the chain. This means that our FakeDns cannot
+ // be used to test cases where we want to test for a negative
+ throw new UnknownHostException();
+ }
+
+ @Override
+ public String getHostByAddr(byte[] addr) throws UnknownHostException {
+ sleep();
+ String hostname = reverseResolutions.get(InetAddress.getByAddress(addr));
+ if (hostname != null) {
+ return hostname;
+ }
+
+ throw new UnknownHostException();
+ }
+
+ private void sleep() {
+ final long delay = delayAnsweringByMs.get();
+ if (delay != 0) {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java
new file mode 100644
index 0000000..47b6f4f
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeoutException;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class IpAddressUtilsTest {
+
+ private FakeDns fakeDns;
+ private InetAddress ipv4Addr;
+ private InetAddress ipv6Addr;
+
+ public IpAddressUtilsTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() throws UnknownHostException {
+ fakeDns = new FakeDns();
+ fakeDns.install(true);
+
+ ipv4Addr = InetAddress.getByName("172.217.17.36"); // just some random address
+ ipv6Addr = InetAddress.getByName("2a00:1450:400e:804::2004"); // just some random address
+ }
+
+ @After
+ public void tearDown() {
+ fakeDns.unInstall();
+ }
+
+ /**
+ * Test of nameResolve method, of class IpAddressUtils.
+ */
+ @Test
+ public void testNameResolveArr() throws Exception {
+ System.out.println("nameResolve");
+
+ InetAddress[] addrInput = new InetAddress[]{ipv4Addr, ipv6Addr};
+ fakeDns.addForwardResolution("test1", addrInput);
+ InetAddress[] addresses2 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.ANY_IPV4_PREF);
+ assertArrayEquals(addrInput, addresses2);
+ InetAddress[] addresses3 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.ANY_IPV6_PREF);
+ assertArrayEquals(new InetAddress[]{ipv6Addr, ipv4Addr}, addresses3);
+ InetAddress[] addresses4 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.IPV4_ONLY);
+ assertArrayEquals(new InetAddress[]{ipv4Addr}, addresses4);
+ InetAddress[] addresses5 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.IPV6_ONLY);
+ assertArrayEquals(new InetAddress[]{ipv6Addr}, addresses5);
+
+
+ fakeDns.delayAnsweringBy(4000);
+ fakeDns.addForwardResolution("test2", addrInput);
+ try {
+ InetAddress[] addresses6 = IpAddressUtils.nameResolveArr("test2", 1000, IpAddressUtils.IpTypePreference.ANY_IPV4_PREF);
+ fail("This should have timed out");
+ } catch (TimeoutException ex) {
+ }
+
+ }
+
+ /**
+ * Test of isValidIpv4Address method, of class IpAddressUtils.
+ */
+ @Test
+ public void testIsValidIP4Address() {
+ System.out.println("isValidIP4Address");
+ assertTrue(IpAddressUtils.isValidIpv4Address("192.168.1.33"));
+ assertFalse(IpAddressUtils.isValidIpv4Address(".3245.23"));
+ assertFalse(IpAddressUtils.isValidIpv4Address("192.923.1.33"));
+ assertFalse(IpAddressUtils.isValidIpv4Address("192.168.1."));
+ }
+
+ /**
+ * Test of looksLikeIpv4Literal method, of class IpAddressUtils.
+ */
+ @Test
+ public void testLooksLikeIpv4Literal() {
+ System.out.println("looksLikeIpv4Literal");
+ assertTrue(IpAddressUtils.looksLikeIpv4Literal("192.168.1.33"));
+ assertTrue(IpAddressUtils.looksLikeIpv4Literal("192.168"));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("1923.43.23.2"));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("192"));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("192.a68"));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("abc."));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal(".abc"));
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("abc.x"));
+
+ // ::8.144.52.38 is a valid IPv6 address, but is not an IPv4 address
+ assertFalse(IpAddressUtils.looksLikeIpv4Literal("::8.144.52.38"));
+ }
+
+ /**
+ * Test of looksLikeIpv6Literal method, of class IpAddressUtils.
+ */
+ @Test
+ public void testLooksLikeIpv6Literal() {
+ System.out.println("looksLikeIpv6Literal");
+ assertFalse(IpAddressUtils.looksLikeIpv6Literal("192.168.1.33"));
+ assertFalse(IpAddressUtils.looksLikeIpv6Literal("3d"));
+
+ assertTrue(IpAddressUtils.looksLikeIpv6Literal("::1"));
+ assertTrue(IpAddressUtils.looksLikeIpv6Literal("::1:3d"));
+ assertTrue(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A"));
+ assertFalse(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A:"));
+ assertFalse(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A:3A"));
+
+ // The values below are certainly not valid IPv6 addresses
+ // but they kinda look like one and they certainly cannot be
+ // hostnames. This is enough for the method to return true.
+ assertTrue(IpAddressUtils.looksLikeIpv6Literal(":foobarfoorbar"));
+ assertTrue(IpAddressUtils.looksLikeIpv6Literal("foobarfoorbar:"));
+ }
+
+ /**
+ * Test of removeDomain method, of class IpAddressUtils.
+ */
+ @Test
+ public void testRemoveDomain() {
+ System.out.println("removeDomain");
+
+ String input;
+
+ input = ".hardlymakessense";
+ assertEquals("", IpAddressUtils.removeDomain(input));
+
+ input = "chicago.internal.net";
+ assertEquals("chicago", IpAddressUtils.removeDomain(input));
+
+ input = "chicago";
+ assertEquals(input, IpAddressUtils.removeDomain(input));
+
+ input = "123.123";
+ assertEquals(input, IpAddressUtils.removeDomain(input));
+
+ input = "3D::123.123";
+ assertEquals(input, IpAddressUtils.removeDomain(input));
+ }
+
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java
new file mode 100644
index 0000000..c71c5a9
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.InetAddress;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * @author lbruun
+ */
+public class LocalAddressUtilsTest {
+
+ public LocalAddressUtilsTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+
+
+ /**
+ * Test of getLocalHostAddr method, of class LocalAddressUtils.
+ */
+ @Test
+ public void testGetLocalHostAddr() {
+ System.out.println("getLocalHostAddr");
+ }
+
+ /**
+ * Test of getLoopbackAddress method, of class LocalAddressUtils.
+ */
+ @Test
+ public void testGetLoopbackAddress() {
+ System.out.println("getLoopbackAddress");
+ }
+
+ /**
+ * Test of refreshLocalNetworkInterfaceAddr method, of class LocalAddressUtils.
+ */
+ @Test
+ public void testRefreshLocalNetworkInterfaceAddr() {
+ System.out.println("refreshLocalNetworkInterfaceAddr");
+ }
+
+ /**
+ * Test of refreshLocalHostAddr method, of class LocalAddressUtils.
+ */
+ @Test
+ public void testRefreshLocalHostAddr() {
+ System.out.println("refreshLocalHostAddr");
+ }
+
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java
new file mode 100644
index 0000000..b40ca2f
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.util.UUID;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author lbruun
+ */
+public class SimpleCacheTest {
+
+ private final SimpleObjCache<String,String> cache;
+ public SimpleCacheTest() {
+ cache = new SimpleObjCache<>(10);
+ }
+
+
+
+ @Test
+ public void testCache() {
+ System.out.println("testCache");
+
+ cache.put("01", UUID.randomUUID().toString());
+ cache.put("02", UUID.randomUUID().toString());
+ cache.put("03", UUID.randomUUID().toString());
+ cache.put("04", UUID.randomUUID().toString());
+ cache.put("05", UUID.randomUUID().toString());
+ cache.put("06", UUID.randomUUID().toString());
+ cache.put("07", UUID.randomUUID().toString());
+ cache.put("08", UUID.randomUUID().toString());
+ cache.put("09", UUID.randomUUID().toString());
+ cache.put("10", UUID.randomUUID().toString());
+
+ shortWait();
+ assertNotNull(cache.get("01")); // reset last usage timestamp for this element
+
+ // We've reached max capacity. Elements will start
+ // to get evicted.
+
+ shortWait();
+ cache.put("11", UUID.randomUUID().toString());
+ assertTrue(cache.getCacheSize() == 10);
+
+
+ assertNull(cache.get("02")); // "02" will have been evicted by now
+
+ shortWait();
+ cache.put("12", UUID.randomUUID().toString());
+ assertTrue(cache.getCacheSize() == 10);
+
+ assertNull(cache.get("03")); // "03" will have been evicted by now
+ assertNotNull(cache.get("01")); // "01" is still there.
+
+ }
+
+ @Test
+ public void testCacheConcurrency() {
+ System.out.println("testCache - concurrency ");
+
+ // See if we can provoke ConcurrentModificationException.
+ // That should not be the case, because of use of ConcurrentHashMap.
+
+ // However, the use of ConcurrentHashMap is not immune to
+ // NoSuchElementException being thrown from the Optional#get() in the
+ // findEvictionCandidate() method.
+
+ SimpleObjCache<Integer, String> intCache = new SimpleObjCache<>(3); // very low capacity cache so that we get a lot of contention
+
+ int noOfThreads = 15;
+ CacheTesterThread[] threads = new CacheTesterThread[noOfThreads];
+ // Prepare threads
+ for(int i = 0; i<noOfThreads; i++) {
+ threads[i] = new CacheTesterThread(intCache);
+ }
+ // Start threads
+ for(int i = 0; i<noOfThreads; i++) {
+ threads[i].start();
+ }
+
+ }
+
+
+
+ private void shortWait() {
+ // The cache only tracks usage of elements down to the millisecond.
+ // So, potentially, two elements can have exactly the same timestamp.
+ // For this reason, it is necessary to wait a bit in order to
+ // accurately predict which element will be evicted, thus only needed
+ // if we need to predict which element will be evicted.
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException ex) {
+ }
+ }
+
+
+ private static class CacheTesterThread extends Thread {
+
+ private final SimpleObjCache<Integer, String> cache;
+
+ public CacheTesterThread(SimpleObjCache cache) {
+ this.cache = cache;
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 100000; i++) {
+ // Do a bit of both writing and reading
+ cache.put(i % 10, "foo");
+ String val = cache.get(i);
+ cache.put((i % 10) + 1, val);
+ }
+ }
+ }
+}
diff --git a/libs.jna.platform/manifest.mf b/libs.jna.platform/manifest.mf
index c32f2f7..e7ad862 100644
--- a/libs.jna.platform/manifest.mf
+++ b/libs.jna.platform/manifest.mf
@@ -1,4 +1,4 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.libs.jna.platform/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/jna/platform/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.15
+OpenIDE-Module-Specification-Version: 1.16
diff --git a/libs.jna.platform/nbproject/project.xml b/libs.jna.platform/nbproject/project.xml
index 0e3792e..bc04a1f 100644
--- a/libs.jna.platform/nbproject/project.xml
+++ b/libs.jna.platform/nbproject/project.xml
@@ -35,6 +35,7 @@
</module-dependencies>
<friend-packages>
<friend>org.netbeans.core.nativeaccess</friend>
+ <friend>org.netbeans.core.network</friend>
<friend>org.netbeans.libs.jsch.agentproxy</friend>
<package>com.sun.jna.platform</package>
<package>com.sun.jna.platform.dnd</package>
diff --git a/nbbuild/build.properties b/nbbuild/build.properties
index a0386dc..5701f4c 100644
--- a/nbbuild/build.properties
+++ b/nbbuild/build.properties
@@ -188,6 +188,7 @@ config.javadoc.devel=\
config.javadoc.friend=\
o.n.bootstrap,\
o.n.core,\
+ core.network,\
code.analysis,\
core.startup,\
classfile,\
diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml
index 6cfb654..db87cae 100644
--- a/nbbuild/javadoctools/links.xml
+++ b/nbbuild/javadoctools/links.xml
@@ -229,3 +229,4 @@
<link href="${javadoc.docs.org-netbeans-modules-xml-text}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-text"/>
<link href="${javadoc.docs.org-netbeans-modules-xml-lexer}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-lexer"/>
<link href="${javadoc.docs.org-netbeans-modules-xml-text}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-text"/>
+<link href="${javadoc.docs.org-netbeans-core-network}" offline="true" packagelistloc="${netbeans.javadoc.dir}\org-netbeans-core-network"/>
diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml
index 11882c4..bd0edd8 100644
--- a/nbbuild/javadoctools/properties.xml
+++ b/nbbuild/javadoctools/properties.xml
@@ -227,3 +227,4 @@
<property name="javadoc.docs.org-netbeans-modules-xml-text" value="${javadoc.web.root}/org-netbeans-modules-xml-text"/>
<property name="javadoc.docs.org-netbeans-modules-xml-lexer" value="${javadoc.web.root}/org-netbeans-modules-xml-lexer"/>
<property name="javadoc.docs.org-netbeans-modules-xml-text" value="${javadoc.web.root}/org-netbeans-modules-xml-text"/>
+<property name="javadoc.docs.org-netbeans-core-network" value="${javadoc.web.root}/org-netbeans-core-network"/>
diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml
index b58fb8a..72e11a4 100644
--- a/nbbuild/javadoctools/replaces.xml
+++ b/nbbuild/javadoctools/replaces.xml
@@ -227,3 +227,4 @@
<replacefilter token="@org-netbeans-modules-xml-text@" value="${javadoc.docs.org-netbeans-modules-xml-text}"/>
<replacefilter token="@org-netbeans-modules-xml-lexer@" value="${javadoc.docs.org-netbeans-modules-xml-lexer}"/>
<replacefilter token="@org-netbeans-modules-xml-text@" value="${javadoc.docs.org-netbeans-modules-xml-text}"/>
+<replacefilter token="@org-netbeans-core-network@" value="${javadoc.docs.org-netbeans-core-network}"/>
--
To stop receiving notification emails like this one, please contact
['"commits@netbeans.apache.org" <co...@netbeans.apache.org>'].