diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/browser/HtmlBrowserPanel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/browser/HtmlBrowserPanel.java index 907fb34d6..3b2db09b3 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/browser/HtmlBrowserPanel.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/browser/HtmlBrowserPanel.java @@ -178,7 +178,7 @@ private void put(URL url, List where) { if (url != null) { if (where.isEmpty()) { where.add(0, url); - } else if (!where.get(0).toString().equals(url.toString())) { + } else if (!UrlUtils.equalUrls(where.get(0), url)) { where.add(0, url); } } diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/MissingALACAttributePanel.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/MissingALACAttributePanel.java index 23c65380c..2aa79a18d 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/MissingALACAttributePanel.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/MissingALACAttributePanel.java @@ -67,8 +67,8 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/security/manifest.html#app_library @@ -151,7 +151,7 @@ public void hyperlinkUpdate(HyperlinkEvent e) { } public static void main(String[] args) throws MalformedURLException { - Set s = new HashSet<>(); + List s = new ArrayList<>(); s.add(new URL("http:/blah.com/blah")); s.add(new URL("http:/blah.com/blah/blah")); MissingALACAttributePanel w = new MissingALACAttributePanel(null, "HelloWorld", "http://nbblah.url", UrlUtils.setOfUrlsToHtmlList(s)); diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogs.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogs.java index 59ce9fdcc..b3718f20a 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogs.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogs.java @@ -58,7 +58,7 @@ import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.Set; +import java.util.List; import java.util.concurrent.Semaphore; /** @@ -221,7 +221,7 @@ public static NamePassword showAuthenticationPrompt(String host, int port, Strin return (NamePassword) response; } - public static boolean showMissingALACAttributePanel(JNLPFile file, URL codeBase, Set remoteUrls) { + public static boolean showMissingALACAttributePanel(JNLPFile file, URL codeBase, List remoteUrls) { SecurityDialogMessage message = new SecurityDialogMessage(file); message.dialogType = DialogType.MISSING_ALACA; @@ -242,7 +242,7 @@ public static boolean showMissingALACAttributePanel(JNLPFile file, URL codeBase, return selectedValue.toBoolean(); } - public static boolean showMatchingALACAttributePanel(JNLPFile file, URL documentBase, Set remoteUrls) { + public static boolean showMatchingALACAttributePanel(JNLPFile file, URL documentBase, List remoteUrls) { SecurityDialogMessage message = new SecurityDialogMessage(file); message.dialogType = DialogType.MATCHING_ALACA; diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/manifest/ManifestAttributesChecker.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/manifest/ManifestAttributesChecker.java index 6dfed8d12..66d068e97 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/manifest/ManifestAttributesChecker.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/manifest/ManifestAttributesChecker.java @@ -52,6 +52,7 @@ import net.sourceforge.jnlp.runtime.classloader.JNLPClassLoader.SigningState; import net.sourceforge.jnlp.runtime.classloader.SecurityDelegate; import net.sourceforge.jnlp.util.ClasspathMatcher.ClasspathMatchers; +import net.sourceforge.jnlp.util.UrlKey; import net.sourceforge.jnlp.util.UrlUtils; import java.net.MalformedURLException; @@ -77,7 +78,7 @@ public class ManifestAttributesChecker { private final SecurityDelegate securityDelegate; public ManifestAttributesChecker(final SecurityDesc security, final JNLPFile file, - final SigningState signing, final SecurityDelegate securityDelegate) { + final SigningState signing, final SecurityDelegate securityDelegate) { this.security = security; this.file = file; this.signing = signing; @@ -122,7 +123,7 @@ public void checkAll() throws LaunchException { } if (attributesCheck.contains(MANIFEST_ATTRIBUTES_CHECK.ALAC) || - attributesCheck.contains(MANIFEST_ATTRIBUTES_CHECK.ALL)) { + attributesCheck.contains(MANIFEST_ATTRIBUTES_CHECK.ALL)) { checkApplicationLibraryAllowableCodebaseAttribute(); } else { LOG.warn("check on {} skipped because property of deployment.manifest.attributes.check was not set to ALL or includes {} in the combination of options", "Application Library Allowable Codebase", "ALAC"); @@ -142,7 +143,7 @@ public static List getAttributesCheck() { final List configs = JNLPRuntime.getConfiguration().getPropertyAsList(ConfigurationConstants.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK); List manifestAttributesCheckList = new ArrayList<>(); for (String attribute : configs) { - for (MANIFEST_ATTRIBUTES_CHECK manifestAttribute : MANIFEST_ATTRIBUTES_CHECK.values()) { + for (MANIFEST_ATTRIBUTES_CHECK manifestAttribute : MANIFEST_ATTRIBUTES_CHECK.values()) { if (manifestAttribute.toString().equals(attribute)) { manifestAttributesCheckList.add(manifestAttribute); } @@ -331,12 +332,12 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx final URL codebase = file.getCodeBase(); //cases - final Map> usedUrls = new HashMap<>(); + final Map> usedUrls = new HashMap<>(); final URL sourceLocation = file.getSourceLocation(); final ResourcesDesc[] resourcesDescs = file.getResourcesDescs(); if ((sourceLocation != null) && !FILE_PROTOCOL.equals(sourceLocation.getProtocol())) { final URL urlWithoutFileName = UrlUtils.removeFileName(sourceLocation); - usedUrls.computeIfAbsent(urlWithoutFileName.toString(), url -> new HashSet<>()).add(sourceLocation); + usedUrls.computeIfAbsent(new UrlKey(urlWithoutFileName), url -> new HashSet<>()).add(new UrlKey(sourceLocation)); } for (ResourcesDesc resourcesDesc : resourcesDescs) { ExtensionDesc[] ex = resourcesDesc.getExtensions(); @@ -344,7 +345,7 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx for (ExtensionDesc extensionDesc : ex) { if (extensionDesc != null) { final URL urlWithoutFileName = UrlUtils.removeFileName(extensionDesc.getLocation()); - usedUrls.computeIfAbsent(urlWithoutFileName.toString(), url -> new HashSet<>()).add(extensionDesc.getLocation()); + usedUrls.computeIfAbsent(new UrlKey(urlWithoutFileName), url -> new HashSet<>()).add(new UrlKey(extensionDesc.getLocation())); } } } @@ -353,7 +354,7 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx for (JARDesc jarDesc : jars) { if (jarDesc != null) { final URL urlWithoutFileName = UrlUtils.removeFileName(jarDesc.getLocation()); - usedUrls.computeIfAbsent(urlWithoutFileName.toString(), url -> new HashSet<>()).add(jarDesc.getLocation()); + usedUrls.computeIfAbsent(new UrlKey(urlWithoutFileName), url -> new HashSet<>()).add(new UrlKey(jarDesc.getLocation())); } } } @@ -365,21 +366,17 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx LOG.debug("The application is not using any url resources, skipping Application-Library-Allowable-Codebase Attribute check."); return; } - final Set notOkUrls = new HashSet<>(); + final Set notOkUrls = new HashSet<>(); final boolean skipResourcesFromFileSystem = Boolean.parseBoolean(JNLPRuntime.getConfiguration().getProperty(ConfigurationConstants.KEY_ASSUME_FILE_STEM_IN_CODEBASE)); - for (String urlString : usedUrls.keySet()) { - try { - final URL u = new URL(urlString); - if (UrlUtils.urlRelativeTo(u, codebase)) { - LOG.debug("OK - '{}' is from codebase '{}'.", u, codebase); - } else if (skipResourcesFromFileSystem && FILE_PROTOCOL.equals(u.getProtocol())) { - LOG.debug("OK - '{}' is from file system", u); - } else { - notOkUrls.add(u); - LOG.warn("Warning! '{}' is NOT from codebase '{}'.", u, codebase); - } - } catch (MalformedURLException mue) { - LOG.debug("Malformed URL checkApplicationLibraryAllowableCodebaseAttribute '{}'.", urlString); + for (UrlKey urlKey : usedUrls.keySet()) { + final URL u = urlKey.getUrl(); + if (UrlUtils.urlRelativeTo(u, codebase)) { + LOG.debug("OK - '{}' is from codebase '{}'.", u, codebase); + } else if (skipResourcesFromFileSystem && FILE_PROTOCOL.equals(u.getProtocol())) { + LOG.debug("OK - '{}' is from file system", u); + } else { + notOkUrls.add(urlKey); + LOG.warn("Warning! '{}' is NOT from codebase '{}'.", u, codebase); } } if (notOkUrls.isEmpty()) { @@ -396,9 +393,11 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx att = null; } - final Set notOkResources = notOkUrls.stream() - .flatMap(notOk -> usedUrls.get(notOk.toString()).stream()) - .collect(Collectors.toSet()); + final List notOkResources = notOkUrls.stream() + .flatMap(notOk -> usedUrls.get(notOk).stream()) + .collect(Collectors.toSet()).stream() + .map(UrlKey::getUrl) + .collect(Collectors.toList()); notOkResources.forEach(url -> LOG.warn("The resource '{}' is not from codebase '{}'", url, codebase)); @@ -411,16 +410,12 @@ private void checkApplicationLibraryAllowableCodebaseAttribute() throws LaunchEx return; } } else { - for (String foundUrlString : usedUrls.keySet()) { - try { - URL foundUrl = new URL(foundUrlString); - if (!att.matches(foundUrl)) { - throw new LaunchException("The resources " + usedUrls.get(foundUrlString) + " do not match the location in Application-Library-Allowable-Codebase Attribute " + att + ". Blocking the application from running."); - } else { - LOG.debug("The resources from {} do match the location in Application-Library-Allowable-Codebase Attribute {}. Continuing.", foundUrl, att); - } - } catch (MalformedURLException mue) { - throw new LaunchException("Malformed URL " + foundUrlString + ". Resources do not match the location in Application-Library-Allowable-Codebase Attribute " + att + ". Blocking the application from running."); + for (UrlKey foundUrlKey : usedUrls.keySet()) { + URL foundUrl = foundUrlKey.getUrl(); + if (!att.matches(foundUrl)) { + throw new LaunchException("The resources " + usedUrls.get(foundUrlKey) + " do not match the location in Application-Library-Allowable-Codebase Attribute " + att + ". Blocking the application from running."); + } else { + LOG.debug("The resources from {} do match the location in Application-Library-Allowable-Codebase Attribute {}. Continuing.", foundUrl, att); } } } @@ -446,7 +441,7 @@ static URL stripDocbase(URL documentBase) { if (i <= 8 || i >= s.length()) { return documentBase; } - s = s.substring(0, i+1); + s = s.substring(0, i + 1); try { documentBase = new URL(s); } catch (MalformedURLException ex) { diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/ResourceTracker.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/ResourceTracker.java index f68c99ced..baa166966 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/ResourceTracker.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/ResourceTracker.java @@ -24,6 +24,7 @@ import net.sourceforge.jnlp.cache.CacheUtil; import net.sourceforge.jnlp.config.ConfigurationConstants; import net.sourceforge.jnlp.runtime.JNLPRuntime; +import net.sourceforge.jnlp.util.UrlKey; import net.sourceforge.jnlp.util.UrlUtils; import javax.jnlp.DownloadServiceListener; @@ -97,7 +98,7 @@ public class ResourceTracker { /** * the resources known about by this resource tracker */ - private final Map resources = new HashMap<>(); + private final Map resources = new HashMap<>(); /** * whether to download parts before requested @@ -166,10 +167,11 @@ public void addResource(URL location, final VersionString version, final UpdateP */ private boolean addToResources(Resource resource) { synchronized (resources) { - final Resource existingResource = resources.get(resource.getLocation().toString()); + final UrlKey urlKey = new UrlKey(resource.getLocation()); + final Resource existingResource = resources.get(urlKey); if (existingResource == null) { - resources.put(resource.getLocation().toString(), resource); + resources.put(urlKey, resource); return true; } @@ -203,7 +205,7 @@ private void startDownloadingIfPrefetch(Resource resource) { public void removeResource(URL location) { synchronized (resources) { Resource resource = getResource(location); - resources.remove(resource.getLocation().toString()); + resources.remove(new UrlKey(resource.getLocation())); } } @@ -362,7 +364,7 @@ private Resource[] getResources(URL[] urls) { private Resource getResource(URL location) { final URL normalizedLocation = normalizeUrlQuietly(location); synchronized (resources) { - final Resource result = resources.get(normalizedLocation.toString()); + final Resource result = resources.get(new UrlKey(normalizedLocation)); if (result == null) { throw new IllegalResourceDescriptorException("Location " + location + " does not specify a resource being tracked."); } diff --git a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/cache/CacheKey.java b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/cache/CacheKey.java index 791af725c..0515fdb8a 100644 --- a/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/cache/CacheKey.java +++ b/core/src/main/java/net/adoptopenjdk/icedteaweb/resources/cache/CacheKey.java @@ -1,6 +1,7 @@ package net.adoptopenjdk.icedteaweb.resources.cache; import net.adoptopenjdk.icedteaweb.jnlp.version.VersionId; +import net.sourceforge.jnlp.util.UrlKey; import java.net.URL; import java.util.Objects; @@ -10,12 +11,12 @@ class CacheKey { private final URL location; - private final String locationString; + private final UrlKey urlKey; private final VersionId version; public CacheKey(final URL location, final VersionId version) { this.location = requireNonNull(location, "location"); - this.locationString = location.toString(); + this.urlKey = new UrlKey(location); this.version = version; } @@ -30,7 +31,7 @@ public VersionId getVersion() { @Override public String toString() { return "CacheKey{" + - "location=" + locationString + + "location=" + location + ", version=" + version + '}'; } @@ -40,15 +41,15 @@ public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CacheKey cacheKey = (CacheKey) o; - return Objects.equals(locationString, cacheKey.locationString) && Objects.equals(version, cacheKey.version); + return Objects.equals(urlKey, cacheKey.urlKey) && Objects.equals(version, cacheKey.version); } @Override public int hashCode() { - return Objects.hash(locationString, version); + return Objects.hash(urlKey, version); } public boolean matches(URL resource) { - return resource != null && locationString.equals(resource.toString()); + return resource != null && urlKey.equals(new UrlKey(resource)); } } diff --git a/core/src/main/java/net/sourceforge/jnlp/Launcher.java b/core/src/main/java/net/sourceforge/jnlp/Launcher.java index d0ad7978b..3ffc387df 100644 --- a/core/src/main/java/net/sourceforge/jnlp/Launcher.java +++ b/core/src/main/java/net/sourceforge/jnlp/Launcher.java @@ -35,6 +35,7 @@ import net.sourceforge.jnlp.runtime.classloader.JNLPClassLoader; import net.sourceforge.jnlp.services.InstanceExistsException; import net.sourceforge.jnlp.services.ServiceUtil; +import net.sourceforge.jnlp.util.UrlUtils; import javax.imageio.ImageIO; import javax.swing.text.html.parser.ParserDelegator; @@ -309,7 +310,7 @@ private JNLPFile fromUrl(URL location) throws LaunchException { } if (!isLocal && haveHref) { //this is case when remote file have href to different file - if (file.getSourceLocation() == null || !location.toString().equals(file.getSourceLocation().toString())) { + if (!UrlUtils.equalUrls(location, file.getSourceLocation())) { //mark local true, so the following condition will be true and //new jnlp file will be downloaded isLocal = true; diff --git a/core/src/main/java/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java b/core/src/main/java/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java index 4fd9db239..68c5ba95f 100644 --- a/core/src/main/java/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java +++ b/core/src/main/java/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java @@ -40,6 +40,7 @@ import net.adoptopenjdk.icedteaweb.logging.Logger; import net.adoptopenjdk.icedteaweb.logging.LoggerFactory; import net.sourceforge.jnlp.util.JarFile; +import net.sourceforge.jnlp.util.UrlKey; import net.sourceforge.jnlp.util.UrlUtils; import sun.net.www.protocol.jar.URLJarFile; import sun.net.www.protocol.jar.URLJarFileCallBack; @@ -73,24 +74,24 @@ public synchronized static CachedJarFileCallback getInstance() { } /* our managed cache */ - private final Map mapping; + private final Map mapping; private CachedJarFileCallback() { - mapping = new ConcurrentHashMap(); + mapping = new ConcurrentHashMap<>(); } public void addMapping(URL remoteUrl, URL localUrl) { LOG.debug("CachedJarFileCallback.addMapping : {} -> {} ", remoteUrl, localUrl); - mapping.put(remoteUrl.toString(), localUrl); + mapping.put(new UrlKey(remoteUrl), localUrl); } @Override public java.util.jar.JarFile retrieve(URL url) throws IOException { - URL localUrl = mapping.get(url.toString()); + URL localUrl = mapping.get(new UrlKey(url)); if (localUrl == null) { if (url.getRef() != null) { url = new URL(url.toString().substring(0, url.toString().lastIndexOf(url.getRef()) - 1)); - localUrl = mapping.get(url.toString()); + localUrl = mapping.get(new UrlKey(url)); } } diff --git a/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/JNLPClassLoader.java b/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/JNLPClassLoader.java index 7afd40e8a..dd3056a1b 100644 --- a/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/JNLPClassLoader.java +++ b/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/JNLPClassLoader.java @@ -52,6 +52,7 @@ import net.sourceforge.jnlp.security.JNLPAppVerifier; import net.sourceforge.jnlp.tools.JarCertVerifier; import net.sourceforge.jnlp.util.JarFile; +import net.sourceforge.jnlp.util.UrlKey; import net.sourceforge.jnlp.util.UrlUtils; import java.io.File; @@ -93,6 +94,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.jar.Attributes; import java.util.jar.JarEntry; +import java.util.stream.Collectors; import java.util.stream.Stream; import static net.adoptopenjdk.icedteaweb.i18n.Translator.R; @@ -256,10 +258,10 @@ public enum SigningState { * classloading threads. See loadClass(String) and * CodebaseClassLoader.findClassNonRecursive(String). */ - final Map jarLocationSecurityMap = Collections.synchronizedMap(new HashMap<>()); + final Map jarLocationSecurityMap = Collections.synchronizedMap(new HashMap<>()); /*Set to prevent once tried-to-get resources to be tried again*/ - private final Set alreadyTried = Collections.synchronizedSet(new HashSet<>()); + private final Set alreadyTried = Collections.synchronizedSet(new HashSet<>()); /** * Loader for codebase (which is a path, rather than a file) @@ -490,7 +492,7 @@ private static JNLPClassLoader getInstance(JNLPFile file, UpdatePolicy policy, S // for this codebase/jnlp yet. Create one. if (baseLoader == null || (file.isApplication() - && (file.getFileLocation() == null || !baseLoader.getJNLPFile().getFileLocation().toString().equals(file.getFileLocation().toString())))) { + && (file.getFileLocation() == null || !UrlUtils.equalUrls(baseLoader.getJNLPFile().getFileLocation(), file.getFileLocation())))) { loader = createInstance(file, policy, mainName, enableCodeBase); } else { @@ -536,7 +538,7 @@ private static JNLPClassLoader getInstance(final URL location, final String uniq synchronized (getUniqueKeyLock(uniqueKey)) { loader = uniqueKeyToLoader.get(uniqueKey); - if (loader == null || loader.getJNLPFile().getFileLocation() == null || !location.toString().equals(loader.getJNLPFile().getFileLocation().toString())) { + if (loader == null || loader.getJNLPFile().getFileLocation() == null || !UrlUtils.equalUrls(location, loader.getJNLPFile().getFileLocation())) { final JNLPFile jnlpFile = new JNLPFileFactory().create(location, uniqueKey, version, settings, policy); loader = getInstance(jnlpFile, policy, mainName, enableCodeBase); @@ -822,7 +824,7 @@ private void initializeResources() throws LaunchException { for (JARDesc jarDesc : validJars) { final URL codebase = getJnlpFileCodebase(); final SecurityDesc jarSecurity = securityDelegate.getCodebaseSecurityDesc(jarDesc, codebase); - jarLocationSecurityMap.put(jarDesc.getLocation().toString(), jarSecurity); + jarLocationSecurityMap.put(new UrlKey(jarDesc.getLocation()), jarSecurity); } activateJars(initialJars); @@ -1254,7 +1256,7 @@ private Void doActivateJars(List jars) { CachedJarFileCallback.getInstance().addMapping(fakeRemote, fileURL); addURL(fakeRemote); - jarLocationSecurityMap.put(fakeRemote.toString(), jarSecurity); + jarLocationSecurityMap.put(new UrlKey(fakeRemote), jarSecurity); } catch (MalformedURLException mfue) { LOG.error("Unable to add extracted nested jar to classpath", mfue); @@ -1566,7 +1568,7 @@ private void addNewJar(final JARDesc desc, UpdatePolicy updatePolicy) { final SecurityDesc security = securityDelegate.getJarPermissions(file.getCodeBase()); - jarLocationSecurityMap.put(remoteURL.toString(), security); + jarLocationSecurityMap.put(new UrlKey(remoteURL), security); return null; }); @@ -1706,7 +1708,7 @@ public Enumeration findResources(String name) throws IOException { * Find the resources in this, the parent, or the extension class loaders. */ private Enumeration findResourcesBySearching(String name) throws IOException { - Set lresources = new HashSet<>(); + List lresources = new ArrayList<>(); Enumeration e = null; for (JNLPClassLoader loader : loaders) { @@ -1750,7 +1752,15 @@ public Collection run() { } } - return Collections.enumeration(lresources); + return Collections.enumeration(removeDuplicates(lresources)); + } + + private Collection removeDuplicates(List lresources) { + Set set = new LinkedHashSet<>(); + for (final URL lresource : lresources) { + set.add(new UrlKey(lresource)); + } + return set.stream().map(UrlKey::getUrl).collect(Collectors.toList()); } /** @@ -1846,16 +1856,17 @@ public SecurityDesc getSecurity() { * @return The SecurityDescriptor for that source */ private SecurityDesc getCodeSourceSecurity(URL source) { - SecurityDesc sec = jarLocationSecurityMap.get(source.toString()); + final UrlKey urlKey = new UrlKey(source); + SecurityDesc sec = jarLocationSecurityMap.get(urlKey); synchronized (alreadyTried) { - if (sec == null && !alreadyTried.contains(source)) { - alreadyTried.add(source); + if (sec == null && !alreadyTried.contains(urlKey)) { + alreadyTried.add(urlKey); //try to load the jar which is requesting the permissions, but was NOT downloaded by standard way LOG.info("Application is trying to get permissions for {}, which was not added by standard way. Trying to download and verify!", source.toString()); try { JARDesc des = new JARDesc(source, null, null, false, false, false, false); addNewJar(des); - sec = jarLocationSecurityMap.get(source.toString()); + sec = jarLocationSecurityMap.get(urlKey); } catch (Throwable t) { LOG.error("Error while getting security", t); sec = null; @@ -1900,7 +1911,7 @@ private void merge(JNLPClassLoader extLoader) { // security descriptors synchronized (jarLocationSecurityMap) { - for (String key : extLoader.jarLocationSecurityMap.keySet()) { + for (UrlKey key : extLoader.jarLocationSecurityMap.keySet()) { jarLocationSecurityMap.put(key, extLoader.jarLocationSecurityMap.get(key)); } } @@ -2102,13 +2113,9 @@ AccessControlContext getAccessControlContextForClassLoading() { // Permissions for all remote hosting urls synchronized (jarLocationSecurityMap) { - for (String urlString : jarLocationSecurityMap.keySet()) { - try { - URL u = new URL(urlString); - permissions.add(new SocketPermission(UrlUtils.getHostAndPort(u), "connect, accept")); - } catch (MalformedURLException mue) { - LOG.debug("Could not add SocketPermission for url : {}", urlString); - } + for (UrlKey urlkey : jarLocationSecurityMap.keySet()) { + URL u = urlkey.getUrl(); + permissions.add(new SocketPermission(UrlUtils.getHostAndPort(u), "connect, accept")); } } diff --git a/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/LocateJnlpClassLoader.java b/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/LocateJnlpClassLoader.java index 52e4962f8..3dc11d6f1 100644 --- a/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/LocateJnlpClassLoader.java +++ b/core/src/main/java/net/sourceforge/jnlp/runtime/classloader/LocateJnlpClassLoader.java @@ -38,6 +38,7 @@ import net.adoptopenjdk.icedteaweb.jnlp.element.resource.ResourcesDesc; import net.adoptopenjdk.icedteaweb.jnlp.version.VersionString; import net.sourceforge.jnlp.JNLPFile; +import net.sourceforge.jnlp.util.UrlUtils; import java.net.URL; @@ -60,7 +61,7 @@ static JNLPClassLoader getLoaderByJnlpFile(final JNLPClassLoader rootClassLoader if (urlToJnlpFile == null) { urlToJnlpFile = rootClassLoader.getJNLPFile().getFileLocation(); - } else if (file.getFileLocation().toString().equals(urlToJnlpFile.toString())) { + } else if (UrlUtils.equalUrls(file.getFileLocation(), urlToJnlpFile)) { return rootClassLoader; } @@ -90,8 +91,7 @@ static JNLPClassLoader getLoaderByResourceUrl(final JNLPClassLoader rootClassLoa ResourcesDesc resources = loader.getJNLPFile().getResources(); for (JARDesc eachJar : resources.getJARs()) { - if (eachJar.getLocation() != null && - ref.toString().equals(eachJar.getLocation().toString()) && + if (UrlUtils.equalUrls(ref, eachJar.getLocation()) && (resourceVersion == null || resourceVersion.equals(eachJar.getVersion()))) return loader; } diff --git a/core/src/main/java/net/sourceforge/jnlp/util/UrlKey.java b/core/src/main/java/net/sourceforge/jnlp/util/UrlKey.java new file mode 100644 index 000000000..c718aeed2 --- /dev/null +++ b/core/src/main/java/net/sourceforge/jnlp/util/UrlKey.java @@ -0,0 +1,68 @@ +package net.sourceforge.jnlp.util; + +import net.adoptopenjdk.icedteaweb.StringUtils; + +import java.net.URL; +import java.util.Locale; +import java.util.Objects; + +public class UrlKey { + private final URL url; + private final String protocol; + private final String host; + private final String file; + private final int port; + private final String ref; + + public UrlKey(final URL url) { + this.url = url; + this.protocol = url.getProtocol() != null ? url.getProtocol().toLowerCase(Locale.ENGLISH) : null; + this.host = url.getHost() != null ? url.getHost().toLowerCase(Locale.ENGLISH) : null; + this.port = url.getPort(); + this.file = url.getFile(); + this.ref = url.getRef(); + } + + public URL getUrl() { + return url; + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof UrlKey)) { + return false; + } + UrlKey other = ((UrlKey) obj); + return Objects.equals(protocol, other.protocol) && + Objects.equals(host, other.host) && + samePort(port, other.port) && + Objects.equals(file, other.file) && + Objects.equals(ref, other.ref); + } + + private boolean samePort(int port, int other) { + if (port == other) { + return true; + } + final int defaultPort = getDefaultPort(); + return (port == defaultPort || port == -1) && (other == defaultPort || other == -1); + } + + private int getDefaultPort() { + if ("https".equalsIgnoreCase(protocol)) { + return 443; + } + if ("http".equalsIgnoreCase(protocol)) { + return 80; + } + if ("ftp".equalsIgnoreCase(protocol)) { + return 21; + } + return -1; + } + + @Override + public int hashCode() { + return Objects.hash(protocol, host, file, ref); + } +} diff --git a/core/src/main/java/net/sourceforge/jnlp/util/UrlUtils.java b/core/src/main/java/net/sourceforge/jnlp/util/UrlUtils.java index 0796c3240..3c85e47da 100644 --- a/core/src/main/java/net/sourceforge/jnlp/util/UrlUtils.java +++ b/core/src/main/java/net/sourceforge/jnlp/util/UrlUtils.java @@ -60,6 +60,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collection; +import java.util.List; import java.util.Objects; import static java.nio.charset.StandardCharsets.UTF_8; @@ -226,7 +227,7 @@ public static URL removeFileName(final URL src) { * @param remoteUrls list of urls * @return String containing html item list of those urls */ - public static String setOfUrlsToHtmlList(final Collection remoteUrls) { + public static String setOfUrlsToHtmlList(final List remoteUrls) { return setOfUrlsToHtmlList(remoteUrls, 4); } @@ -592,4 +593,14 @@ public static Socket createSocketFromUrl(final URL url) throws IOException { return s; } + + public static boolean equalUrls(URL url1, URL url2) { + if (url1 == url2) { + return true; + } + if (url1 == null || url2 == null) { + return false; + } + return new UrlKey(url1).equals(new UrlKey(url2)); + } } diff --git a/core/src/test/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogsTest.java b/core/src/test/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogsTest.java index d72e491a7..57298be1b 100644 --- a/core/src/test/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogsTest.java +++ b/core/src/test/java/net/adoptopenjdk/icedteaweb/client/parts/dialogs/security/SecurityDialogsTest.java @@ -66,7 +66,7 @@ import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; +import java.util.ArrayList; @Ignore public class SecurityDialogsTest extends NoStdOutErrTest { @@ -312,9 +312,9 @@ private void testAllDialogs(ExpectedResults r) throws MalformedURLException { //Assert.assertEquals(r.ea, r5); NamePassword r6 = SecurityDialogs.showAuthenticationPrompt(null, 123456, null, null); Assert.assertEquals(r.np, r6); - boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new HashSet()); + boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new ArrayList<>()); Assert.assertEquals(r.b, r7); - boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new HashSet()); + boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new ArrayList<>()); Assert.assertEquals(r.b, r8); boolean r9 = SecurityDialogs.showMissingPermissionsAttributeDialogue(crtJnlpF()); Assert.assertEquals(r.b, r9); @@ -336,9 +336,9 @@ private void testAllDialogsNullResults() throws MalformedURLException { //Assert.assertEquals(r.ea, r5); NamePassword r6 = SecurityDialogs.showAuthenticationPrompt(null, 123456, null, null); Assert.assertEquals(null, r6); - boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new HashSet()); + boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new ArrayList<>()); Assert.assertEquals(false, r7); - boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new HashSet()); + boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new ArrayList<>()); Assert.assertEquals(false, r8); boolean r9 = SecurityDialogs.showMissingPermissionsAttributeDialogue(crtJnlpF()); Assert.assertEquals(false, r9); @@ -441,7 +441,7 @@ private void countNPES(int allowedRuns) throws MalformedURLException { } try { metcounter++; - SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new HashSet()); + SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new ArrayList<>()); } catch (NullPointerException ex) { npecounter++; } @@ -628,9 +628,9 @@ public void testUnsignedDialogsNotHeadlessPrompt() throws Exception { + ".* \\Q" + urlstr + "\\E "; private void runRememeberableClasses(ExpectedResults r) throws MalformedURLException { - boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new HashSet()); + boolean r7 = SecurityDialogs.showMissingALACAttributePanel(crtJnlpF(), null, new ArrayList<>()); Assert.assertEquals(r.b, r7); - boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new HashSet()); + boolean r8 = SecurityDialogs.showMatchingALACAttributePanel(crtJnlpF(), url, new ArrayList<>()); Assert.assertEquals(r.b, r8); boolean r9 = testUnsignedBehaviour(); Assert.assertEquals(r.b, r9); diff --git a/core/src/test/java/net/sourceforge/jnlp/util/UrlKeyTest.java b/core/src/test/java/net/sourceforge/jnlp/util/UrlKeyTest.java new file mode 100644 index 000000000..9dd9c711f --- /dev/null +++ b/core/src/test/java/net/sourceforge/jnlp/util/UrlKeyTest.java @@ -0,0 +1,95 @@ +package net.sourceforge.jnlp.util; + +import junit.framework.TestCase; + +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class UrlKeyTest extends TestCase { + + public void testEqualsHttp() throws Exception { + URL url1 = new URL("http://www.example.com:80/index.html?a=b#3"); + List urlList = Arrays.asList( + new URL("http://www.example.com/index.html?a=b#3"), + new URL("http://www.Example.Com/index.html?a=b#3"), + new URL("Http://www.example.com/index.html?a=b#3"), + new URL("http://www.example.com:80/index.html?a=b#3")); + + for (final URL url : urlList) { + UrlKey key1 = new UrlKey(url1); + UrlKey key2 = new UrlKey(url); + + assertEquals(key1.hashCode(), key2.hashCode()); + assertEquals(key1, key2); + assertEquals(key2, key1); + } + } + + public void testNotEqualsHttp() throws Exception { + URL url1 = new URL("http://www.example.com:80/index.html?a=b#3"); + List urlList = Arrays.asList( + new URL("http://www.example.org:80/index.html?a=b#3"), + new URL("http://www.example.com:80/index2.html?a=b#3"), + new URL("http://www.example.com:80/index.html#3"), + new URL("https://www.example.com:80/index.html?a=b#3"), + new URL("http://www.example.com:80/index.html?a=c#3"), + new URL("http://www.example.com:80/index.html?a=b#4"), + new URL("http://www.example.com:80/index.html?a=b"), + new URL("http://www.example.com:80/#3"), + new URL("http://:80/index.html?a=b#3") + ); + + for (final URL url : urlList) { + UrlKey key1 = new UrlKey(url1); + UrlKey key2 = new UrlKey(url); + + assertNotEquals(key1.hashCode(), key2.hashCode()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); + } + } + + public void testNotEqualsDifferentPorts() throws Exception { + URL url1 = new URL("http://www.example.com:80/index.html?a=b#3"); + URL url = new URL("http://www.example.com:81/index.html?a=b#3"); + + UrlKey key1 = new UrlKey(url1); + UrlKey key2 = new UrlKey(url); + + assertEquals(key1.hashCode(), key2.hashCode()); + assertNotEquals(key1, key2); + assertNotEquals(key2, key1); + } + + private void assertNotEquals(Object o1, Object o2) { + assertFalse(Objects.equals(o1, o2)); + } + + public void testEqualsHttps() throws Exception { + URL url1 = new URL("https://www.example.com:443/index.html"); + URL url2 = new URL("https://www.example.com/index.html"); + + UrlKey key1 = new UrlKey(url1); + UrlKey key2 = new UrlKey(url2); + + assertEquals(key1.hashCode(), key2.hashCode()); + assertEquals(key1, key2); + assertEquals(key2, key1); + } + + public void testEqualsFtp() throws Exception { + URL url1 = new URL("ftp://www.example.com:21/index.html"); + URL url2 = new URL("ftp://www.example.com/index.html"); + + UrlKey key1 = new UrlKey(url1); + UrlKey key2 = new UrlKey(url2); + + assertEquals(key1.hashCode(), key2.hashCode()); + assertEquals(key1, key2); + assertEquals(key2, key1); + } + +} +