aboutsummaryrefslogtreecommitdiffstats
path: root/netx
diff options
context:
space:
mode:
Diffstat (limited to 'netx')
-rw-r--r--netx/javaws.1123
-rw-r--r--netx/javax/jnlp/BasicService.java12
-rw-r--r--netx/javax/jnlp/ClipboardService.java10
-rw-r--r--netx/javax/jnlp/DownloadService.java24
-rw-r--r--netx/javax/jnlp/DownloadServiceListener.java12
-rw-r--r--netx/javax/jnlp/ExtendedService.java51
-rw-r--r--netx/javax/jnlp/ExtensionInstallerService.java21
-rw-r--r--netx/javax/jnlp/FileContents.java17
-rw-r--r--netx/javax/jnlp/FileOpenService.java10
-rw-r--r--netx/javax/jnlp/FileSaveService.java10
-rw-r--r--netx/javax/jnlp/JNLPRandomAccessFile.java45
-rw-r--r--netx/javax/jnlp/PersistenceService.java17
-rw-r--r--netx/javax/jnlp/PrintService.java12
-rw-r--r--netx/javax/jnlp/ServiceManager.java53
-rw-r--r--netx/javax/jnlp/ServiceManagerStub.java10
-rw-r--r--netx/javax/jnlp/SingleInstanceListener.java35
-rw-r--r--netx/javax/jnlp/SingleInstanceService.java46
-rw-r--r--netx/javax/jnlp/UnavailableServiceException.java15
-rw-r--r--netx/net/sourceforge/jnlp/AppletDesc.java122
-rw-r--r--netx/net/sourceforge/jnlp/ApplicationDesc.java74
-rw-r--r--netx/net/sourceforge/jnlp/AssociationDesc.java56
-rw-r--r--netx/net/sourceforge/jnlp/ComponentDesc.java40
-rw-r--r--netx/net/sourceforge/jnlp/DefaultLaunchHandler.java112
-rw-r--r--netx/net/sourceforge/jnlp/ExtensionDesc.java143
-rw-r--r--netx/net/sourceforge/jnlp/IconDesc.java135
-rw-r--r--netx/net/sourceforge/jnlp/InformationDesc.java267
-rw-r--r--netx/net/sourceforge/jnlp/InstallerDesc.java52
-rw-r--r--netx/net/sourceforge/jnlp/JARDesc.java141
-rw-r--r--netx/net/sourceforge/jnlp/JNLPFile.java633
-rw-r--r--netx/net/sourceforge/jnlp/JNLPSplashScreen.java99
-rw-r--r--netx/net/sourceforge/jnlp/JREDesc.java165
-rw-r--r--netx/net/sourceforge/jnlp/LaunchException.java188
-rw-r--r--netx/net/sourceforge/jnlp/LaunchHandler.java66
-rw-r--r--netx/net/sourceforge/jnlp/Launcher.java883
-rw-r--r--netx/net/sourceforge/jnlp/MenuDesc.java38
-rw-r--r--netx/net/sourceforge/jnlp/NetxPanel.java168
-rw-r--r--netx/net/sourceforge/jnlp/Node.java146
-rw-r--r--netx/net/sourceforge/jnlp/PackageDesc.java102
-rw-r--r--netx/net/sourceforge/jnlp/ParseException.java92
-rw-r--r--netx/net/sourceforge/jnlp/Parser.java1320
-rw-r--r--netx/net/sourceforge/jnlp/PluginBridge.java253
-rw-r--r--netx/net/sourceforge/jnlp/PropertyDesc.java64
-rw-r--r--netx/net/sourceforge/jnlp/RelatedContentDesc.java93
-rw-r--r--netx/net/sourceforge/jnlp/ResourcesDesc.java230
-rw-r--r--netx/net/sourceforge/jnlp/SecurityDesc.java201
-rw-r--r--netx/net/sourceforge/jnlp/ShortcutDesc.java70
-rw-r--r--netx/net/sourceforge/jnlp/StreamEater.java45
-rw-r--r--netx/net/sourceforge/jnlp/UpdateDesc.java70
-rw-r--r--netx/net/sourceforge/jnlp/Version.java352
-rw-r--r--netx/net/sourceforge/jnlp/cache/CacheEntry.java172
-rw-r--r--netx/net/sourceforge/jnlp/cache/CacheUtil.java450
-rw-r--r--netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java320
-rw-r--r--netx/net/sourceforge/jnlp/cache/DownloadIndicator.java91
-rw-r--r--netx/net/sourceforge/jnlp/cache/Resource.java269
-rw-r--r--netx/net/sourceforge/jnlp/cache/ResourceTracker.java1049
-rw-r--r--netx/net/sourceforge/jnlp/cache/UpdatePolicy.java88
-rw-r--r--netx/net/sourceforge/jnlp/cache/package.html28
-rw-r--r--netx/net/sourceforge/jnlp/event/ApplicationEvent.java55
-rw-r--r--netx/net/sourceforge/jnlp/event/ApplicationListener.java37
-rw-r--r--netx/net/sourceforge/jnlp/event/DownloadEvent.java70
-rw-r--r--netx/net/sourceforge/jnlp/event/DownloadListener.java50
-rw-r--r--netx/net/sourceforge/jnlp/event/package.html28
-rw-r--r--netx/net/sourceforge/jnlp/package.html30
-rw-r--r--netx/net/sourceforge/jnlp/resources/Manifest.mf6
-rw-r--r--netx/net/sourceforge/jnlp/resources/Messages.properties182
-rw-r--r--netx/net/sourceforge/jnlp/resources/about.jnlp20
-rw-r--r--netx/net/sourceforge/jnlp/resources/default.jnlp20
-rw-r--r--netx/net/sourceforge/jnlp/resources/info-small.pngbin0 -> 1157 bytes
-rw-r--r--netx/net/sourceforge/jnlp/resources/install.pngbin0 -> 2324 bytes
-rw-r--r--netx/net/sourceforge/jnlp/resources/netx-icon.pngbin0 -> 555 bytes
-rw-r--r--netx/net/sourceforge/jnlp/resources/warning-small.pngbin0 -> 1123 bytes
-rw-r--r--netx/net/sourceforge/jnlp/resources/warning.pngbin0 -> 1346 bytes
-rw-r--r--netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java68
-rw-r--r--netx/net/sourceforge/jnlp/runtime/AppletAudioClip.java107
-rw-r--r--netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java354
-rw-r--r--netx/net/sourceforge/jnlp/runtime/AppletInstance.java138
-rw-r--r--netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java319
-rw-r--r--netx/net/sourceforge/jnlp/runtime/Boot.java463
-rw-r--r--netx/net/sourceforge/jnlp/runtime/Boot13.java102
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java1198
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPPolicy.java103
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java572
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java541
-rw-r--r--netx/net/sourceforge/jnlp/runtime/package.html29
-rw-r--r--netx/net/sourceforge/jnlp/security/AccessWarningPane.java209
-rw-r--r--netx/net/sourceforge/jnlp/security/AppletWarningPane.java115
-rw-r--r--netx/net/sourceforge/jnlp/security/CertVerifier.java92
-rw-r--r--netx/net/sourceforge/jnlp/security/CertWarningPane.java254
-rw-r--r--netx/net/sourceforge/jnlp/security/CertsInfoPane.java341
-rw-r--r--netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java238
-rw-r--r--netx/net/sourceforge/jnlp/security/MoreInfoPane.java110
-rw-r--r--netx/net/sourceforge/jnlp/security/NotAllSignedWarningPane.java120
-rw-r--r--netx/net/sourceforge/jnlp/security/SecurityDialogPanel.java134
-rw-r--r--netx/net/sourceforge/jnlp/security/SecurityUtil.java285
-rw-r--r--netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java474
-rw-r--r--netx/net/sourceforge/jnlp/security/SingleCertInfoPane.java77
-rw-r--r--netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java314
-rw-r--r--netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java335
-rw-r--r--netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java120
-rw-r--r--netx/net/sourceforge/jnlp/services/ExtendedSingleInstanceService.java49
-rw-r--r--netx/net/sourceforge/jnlp/services/InstanceExistsException.java35
-rw-r--r--netx/net/sourceforge/jnlp/services/ServiceUtil.java293
-rw-r--r--netx/net/sourceforge/jnlp/services/SingleInstanceLock.java194
-rw-r--r--netx/net/sourceforge/jnlp/services/XBasicService.java232
-rw-r--r--netx/net/sourceforge/jnlp/services/XClipboardService.java81
-rw-r--r--netx/net/sourceforge/jnlp/services/XDownloadService.java179
-rw-r--r--netx/net/sourceforge/jnlp/services/XExtendedService.java56
-rw-r--r--netx/net/sourceforge/jnlp/services/XExtensionInstallerService.java121
-rw-r--r--netx/net/sourceforge/jnlp/services/XFileContents.java121
-rw-r--r--netx/net/sourceforge/jnlp/services/XFileOpenService.java113
-rw-r--r--netx/net/sourceforge/jnlp/services/XFileSaveService.java140
-rw-r--r--netx/net/sourceforge/jnlp/services/XJNLPRandomAccessFile.java203
-rw-r--r--netx/net/sourceforge/jnlp/services/XPersistenceService.java177
-rw-r--r--netx/net/sourceforge/jnlp/services/XPrintService.java123
-rw-r--r--netx/net/sourceforge/jnlp/services/XServiceManagerStub.java106
-rw-r--r--netx/net/sourceforge/jnlp/services/XSingleInstanceService.java236
-rw-r--r--netx/net/sourceforge/jnlp/services/package.html29
-rw-r--r--netx/net/sourceforge/jnlp/tools/CharacterEncoder.java354
-rw-r--r--netx/net/sourceforge/jnlp/tools/HexDumpEncoder.java120
-rw-r--r--netx/net/sourceforge/jnlp/tools/JarRunner.java15
-rw-r--r--netx/net/sourceforge/jnlp/tools/JarSigner.java553
-rw-r--r--netx/net/sourceforge/jnlp/tools/JarSignerResources.java212
-rw-r--r--netx/net/sourceforge/jnlp/tools/KeyStoreUtil.java69
-rw-r--r--netx/net/sourceforge/jnlp/tools/KeyTool.java461
-rw-r--r--netx/net/sourceforge/jnlp/util/FileUtils.java159
-rw-r--r--netx/net/sourceforge/jnlp/util/PropertiesFile.java146
-rw-r--r--netx/net/sourceforge/jnlp/util/Reflect.java146
-rw-r--r--netx/net/sourceforge/jnlp/util/WeakList.java126
-rw-r--r--netx/net/sourceforge/jnlp/util/XDesktopEntry.java213
-rw-r--r--netx/net/sourceforge/nanoxml/XMLElement.java1334
-rw-r--r--netx/net/sourceforge/nanoxml/XMLParseException.java130
131 files changed, 23646 insertions, 0 deletions
diff --git a/netx/javaws.1 b/netx/javaws.1
new file mode 100644
index 0000000..dcda508
--- /dev/null
+++ b/netx/javaws.1
@@ -0,0 +1,123 @@
+.TH javaws 1 "9 Sep 2010"
+.SH NAME
+javaws - a Java Web Start client
+.SH SYNOPSYS
+.B javaws
+[-run-options] jnlp-file
+.br
+.B javaws
+[-control-option]
+.SH DESCRIPTION
+.B javaws
+is an implementation of a JNLP client. It uses a JNLP (Java Network
+Launch Protocol) file to securely run a remote Java application or
+a Java applet. This implementation of
+.B javaws
+is from the IcedTea project and is based on the NetX project.
+.PP
+A JNLP file is an xml file that describes how to securely run a
+remote Java application or a Java applet.
+
+.SH OPTIONS
+When specifying options, the name of the jnlp file must be the last
+argument to
+.B javaws
+- all the options must preceede it.
+.PP
+The jnlp-file can either be a url or a local path.
+.PP
+.B Control Options
+.PP
+By default
+.B javaws
+will launch the jnlp file specified on the command line. The control
+options can be used to change this behaviour.
+.TP 12
+\-about
+Shows a sample application that can be used to test the basic functionality
+of this implementation.
+.TP
+\-viewer
+Shows the trusted certificate viewer. This allows a user to list, examine, remove
+or export trusted certificates. Note that this only reflects the certificates
+trusted by
+.B javaws
+and not any other certificates or programs.
+
+.PP
+.B Run Options
+.PP
+In the default mode, the following run-options can be used:
+.TP 12
+\-basedir dir
+Directory where the cache and certificates to be used are stored.
+.TP
+\-arg arg
+Adds an application argument before launching.
+.TP
+\-param name=value
+Adds an applet parameter before launching.
+.TP
+\-property name=value
+Sets a system property before launching.
+.TP
+\-update seconds
+Update check if seconds since last checked.
+.TP
+\-license
+Display the GPL license and exit.
+.TP
+\-verbose
+Enable verbose output. Very useful in debugging.
+.TP
+\-nosecurity
+Disables the secure runtime environment.
+.TP
+\-noupdate
+Disables checking for updates.
+.TP
+\-headless
+Disables download window, other UIs.
+.TP
+\-strict
+Enables strict checking of JNLP file format. Any deviations from
+the JNLP DTD will cause
+.B javaws
+to abort.
+.TP
+\-umask=value
+Sets the umask for files created by an application.
+.TP
+\-Xnofork
+Do not create another JVM, even if the JNLP file asks for running in
+a separate JVM. This is useful for debugging.
+.TP
+\-Jjava-option
+This passes along java-option to the java binary that is running
+javaws. For example, to make javaws run with a max heap size
+of 80m, use -J-Xmx80m.
+.TP
+\-help
+Print a help message and exit.
+
+.SH FILES
+~/.netxrc specifies the location of the base directory
+
+.SH BUGS
+There arent any known bugs. If you come across one, please file it at
+ http://icedtea.classpath.org/bugzilla/
+.br
+Please run javaws in verbose mode and include that output along
+with the jnlp file when filing out the bug report.
+
+.SH AUTHOR
+Originally written by Jon. A. Maxwell.
+.br
+Currently maintained by the IcedTea contributors.
+
+.SH SEE ALSO
+.BR java (1)
+.br
+http://icedtea.classpath.org/
+.br
+http://jnlp.sourceforge.net/netx/
diff --git a/netx/javax/jnlp/BasicService.java b/netx/javax/jnlp/BasicService.java
new file mode 100644
index 0000000..cb9a445
--- /dev/null
+++ b/netx/javax/jnlp/BasicService.java
@@ -0,0 +1,12 @@
+
+
+package javax.jnlp;
+
+public interface BasicService {
+
+ public java.net.URL getCodeBase();
+ public boolean isOffline();
+ public boolean showDocument(java.net.URL url);
+ public boolean isWebBrowserSupported();
+
+}
diff --git a/netx/javax/jnlp/ClipboardService.java b/netx/javax/jnlp/ClipboardService.java
new file mode 100644
index 0000000..cada884
--- /dev/null
+++ b/netx/javax/jnlp/ClipboardService.java
@@ -0,0 +1,10 @@
+
+
+package javax.jnlp;
+
+public interface ClipboardService {
+
+ public java.awt.datatransfer.Transferable getContents();
+ public void setContents(java.awt.datatransfer.Transferable contents);
+
+}
diff --git a/netx/javax/jnlp/DownloadService.java b/netx/javax/jnlp/DownloadService.java
new file mode 100644
index 0000000..6710fc6
--- /dev/null
+++ b/netx/javax/jnlp/DownloadService.java
@@ -0,0 +1,24 @@
+
+
+package javax.jnlp;
+
+public interface DownloadService {
+
+ public boolean isResourceCached(java.net.URL ref, java.lang.String version);
+ public boolean isPartCached(java.lang.String part);
+ public boolean isPartCached(java.lang.String[] parts);
+ public boolean isExtensionPartCached(java.net.URL ref, java.lang.String version, java.lang.String part);
+ public boolean isExtensionPartCached(java.net.URL ref, java.lang.String version, java.lang.String[] parts);
+ public void loadResource(java.net.URL ref, java.lang.String version, DownloadServiceListener progress) throws java.io.IOException;
+ public void loadPart(java.lang.String part, DownloadServiceListener progress) throws java.io.IOException;
+ public void loadPart(java.lang.String[] parts, DownloadServiceListener progress) throws java.io.IOException;
+ public void loadExtensionPart(java.net.URL ref, java.lang.String version, java.lang.String part, DownloadServiceListener progress) throws java.io.IOException;
+ public void loadExtensionPart(java.net.URL ref, java.lang.String version, java.lang.String[] parts, DownloadServiceListener progress) throws java.io.IOException;
+ public void removeResource(java.net.URL ref, java.lang.String version) throws java.io.IOException;
+ public void removePart(java.lang.String part) throws java.io.IOException;
+ public void removePart(java.lang.String[] parts) throws java.io.IOException;
+ public void removeExtensionPart(java.net.URL ref, java.lang.String version, java.lang.String part) throws java.io.IOException;
+ public void removeExtensionPart(java.net.URL ref, java.lang.String version, java.lang.String[] parts) throws java.io.IOException;
+ public DownloadServiceListener getDefaultProgressWindow();
+
+}
diff --git a/netx/javax/jnlp/DownloadServiceListener.java b/netx/javax/jnlp/DownloadServiceListener.java
new file mode 100644
index 0000000..4d0fba3
--- /dev/null
+++ b/netx/javax/jnlp/DownloadServiceListener.java
@@ -0,0 +1,12 @@
+
+
+package javax.jnlp;
+
+public interface DownloadServiceListener {
+
+ public void progress(java.net.URL url, java.lang.String version, long readSoFar, long total, int overallPercent);
+ public void validating(java.net.URL url, java.lang.String version, long entry, long total, int overallPercent);
+ public void upgradingArchive(java.net.URL url, java.lang.String version, int patchPercent, int overallPercent);
+ public void downloadFailed(java.net.URL url, java.lang.String version);
+
+}
diff --git a/netx/javax/jnlp/ExtendedService.java b/netx/javax/jnlp/ExtendedService.java
new file mode 100644
index 0000000..b70a42b
--- /dev/null
+++ b/netx/javax/jnlp/ExtendedService.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package javax.jnlp;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This interface provides a way for the JNLP application to open specific files
+ * in the client's system. It asks permission from the user before opening any
+ * files.
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ *
+ */
+public interface ExtendedService {
+
+ /**
+ * Open a file on the client' system and return its contents. The user must
+ * grant permission to the application for this to work.
+ *
+ * @param file the file to open
+ * @return the opened file as a {@link FileContents} object
+ * @throws IOException on any io problems
+ */
+ FileContents openFile(File file) throws IOException;
+
+ /**
+ * Opens multiple files on the user's sytem and returns their contents as a
+ * {@link FileContents} array
+ *
+ * @param files the files to open
+ * @return an array of FileContents objects
+ * @throws IOException on any io problems
+ */
+ FileContents[] openFiles(File[] files) throws IOException;
+}
diff --git a/netx/javax/jnlp/ExtensionInstallerService.java b/netx/javax/jnlp/ExtensionInstallerService.java
new file mode 100644
index 0000000..a3b686c
--- /dev/null
+++ b/netx/javax/jnlp/ExtensionInstallerService.java
@@ -0,0 +1,21 @@
+
+
+package javax.jnlp;
+
+public interface ExtensionInstallerService {
+
+ public java.lang.String getInstallPath();
+ public java.lang.String getExtensionVersion();
+ public java.net.URL getExtensionLocation();
+ public void hideProgressBar();
+ public void hideStatusWindow();
+ public void setHeading(java.lang.String heading);
+ public void setStatus(java.lang.String status);
+ public void updateProgress(int value);
+ public void installSucceeded(boolean needsReboot);
+ public void installFailed();
+ public void setJREInfo(java.lang.String platformVersion, java.lang.String jrePath);
+ public void setNativeLibraryInfo(java.lang.String path);
+ public java.lang.String getInstalledJRE(java.net.URL url, java.lang.String version);
+
+}
diff --git a/netx/javax/jnlp/FileContents.java b/netx/javax/jnlp/FileContents.java
new file mode 100644
index 0000000..a612202
--- /dev/null
+++ b/netx/javax/jnlp/FileContents.java
@@ -0,0 +1,17 @@
+
+
+package javax.jnlp;
+
+public interface FileContents {
+
+ public java.lang.String getName() throws java.io.IOException;
+ public java.io.InputStream getInputStream() throws java.io.IOException;
+ public java.io.OutputStream getOutputStream(boolean overwrite) throws java.io.IOException;
+ public long getLength() throws java.io.IOException;
+ public boolean canRead() throws java.io.IOException;
+ public boolean canWrite() throws java.io.IOException;
+ public JNLPRandomAccessFile getRandomAccessFile(java.lang.String mode) throws java.io.IOException;
+ public long getMaxLength() throws java.io.IOException;
+ public long setMaxLength(long maxlength) throws java.io.IOException;
+
+}
diff --git a/netx/javax/jnlp/FileOpenService.java b/netx/javax/jnlp/FileOpenService.java
new file mode 100644
index 0000000..1ca5ee9
--- /dev/null
+++ b/netx/javax/jnlp/FileOpenService.java
@@ -0,0 +1,10 @@
+
+
+package javax.jnlp;
+
+public interface FileOpenService {
+
+ public FileContents openFileDialog(java.lang.String pathHint, java.lang.String[] extensions) throws java.io.IOException;
+ public FileContents[] openMultiFileDialog(java.lang.String pathHint, java.lang.String[] extensions) throws java.io.IOException;
+
+}
diff --git a/netx/javax/jnlp/FileSaveService.java b/netx/javax/jnlp/FileSaveService.java
new file mode 100644
index 0000000..b06d761
--- /dev/null
+++ b/netx/javax/jnlp/FileSaveService.java
@@ -0,0 +1,10 @@
+
+
+package javax.jnlp;
+
+public interface FileSaveService {
+
+ public FileContents saveFileDialog(java.lang.String pathHint, java.lang.String[] extensions, java.io.InputStream stream, java.lang.String name) throws java.io.IOException;
+ public FileContents saveAsFileDialog(java.lang.String pathHint, java.lang.String[] extensions, FileContents contents) throws java.io.IOException;
+
+}
diff --git a/netx/javax/jnlp/JNLPRandomAccessFile.java b/netx/javax/jnlp/JNLPRandomAccessFile.java
new file mode 100644
index 0000000..3d67fce
--- /dev/null
+++ b/netx/javax/jnlp/JNLPRandomAccessFile.java
@@ -0,0 +1,45 @@
+
+
+package javax.jnlp;
+public interface JNLPRandomAccessFile extends java.io.DataInput, java.io.DataOutput {
+
+
+ public void close() throws java.io.IOException;
+ public long length() throws java.io.IOException;
+ public long getFilePointer() throws java.io.IOException;
+ public int read() throws java.io.IOException;
+ public int read(byte[] b, int off, int len) throws java.io.IOException;
+ public int read(byte[] b) throws java.io.IOException;
+ public void readFully(byte[] b) throws java.io.IOException;
+ public void readFully(byte[] b, int off, int len) throws java.io.IOException;
+ public int skipBytes(int n) throws java.io.IOException;
+ public boolean readBoolean() throws java.io.IOException;
+ public byte readByte() throws java.io.IOException;
+ public int readUnsignedByte() throws java.io.IOException;
+ public short readShort() throws java.io.IOException;
+ public int readUnsignedShort() throws java.io.IOException;
+ public char readChar() throws java.io.IOException;
+ public int readInt() throws java.io.IOException;
+ public long readLong() throws java.io.IOException;
+ public float readFloat() throws java.io.IOException;
+ public double readDouble() throws java.io.IOException;
+ public java.lang.String readLine() throws java.io.IOException;
+ public java.lang.String readUTF() throws java.io.IOException;
+ public void seek(long pos) throws java.io.IOException;
+ public void setLength(long newLength) throws java.io.IOException;
+ public void write(int b) throws java.io.IOException;
+ public void write(byte[] b) throws java.io.IOException;
+ public void write(byte[] b, int off, int len) throws java.io.IOException;
+ public void writeBoolean(boolean v) throws java.io.IOException;
+ public void writeByte(int v) throws java.io.IOException;
+ public void writeShort(int v) throws java.io.IOException;
+ public void writeChar(int v) throws java.io.IOException;
+ public void writeInt(int v) throws java.io.IOException;
+ public void writeLong(long v) throws java.io.IOException;
+ public void writeFloat(float v) throws java.io.IOException;
+ public void writeDouble(double v) throws java.io.IOException;
+ public void writeBytes(java.lang.String s) throws java.io.IOException;
+ public void writeChars(java.lang.String s) throws java.io.IOException;
+ public void writeUTF(java.lang.String str) throws java.io.IOException;
+
+}
diff --git a/netx/javax/jnlp/PersistenceService.java b/netx/javax/jnlp/PersistenceService.java
new file mode 100644
index 0000000..008c670
--- /dev/null
+++ b/netx/javax/jnlp/PersistenceService.java
@@ -0,0 +1,17 @@
+
+package javax.jnlp;
+
+public interface PersistenceService {
+
+ public static final int CACHED = 0;
+ public static final int TEMPORARY = 1;
+ public static final int DIRTY = 2;
+
+ public long create(java.net.URL url, long maxsize) throws java.net.MalformedURLException, java.io.IOException;
+ public FileContents get(java.net.URL url) throws java.net.MalformedURLException, java.io.IOException, java.io.FileNotFoundException;
+ public void delete(java.net.URL url) throws java.net.MalformedURLException, java.io.IOException;
+ public java.lang.String[] getNames(java.net.URL url) throws java.net.MalformedURLException, java.io.IOException;
+ public int getTag(java.net.URL url) throws java.net.MalformedURLException, java.io.IOException;
+ public void setTag(java.net.URL url, int tag) throws java.net.MalformedURLException, java.io.IOException;
+
+}
diff --git a/netx/javax/jnlp/PrintService.java b/netx/javax/jnlp/PrintService.java
new file mode 100644
index 0000000..7573233
--- /dev/null
+++ b/netx/javax/jnlp/PrintService.java
@@ -0,0 +1,12 @@
+
+
+package javax.jnlp;
+
+public interface PrintService {
+
+ public java.awt.print.PageFormat getDefaultPage();
+ public java.awt.print.PageFormat showPageFormatDialog(java.awt.print.PageFormat page);
+ public boolean print(java.awt.print.Pageable document);
+ public boolean print(java.awt.print.Printable painter);
+
+}
diff --git a/netx/javax/jnlp/ServiceManager.java b/netx/javax/jnlp/ServiceManager.java
new file mode 100644
index 0000000..3488b3c
--- /dev/null
+++ b/netx/javax/jnlp/ServiceManager.java
@@ -0,0 +1,53 @@
+
+
+
+package javax.jnlp;
+
+import java.util.*;
+
+
+public final class ServiceManager {
+
+ private static ServiceManagerStub stub = null;
+
+ private static Map lookupTable = new HashMap(); // ensure lookup is idempotent
+
+ private ServiceManager() {
+ // says it can't be instantiated
+ }
+
+
+ public static java.lang.Object lookup(java.lang.String name) throws UnavailableServiceException {
+ if (stub == null)
+ throw new UnavailableServiceException("service stub not set.");
+
+ synchronized(lookupTable) {
+ Object result = lookupTable.get(name);
+
+ if (result == null) {
+ result = stub.lookup(name);
+ if (result != null)
+ lookupTable.put(name, result);
+ }
+
+ if (result == null)
+ throw new UnavailableServiceException("service not available (stub returned null).");
+
+ return result;
+ }
+ }
+
+ public static java.lang.String[] getServiceNames() {
+ // should this return the required ones even though no stub??
+ if (stub == null)
+ return new String[0];
+
+ return stub.getServiceNames();
+ }
+
+ public static void setServiceManagerStub(ServiceManagerStub stub) {
+ if (ServiceManager.stub == null)
+ ServiceManager.stub = stub;
+ }
+
+}
diff --git a/netx/javax/jnlp/ServiceManagerStub.java b/netx/javax/jnlp/ServiceManagerStub.java
new file mode 100644
index 0000000..04af1dd
--- /dev/null
+++ b/netx/javax/jnlp/ServiceManagerStub.java
@@ -0,0 +1,10 @@
+
+
+package javax.jnlp;
+
+public interface ServiceManagerStub {
+
+ public java.lang.Object lookup(java.lang.String name) throws UnavailableServiceException;
+ public java.lang.String[] getServiceNames();
+
+}
diff --git a/netx/javax/jnlp/SingleInstanceListener.java b/netx/javax/jnlp/SingleInstanceListener.java
new file mode 100644
index 0000000..f40a631
--- /dev/null
+++ b/netx/javax/jnlp/SingleInstanceListener.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package javax.jnlp;
+
+/**
+ * This interface specifies a listener which is notified whenever a new instance
+ * of the web start application is launched.
+ *
+ */
+public interface SingleInstanceListener {
+
+ /**
+ * This method is called when a new instance of the application is launched.
+ * The arguments passed to the new instance are passed into this method.
+ *
+ * @param arguments the arguments passed to the new instance of the
+ * application
+ */
+ void newActivation(String[] arguments);
+
+}
diff --git a/netx/javax/jnlp/SingleInstanceService.java b/netx/javax/jnlp/SingleInstanceService.java
new file mode 100644
index 0000000..1eb9ddc
--- /dev/null
+++ b/netx/javax/jnlp/SingleInstanceService.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package javax.jnlp;
+
+/**
+ * The SingleInstanceService provides a way to ensure that only one instance of
+ * the application is ever running - singleton behavior at the application
+ * level.
+ *
+ */
+public interface SingleInstanceService {
+
+ /**
+ * Adds the specified SingleInstanceListener to the notification list. This
+ * listener is notified when a new instance of the application is started.
+ *
+ *
+ * @param listener the single instance listener to be added. No action is
+ * performed if it is null.
+ */
+ void addSingleInstanceListener(SingleInstanceListener listener);
+
+ /**
+ * Removes the specified SingleInstanceListener from the notification list.
+ * This listener will not be notified if a new instance of the application
+ * is started.
+ *
+ * @param listener the single instance listener to be removed. No action is
+ * performed if it is null or not in the notification list.
+ */
+ void removeSingleInstanceListener(SingleInstanceListener listener);
+}
diff --git a/netx/javax/jnlp/UnavailableServiceException.java b/netx/javax/jnlp/UnavailableServiceException.java
new file mode 100644
index 0000000..b5c342d
--- /dev/null
+++ b/netx/javax/jnlp/UnavailableServiceException.java
@@ -0,0 +1,15 @@
+
+
+package javax.jnlp;
+
+public class UnavailableServiceException extends Exception {
+
+ public UnavailableServiceException() {
+ super();
+ }
+
+ public UnavailableServiceException(java.lang.String message) {
+ super(message);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/AppletDesc.java b/netx/net/sourceforge/jnlp/AppletDesc.java
new file mode 100644
index 0000000..1563b87
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/AppletDesc.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The applet-desc element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.8 $
+ */
+public class AppletDesc {
+
+ /** the applet name */
+ private String name;
+
+ /** the main class name and package */
+ private String mainClass;
+
+ /** the document base */
+ private URL documentBase;
+
+ /** the width */
+ private int width;
+
+ /** the height */
+ private int height;
+
+ /** the parameters */
+ private Map parameters;
+
+
+ /**
+ * Create an Applet descriptor.
+ *
+ * @param name the applet name
+ * @param mainClass the main class name and package
+ * @param documentBase the document base
+ * @param width the width
+ * @param height the height
+ * @param parameters the parameters
+ */
+ public AppletDesc(String name, String mainClass, URL documentBase, int width, int height, Map parameters) {
+ this.name = name;
+ this.mainClass = mainClass;
+ this.documentBase = documentBase;
+ this.width = width;
+ this.height = height;
+ this.parameters = new HashMap(parameters);
+ }
+
+ /**
+ * Returns the applet name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the main class name
+ */
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ /**
+ * Returns the document base
+ */
+ public URL getDocumentBase() {
+ return documentBase;
+ }
+
+ /**
+ * Returns the width
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Returns the height
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Returns the applet parameters
+ */
+ public Map getParameters() {
+ return new HashMap(parameters);
+ }
+
+ /**
+ * Adds a parameter to the applet. If the parameter already
+ * exists then it is overwritten with the new value. Adding a
+ * parameter will have no effect on already-running applets
+ * launched from this JNLP file.
+ */
+ public void addParameter(String name, String value) {
+ parameters.put(name, value);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ApplicationDesc.java b/netx/net/sourceforge/jnlp/ApplicationDesc.java
new file mode 100644
index 0000000..cb5c458
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ApplicationDesc.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The application-desc element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+public class ApplicationDesc {
+
+ /** the main class name and package */
+ private String mainClass;
+
+ /** the arguments */
+ private String arguments[];
+
+
+ /**
+ * Create an Application descriptor.
+ *
+ * @param mainClass the main class name and package
+ * @param arguments the arguments
+ */
+ public ApplicationDesc(String mainClass, String arguments[]) {
+ this.mainClass = mainClass;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Returns the main class name
+ */
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ /**
+ * Returns the arguments
+ */
+ public String[] getArguments() {
+ return (String[]) arguments.clone();
+ }
+
+ /**
+ * Add an argument to the end of the arguments.
+ */
+ public void addArgument(String arg) {
+ ArrayList l = new ArrayList(Arrays.asList(arguments));
+ l.add(arg);
+
+ arguments = (String[]) l.toArray(arguments);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/AssociationDesc.java b/netx/net/sourceforge/jnlp/AssociationDesc.java
new file mode 100644
index 0000000..8acb66d
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/AssociationDesc.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+public final class AssociationDesc {
+
+ /** the extensions this application wants to register with */
+ private String[] extensions;
+
+ /** the mime type for the association */
+ private String mimeType;
+
+ public AssociationDesc(String mimeType, String[] extensions) throws ParseException {
+ checkMimeType(mimeType);
+ this.mimeType = mimeType;
+ this.extensions = extensions;
+ }
+
+ /**
+ * Return the extensions for this association
+ */
+ public String[] getExtensions() {
+ return extensions;
+ }
+
+ /**
+ * Return the mimetype for this association
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Check for valid mimeType
+ * @param mimeType a mime type
+ * @throws ParseException if mimeType is an invalid MIME type
+ */
+ private void checkMimeType(String mimeType) throws ParseException {
+ // TODO check that mime type is valid
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ComponentDesc.java b/netx/net/sourceforge/jnlp/ComponentDesc.java
new file mode 100644
index 0000000..7d0b2a3
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ComponentDesc.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The component-desc element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class ComponentDesc {
+
+ // this is for completeness and in case of changes to spec for components.
+
+ /**
+ * Create a component descriptor.
+ */
+ public ComponentDesc() {
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/DefaultLaunchHandler.java b/netx/net/sourceforge/jnlp/DefaultLaunchHandler.java
new file mode 100644
index 0000000..357ad1a
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/DefaultLaunchHandler.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.util.*;
+
+import java.awt.*;
+import java.util.*;
+import javax.swing.*;
+
+
+/**
+ * This default implementation shows prints the exception to
+ * stdout and if not in headless mode displays the exception in a
+ * dialog.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.1 $
+ */
+public class DefaultLaunchHandler implements LaunchHandler {
+
+ /**
+ * Called when the application could not be launched due to a
+ * fatal error, such as the inability to find the main class
+ * or non-parseable XML.
+ */
+ public void launchError(LaunchException exception) {
+ printMessage(exception);
+ }
+
+ /**
+ * Called when launching the application can not be launched
+ * due to an error that is not fatal. For example a JNLP file
+ * that is not strictly correct yet does not necessarily
+ * prohibit the system from attempting to launch the
+ * application.
+ *
+ * @return true if the launch should continue, false to abort
+ */
+ public boolean launchWarning(LaunchException warning) {
+ printMessage(warning);
+ return true;
+ }
+
+ /**
+ * Called when a security validation error occurs while
+ * launching the application.
+ *
+ * @return true to allow the application to continue, false to stop it.
+ */
+ public boolean validationError(LaunchException security) {
+ printMessage(security);
+ return true;
+ }
+
+ /**
+ * Called when an application, applet, or installer has been
+ * launched successfully (the main method or applet start method
+ * returned normally).
+ *
+ * @param application the launched application instance
+ */
+ public void launchCompleted(ApplicationInstance application) {
+ //
+ }
+
+ /**
+ * Print a message to stdout.
+ */
+ protected void printMessage(LaunchException ex) {
+ StringBuffer result = new StringBuffer();
+ result.append("netx: ");
+ result.append(ex.getCategory());
+ if (ex.getSummary() != null) {
+ result.append(": ");
+ result.append(ex.getSummary());
+ }
+
+ if (JNLPRuntime.isDebug()) {
+ if (ex.getCause() != null)
+ ex.getCause().printStackTrace();
+ else
+ ex.printStackTrace();
+ }
+
+ Throwable causes[] = ex.getCauses();
+
+ for (int i=0; i < causes.length; i++) {
+ result.append(" (");
+ result.append(causes[i].getClass().getName());
+ result.append(" ");
+ result.append(causes[i].getMessage());
+ result.append(")");
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ExtensionDesc.java b/netx/net/sourceforge/jnlp/ExtensionDesc.java
new file mode 100644
index 0000000..2202058
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ExtensionDesc.java
@@ -0,0 +1,143 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+
+/**
+ * The extension element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.8 $
+ */
+public class ExtensionDesc {
+
+ /** the extension name */
+ private String name;
+
+ /** the required extension version */
+ private Version version;
+
+ /** the location of the extension JNLP file */
+ private URL location;
+
+ /** the JNLPFile the extension refers to */
+ private JNLPFile file;
+
+ /** map from ext-part to local part */
+ private Map extToPart = new HashMap();
+
+ /** eager ext parts */
+ private List eagerExtParts = new ArrayList();
+
+
+ /**
+ * Create an extention descriptor.
+ *
+ * @param name the extension name
+ * @param version the required version of the extention JNLPFile
+ * @param location the location of the extention JNLP file
+ */
+ public ExtensionDesc(String name, Version version, URL location) {
+ this.name = name;
+ this.version = version;
+ this.location = location;
+ }
+
+ /**
+ * Adds an extension part to be downloaded when the specified
+ * part of the main JNLP file is loaded. The extension part
+ * will be downloaded before the application is launched if the
+ * lazy value is false or the part is empty or null.
+ *
+ * @param extPart the part name in the extension file
+ * @param part the part name in the main file
+ * @param lazy whether to load the part before launching
+ */
+ protected void addPart(String extPart, String part, boolean lazy) {
+ extToPart.put(extPart, part);
+
+ if (!lazy || part == null || part.length() == 0)
+ eagerExtParts.add(extPart);
+ }
+
+ /**
+ * Returns the parts in the extension JNLP file mapped to the
+ * part of the main file.
+ */
+ public String[] getExtensionParts(String thisPart) {
+
+ return null;
+ }
+
+ /**
+ * Returns the name of the extension.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the required version of the extension JNLP file.
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the location of the extension JNLP file.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Resolves the extension by creating a JNLPFile from the file
+ * specified by the extension's location property.
+ *
+ * @throws IOException if the extension JNLPFile could not be resolved.
+ * @throws ParseException if the extension JNLPFile could not be
+ * parsed or was not a component or installer descriptor.
+ */
+ public void resolve() throws ParseException, IOException {
+ if (file == null) {
+ file = new JNLPFile(location);
+
+ if (JNLPRuntime.isDebug())
+ System.out.println("Resolve: "+file.getInformation().getTitle());
+
+ // check for it being an extension descriptor
+ if (!file.isComponent() && !file.isInstaller())
+ throw new ParseException(JNLPRuntime.getMessage("JInvalidExtensionDescriptor", new Object[] {name, location} ));
+ }
+
+ }
+
+ /**
+ * Returns a JNLPFile for the extension, or null if the JNLP
+ * file has not been resolved.
+ */
+ public JNLPFile getJNLPFile() {
+ return file;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/IconDesc.java b/netx/net/sourceforge/jnlp/IconDesc.java
new file mode 100644
index 0000000..c115b42
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/IconDesc.java
@@ -0,0 +1,135 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The icon element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.8 $
+ */
+public class IconDesc {
+
+ /** default icon */
+ public static final Object DEFAULT = "default";
+
+ /** selected icon */
+ public static final Object SELECTED = "selected";
+
+ /** disabled icon */
+ public static final Object DISABLED = "disabled";
+
+ /** rollover icon */
+ public static final Object ROLLOVER = "rollover";
+
+ /** splash icon */
+ public static final Object SPLASH = "splash";
+
+ /** destop shortcut icon */
+ public static final Object SHORTCUT = "shortcut";
+
+
+ /** the location of the icon */
+ private URL location;
+
+ /** the type of icon*/
+ private Object kind;
+
+ /** the width, or -1 if unknown*/
+ private int width;
+
+ /** the height, or -1 if unknown*/
+ private int height;
+
+ /** the depth, or -1 if unknown*/
+ private int depth;
+
+ /** the size, or -1 if unknown*/
+ private int size;
+
+
+ /**
+ * Creates an icon descriptor with the specified information.
+ *
+ * @param location the location of the icon
+ * @param kind the type of icon
+ * @param width the width, or -1 if unknown
+ * @param height the height, or -1 if unknown
+ * @param depth the depth, or -1 if unknown
+ * @param size the size, or -1 if unknown
+ */
+ IconDesc(URL location, Object kind, int width, int height, int depth, int size) {
+ this.location = location;
+ this.kind = kind;
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ this.size = size;
+ }
+
+ /**
+ * Returns the location of the icon.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the icon type.
+ */
+ public Object getKind() {
+ return kind;
+ }
+
+ /**
+ * Returns the icon width or -1 if not specified in the
+ * JNLPFile.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Returns the icon height or -1 if not specified in the
+ * JNLPFile.
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Returns the icon size or -1 if not specified in the JNLPFile.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the icon depth or -1 if not specified in the
+ * JNLPFile.
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+
+}
diff --git a/netx/net/sourceforge/jnlp/InformationDesc.java b/netx/net/sourceforge/jnlp/InformationDesc.java
new file mode 100644
index 0000000..cd039a0
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/InformationDesc.java
@@ -0,0 +1,267 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.awt.Dimension;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The information element.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public class InformationDesc {
+
+ // There is an understanding between this class and the parser
+ // that description and icon types are keyed by "icon-"+kind and
+ // "description-"+kind, and that other types are keyed by their
+ // specification name.
+
+ /** one-line description */
+ public static final Object ONE_LINE= "oneline";
+
+ /** short description */
+ public static final Object SHORT= "short";
+
+ /** tooltip description */
+ public static final Object TOOLTIP= "tooltip";
+
+ /** default description */
+ public static final Object DEFAULT = "default";
+
+ /** the locales for the information */
+ private Locale locales[];
+
+ /** the data as list of key,value pairs */
+ private List info;
+
+ /** the JNLPFile this information is for */
+ private JNLPFile jnlpFile;
+
+
+ /**
+ * Create an information element object.
+ *
+ * @param jnlpFile file that the information is for
+ * @param locale the the information is for
+ */
+ public InformationDesc(JNLPFile jnlpFile, Locale locales[]) {
+ this.jnlpFile = jnlpFile;
+ this.locales = locales;
+ }
+
+ /**
+ * Returns the application's title.
+ */
+ public String getTitle() {
+ return (String) getItem("title");
+ }
+
+ /**
+ * Returns the application's vendor.
+ */
+ public String getVendor() {
+ return (String) getItem("vendor");
+ }
+
+ /**
+ * Returns the application's homepage.
+ */
+ public URL getHomepage() {
+ return (URL)getItem("homepage");
+ }
+
+ /**
+ * Returns the default description for the application.
+ */
+ public String getDescription() {
+ String result = getDescription(DEFAULT);
+
+ // try to find any description if default is null
+ if (result == null)
+ result = getDescription(ONE_LINE);
+ if (result == null)
+ result = getDescription(SHORT);
+ if (result == null)
+ result = getDescription(TOOLTIP);
+
+ return result;
+ }
+
+ /**
+ * Returns the application's description of the specified type.
+ *
+ * @param kind one of Information.SHORT, Information.ONE_LINE,
+ * Information.TOOLTIP, Information.DEFAULT
+ */
+ public String getDescription(Object kind) {
+ String result = (String) getItem("description-"+kind);
+ if (result == null)
+ return (String) getItem("description-"+DEFAULT);
+ else
+ return result;
+ }
+
+ /**
+ * Returns the icons specified by the JNLP file.
+ *
+ * @param kind one of IconDesc.SELECTED, IconDesc.DISABLED,
+ * IconDesc.ROLLOVER, IconDesc.SPLASH, IconDesc.DEFAULT
+ * @return an array of zero of more IconDescs of the specified icon type
+ */
+ public IconDesc[] getIcons(Object kind) {
+ List icons = getItems("icon-"+kind);
+
+ return (IconDesc[]) icons.toArray(new IconDesc[icons.size()]);
+ };
+
+ /**
+ * Returns the URL of the icon closest to the specified size and
+ * kind. This method will not return an icon smaller than the
+ * specified width and height unless there are no other icons
+ * available.
+ *
+ * @param kind the kind of icon to get
+ * @param width desired width of icon
+ * @param height desired height of icon
+ * @return the closest icon by size or null if no icons declared
+ */
+ public URL getIconLocation(Object kind, int width, int height) {
+ IconDesc icons[] = getIcons(kind);
+ if (icons.length == 0)
+ return null;
+
+ IconDesc best = null;
+ for (int i=0; i < icons.length; i++) {
+ if (icons[i].getWidth() >= width &&
+ icons[i].getHeight() >= height) {
+ if (best == null)
+ best = icons[i];
+
+ if (icons[i].getWidth() <= best.getWidth() && // Use <= so last specified of
+ icons[i].getHeight() <= best.getHeight()) // equivalent icons is chosen.
+ best = icons[i];
+ }
+ }
+
+ if (best == null)
+ best = icons[0];
+
+ return best.getLocation();
+ }
+
+ /**
+ * Returns the locales for the information.
+ */
+ public Locale[] getLocales() {
+ return locales;
+ }
+
+ /**
+ * Returns the JNLPFile the information is for.
+ */
+ public JNLPFile getJNLPFile() {
+ return jnlpFile;
+ }
+
+ /**
+ * Returns whether offline execution allowed.
+ */
+ public boolean isOfflineAllowed() {
+ return null != getItem("offline-allowed");
+ }
+
+ /**
+ * Returns whether the resources specified in the JNLP file may
+ * be shared by more than one instance in the same JVM
+ * (JNLP extension). This is an extension to the JNLP spec and
+ * will always return false for standard JNLP files.
+ */
+ public boolean isSharingAllowed() {
+ return null != getItem("sharing-allowed");
+ }
+
+ /**
+ * Returns the associations specified in the JNLP file
+ */
+ public AssociationDesc[] getAssociations() {
+ List associations = getItems("association");
+
+ return (AssociationDesc[]) associations.toArray(new AssociationDesc[associations.size()]);
+ }
+
+ /**
+ * Returns the shortcut specified by this JNLP file
+ */
+ public ShortcutDesc getShortcut() {
+ return (ShortcutDesc) getItem("shortcut");
+ }
+
+ /**
+ * Returns the related-contents specified by this JNLP file
+ */
+ public RelatedContentDesc[] getRelatedContents() {
+ List relatedContents = getItems("related-content");
+
+ return (RelatedContentDesc[]) relatedContents.toArray(
+ new RelatedContentDesc[relatedContents.size()]);
+ }
+
+ /**
+ * Returns the last item matching the specified key.
+ */
+ protected Object getItem(Object key) {
+ List items = getItems(key);
+ if (items.size() == 0)
+ return null;
+ else
+ return items.get( items.size()-1 );
+ }
+
+ /**
+ * Returns all items matching the specified key.
+ */
+ protected List getItems(Object key) {
+ if (info == null)
+ return Collections.EMPTY_LIST;
+
+ List result = new ArrayList();
+ for (int i=0; i < info.size(); i+=2)
+ if (info.get(i).equals(key))
+ result.add( info.get(i+1) );
+
+ return result;
+ }
+
+ /**
+ * Add an information item (description, icon, etc) under a
+ * specified key name.
+ */
+ protected void addItem(String key, Object value) {
+ if (info == null)
+ info = new ArrayList();
+
+ info.add(key);
+ info.add(value);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/InstallerDesc.java b/netx/net/sourceforge/jnlp/InstallerDesc.java
new file mode 100644
index 0000000..17b8f87
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/InstallerDesc.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The installer-desc element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class InstallerDesc {
+
+ /** the main class name and package. */
+ private String mainClass;
+
+
+ /**
+ * Creates a installer descriptor.
+ *
+ * @param mainClass main class name and package
+ */
+ public InstallerDesc(String mainClass) {
+ this.mainClass = mainClass;
+ }
+
+ /**
+ * Returns the main class name and package.
+ */
+ public String getMainClass() {
+ return mainClass;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/JARDesc.java b/netx/net/sourceforge/jnlp/JARDesc.java
new file mode 100644
index 0000000..d3941ca
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/JARDesc.java
@@ -0,0 +1,141 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The JAR element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class JARDesc {
+
+ /** the location of the JAR file */
+ private URL location;
+
+ /** the required JAR versions, or null */
+ private Version version;
+
+ /** the part name */
+ private String part;
+
+ /** whether to load the JAR on demand */
+ private boolean lazy;
+
+ /** whether the JAR contains the main class */
+ private boolean main;
+
+ /** whether the JAR contains native libraries */
+ private boolean nativeJar;
+
+ /** whether the JAR can be cached */
+ private boolean cacheable;
+
+ /**
+ * Create a JAR descriptor.
+ *
+ * @param location the location of the JAR file
+ * @param version the required JAR versions, or null
+ * @param part the part name, or null
+ * @param lazy whether to load the JAR on demand
+ * @param main whether the JAR contains the main class
+ * @param nativeJam whether the JAR contains native libraries
+ */
+ public JARDesc(URL location, Version version, String part, boolean lazy, boolean main, boolean nativeJar, boolean cacheable) {
+ this.location = location;
+ this.version = version;
+ this.part = part;
+ this.lazy = lazy;
+ this.main = main;
+ this.nativeJar = nativeJar;
+ this.cacheable = cacheable;
+ }
+
+ /**
+ * Returns the URL of the JAR file.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the required version of the JAR file.
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the part name, or null if not specified in the JNLP
+ * file.
+ */
+ public String getPart() {
+ return part;
+ }
+
+ /**
+ * Returns true if the JAR file contains native code
+ * libraries.
+ */
+ public boolean isNative() {
+ return nativeJar;
+ }
+
+ // these both are included in case the spec adds a new value,
+ // where !lazy would no longer imply eager.
+
+ /**
+ * Returns true if the JAR file should be downloaded before
+ * starting the application.
+ */
+ public boolean isEager() {
+ return !lazy;
+ }
+
+ /**
+ * Returns true if the JAR file should be downloaded on demand.
+ */
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ /**
+ * Returns true if the JNLP file defined this JAR as containing
+ * the main class. If no JARs were defined as the main JAR then
+ * the first JAR should be used to locate the main class.
+ *
+ * @see ResourcesDesc#getMainJAR
+ */
+ public boolean isMain() {
+ return main;
+ }
+
+ /**
+ * Returns if this jar is cacheable
+ *
+ * @return Whether or not this jar is cacheable
+ */
+ public boolean isCacheable() {
+ return cacheable;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/JNLPFile.java b/netx/net/sourceforge/jnlp/JNLPFile.java
new file mode 100644
index 0000000..a02ca1e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/JNLPFile.java
@@ -0,0 +1,633 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * Provides methods to access the information in a Java Network
+ * Launching Protocol (JNLP) file. The Java Network Launching
+ * Protocol specifies in an XML file the information needed to
+ * load, cache, and run Java code over the network and in a secure
+ * environment.<p>
+ *
+ * This class represents the overall information about a JNLP file
+ * from the jnlp element. Other information is accessed through
+ * objects that represent the elements of a JNLP file
+ * (information, resources, application-desc, etc). References to
+ * these objects are obtained by calling the getInformation,
+ * getResources, getSecurity, etc methods.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.21 $
+ */
+public class JNLPFile {
+
+ // todo: save the update policy, then if file was not updated
+ // then do not check resources for being updated.
+ //
+ // todo: make getLaunchInfo return a superclass that all the
+ // launch types implement (can get codebase from it).
+ //
+ // todo: currently does not filter resources by jvm version.
+ //
+
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+
+ /** the location this JNLP file was created from */
+ protected URL sourceLocation = null;
+
+ /** the network location of this JNLP file */
+ protected URL fileLocation;
+
+ /** A key that uniquely identifies connected instances (main jnlp+ext) */
+ protected String uniqueKey = null;
+
+ /** the URL used to resolve relative URLs in the file */
+ protected URL codeBase;
+
+ /** file version */
+ protected Version fileVersion;
+
+ /** spec version */
+ protected Version specVersion;
+
+ /** information */
+ protected List info;
+
+ protected UpdateDesc update;
+
+ /** resources */
+ protected List resources;
+
+ /** additional resources not in JNLP file (from command line) */
+ protected ResourcesDesc sharedResources = new ResourcesDesc(this, null, null, null);
+
+ /** the application description */
+ protected Object launchType;
+
+ /** the security descriptor */
+ protected SecurityDesc security;
+
+ /** the default OS */
+ protected Locale defaultLocale = null;
+
+ /** the default arch */
+ protected String defaultOS = null;
+
+ /** the default jvm */
+ protected String defaultArch = null;
+
+ { // initialize defaults if security allows
+ try {
+ defaultLocale = Locale.getDefault();
+ defaultOS = System.getProperty("os.name");
+ defaultArch = System.getProperty("os.arch");
+ }
+ catch (SecurityException ex) {
+ // null values will still work, and app can set defaults later
+ }
+ }
+
+ /**
+ * Empty stub, allowing child classes to override the constructor
+ */
+ protected JNLPFile() {
+ }
+
+ /**
+ * Create a JNLPFile from a URL.
+ *
+ * @param location the location of the JNLP file
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(URL location) throws IOException, ParseException {
+ this(location, false); // not strict
+ }
+
+ /**
+ * Create a JNLPFile from a URL checking for updates using the
+ * default policy.
+ *
+ * @param location the location of the JNLP file
+ * @param strict whether to enforce the spec when
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(URL location, boolean strict) throws IOException, ParseException {
+ this(location, (Version) null, strict);
+ }
+
+ /**
+ * Create a JNLPFile from a URL and a Version checking for updates using
+ * the default policy.
+ *
+ * @param location the location of the JNLP file
+ * @param version the version of the JNLP file
+ * @param strict whether to enforce the spec when
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(URL location, Version version, boolean strict) throws IOException, ParseException {
+ this(location, version, strict, JNLPRuntime.getDefaultUpdatePolicy());
+ }
+
+ /**
+ * Create a JNLPFile from a URL and a version, checking for updates
+ * using the specified policy.
+ *
+ * @param location the location of the JNLP file
+ * @param version the version of the JNLP file
+ * @param strict whether to enforce the spec when
+ * @param policy the update policy
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(URL location, Version version, boolean strict, UpdatePolicy policy) throws IOException, ParseException {
+ Node root = Parser.getRootNode(openURL(location, version, policy));
+ parse(root, strict, location);
+
+ //Downloads the original jnlp file into the cache if possible
+ //(i.e. If the jnlp file being launched exist locally, but it
+ //originated from a website, then download the one from the website
+ //into the cache).
+ if (sourceLocation != null && location.getProtocol() == "file") {
+ openURL(sourceLocation, version, policy);
+ }
+
+ this.fileLocation = location;
+
+ this.uniqueKey = Calendar.getInstance().getTimeInMillis() + "-" +
+ Math.abs(((new java.util.Random()).nextInt())) + "-" +
+ location;
+
+ if (JNLPRuntime.isDebug())
+ System.err.println("UNIQUEKEY=" + this.uniqueKey);
+ }
+
+ /**
+ * Create a JNLPFile from a URL, parent URLm a version and checking for
+ * updates using the specified policy.
+ *
+ * @param location the location of the JNLP file
+ * @param uniqueKey A string that uniquely identifies connected instances
+ * @param version the version of the JNLP file
+ * @param strict whether to enforce the spec when
+ * @param policy the update policy
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(URL location, String uniqueKey, Version version, boolean strict, UpdatePolicy policy) throws IOException, ParseException {
+ this(location, version, strict, policy);
+ this.uniqueKey = uniqueKey;
+
+ if (JNLPRuntime.isDebug())
+ System.err.println("UNIQUEKEY (override) =" + this.uniqueKey);
+ }
+
+ /**
+ * Create a JNLPFile from an input stream.
+ *
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ public JNLPFile(InputStream input, boolean strict) throws ParseException {
+ parse(Parser.getRootNode(input), strict, null);
+ }
+
+ /**
+ * Create a JNLPFile from a character stream.
+ *
+ * @param input the stream
+ * @param strict whether to enforce the spec when
+ * @throws IOException if an IO exception occurred
+ * @throws ParseException if the JNLP file was invalid
+ */
+ private JNLPFile(Reader input, boolean strict) throws ParseException {
+ // todo: now that we are using NanoXML we can use a Reader
+ //parse(Parser.getRootNode(input), strict, null);
+ }
+
+
+ /**
+ * Open the jnlp file URL from the cache if there, otherwise
+ * download to the cache. Called from constructor.
+ */
+ private static InputStream openURL(URL location, Version version, UpdatePolicy policy) throws IOException {
+ if (location == null || policy == null)
+ throw new IllegalArgumentException(R("NullParameter"));
+
+ try {
+ ResourceTracker tracker = new ResourceTracker(false); // no prefetch
+ tracker.addResource(location, version , policy);
+
+ return tracker.getInputStream(location);
+ }
+ catch (Exception ex) {
+ throw new IOException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Returns the JNLP specification versions supported.
+ */
+ public static Version getSupportedVersions() {
+ return Parser.getSupportedVersions();
+ }
+
+ /**
+ * Returns the JNLP file's title. This method returns the same
+ * value as InformationDesc.getTitle().
+ */
+ public String getTitle() {
+ return getInformation().getTitle();
+ }
+
+ /**
+ * Returns the JNLP file's network location as specified in the
+ * JNLP file.
+ */
+ public URL getSourceLocation() {
+ return sourceLocation;
+ }
+
+ /**
+ * Returns the location of the file parsed to create the JNLP
+ * file, or null if it was not created from a URL.
+ */
+ public URL getFileLocation() {
+ return fileLocation;
+ }
+
+ /**
+ * Returns the location of the parent file if it exists, null otherwise
+ */
+ public String getUniqueKey() {
+ return uniqueKey;
+ }
+
+ /**
+ * Returns the JNLP file's version.
+ */
+ public Version getFileVersion() {
+ return fileVersion;
+ }
+
+ /**
+ * Returns the specification version required by the file.
+ */
+ public Version getSpecVersion() {
+ return specVersion;
+ }
+
+ /**
+ * Returns the codebase URL for the JNLP file.
+ */
+ public URL getCodeBase() {
+ return codeBase;
+ }
+
+ /**
+ * Returns the information section of the JNLP file as viewed
+ * through the default locale.
+ */
+ public InformationDesc getInformation() {
+ return getInformation(defaultLocale);
+ }
+
+ /**
+ * Returns the information section of the JNLP file as viewed
+ * through the specified locale.
+ */
+ public InformationDesc getInformation(final Locale locale) {
+ return new InformationDesc(this, new Locale[] {locale}) {
+ protected List getItems(Object key) {
+ List result = new ArrayList();
+
+ for (int i=0; i < info.size(); i++) {
+ InformationDesc infoDesc = (InformationDesc) info.get(i);
+
+ if (localMatches(locale, infoDesc.getLocales()))
+ if (localMatches(locale, infoDesc.getLocales()))
+ result.addAll(infoDesc.getItems(key) );
+ }
+
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Returns the update section of the JNLP file.
+ */
+ public UpdateDesc getUpdate() {
+ return update;
+ }
+
+ /**
+ * Returns the security section of the JNLP file.
+ */
+ public SecurityDesc getSecurity() {
+ return security;
+ }
+
+ /**
+ * Returns the resources section of the JNLP file as viewed
+ * through the default locale and the os.name and os.arch
+ * properties.
+ */
+ public ResourcesDesc getResources() {
+ return getResources(defaultLocale, defaultOS, defaultArch);
+ }
+
+ /**
+ * Returns the information section of the JNLP file for the
+ * specified locale, os, and arch.
+ */
+ public ResourcesDesc getResources(final Locale locale, final String os, final String arch) {
+ return new ResourcesDesc(this, new Locale[] {locale}, new String[] {os}, new String[] {arch}) {
+ public List getResources(Class launchType) {
+ List result = new ArrayList();
+
+ for (int i=0; i < resources.size(); i++) {
+ ResourcesDesc rescDesc = (ResourcesDesc) resources.get(i);
+
+ if (localMatches(locale, rescDesc.getLocales())
+ && stringMatches(os, rescDesc.getOS())
+ && stringMatches(arch, rescDesc.getArch()))
+ result.addAll(rescDesc.getResources(launchType) );
+ }
+
+ result.addAll(sharedResources.getResources(launchType));
+
+ return result;
+ }
+
+ public void addResource(Object resource) {
+ // todo: honor the current locale, os, arch values
+ sharedResources.addResource(resource);
+ }
+ };
+ }
+
+ /**
+ * Returns an object of one of the following types: AppletDesc,
+ * ApplicationDesc, InstallerDesc, and ComponentDesc.
+ */
+ public Object getLaunchInfo() {
+ return launchType;
+ }
+
+ /**
+ * Returns the launch information for an applet.
+ *
+ * @throws UnsupportedOperationException if there is no applet information
+ */
+ public AppletDesc getApplet() {
+ if (!isApplet())
+ throw new UnsupportedOperationException(R("JNotApplet"));
+
+ return (AppletDesc) launchType;
+ }
+
+ /**
+ * Returns the launch information for an application.
+ *
+ * @throws UnsupportedOperationException if there is no application information
+ */
+ public ApplicationDesc getApplication() {
+ if (!isApplication())
+ throw new UnsupportedOperationException(R("JNotApplication"));
+
+ return (ApplicationDesc) launchType;
+ }
+
+ /**
+ * Returns the launch information for a component.
+ *
+ * @throws UnsupportedOperationException if there is no component information
+ */
+ public ComponentDesc getComponent() {
+ if (!isComponent())
+ throw new UnsupportedOperationException(R("JNotComponent"));
+
+ return (ComponentDesc) launchType;
+ }
+
+ /**
+ * Returns the launch information for an installer.
+ *
+ * @throws UnsupportedOperationException if there is no installer information
+ */
+ public InstallerDesc getInstaller() {
+ if (!isInstaller())
+ throw new UnsupportedOperationException(R("NotInstaller"));
+
+ return (InstallerDesc) launchType;
+ }
+
+ /**
+ * Returns whether the lauch descriptor describes an Applet.
+ */
+ public boolean isApplet() {
+ return launchType instanceof AppletDesc;
+ }
+
+ /**
+ * Returns whether the lauch descriptor describes an Application.
+ */
+ public boolean isApplication() {
+ return launchType instanceof ApplicationDesc;
+ }
+
+ /**
+ * Returns whether the lauch descriptor describes a Component.
+ */
+ public boolean isComponent() {
+ return launchType instanceof ComponentDesc;
+ }
+
+ /**
+ * Returns whether the lauch descriptor describes an Installer.
+ */
+ public boolean isInstaller() {
+ return launchType instanceof InstallerDesc;
+ }
+
+ /**
+ * Sets the default view of the JNLP file returned by
+ * getInformation, getResources, etc. If unset, the defaults
+ * are the properties os.name, os.arch, and the locale returned
+ * by Locale.getDefault().
+ */
+ public void setDefaults(String os, String arch, Locale locale) {
+ defaultOS = os;
+ defaultArch = arch;
+ defaultLocale = locale;
+ }
+
+
+ /**
+ * Returns whether a locale is matched by one of more other
+ * locales. Only the non-empty language, country, and variant
+ * codes are compared; for example, a requested locale of
+ * Locale("","","") would always return true.
+ *
+ * @param requested the local
+ * @param available the available locales
+ * @return true if requested matches any of available, or if
+ * available is empty or null.
+ */
+ private boolean localMatches(Locale requested, Locale available[]) {
+ if (available == null || available.length == 0)
+ return true;
+
+ for (int i=0; i < available.length; i++) {
+ String language = requested.getLanguage(); // "" but never null
+ String country = requested.getCountry();
+ String variant = requested.getVariant();
+
+ if (!"".equals(language) && !language.equalsIgnoreCase(available[i].getLanguage()))
+ continue;
+ if (!"".equals(country) && !country.equalsIgnoreCase(available[i].getCountry()))
+ continue;
+ if (!"".equals(variant) && !variant.equalsIgnoreCase(available[i].getVariant()))
+ continue;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the string is a prefix for any of the strings
+ * in the specified array.
+ *
+ * @param prefixStr the prefix string
+ * @param available the strings to test
+ * @return true if prefixStr is a prefix of any strings in
+ * available, or if available is empty or null.
+ */
+ private boolean stringMatches(String prefixStr, String available[]) {
+ if (available == null || available.length == 0)
+ return true;
+
+ for (int i=0; i < available.length; i++)
+ if (available[i] != null && available[i].startsWith(prefixStr))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Initialize the JNLPFile fields. Private because it's called
+ * from the constructor.
+ *
+ * @param root the root node
+ * @param strict whether to enforce the spec when
+ * @param location the file location or null
+ */
+ private void parse(Node root, boolean strict, URL location) throws ParseException {
+ try {
+ //if (location != null)
+ // location = new URL(location, "."); // remove filename
+
+ Parser parser = new Parser(this, location, root, strict, true); // true == allow extensions
+
+ // JNLP tag information
+ specVersion = parser.getSpecVersion();
+ fileVersion = parser.getFileVersion();
+ codeBase = parser.getCodeBase();
+ sourceLocation = parser.getFileLocation() != null ? parser.getFileLocation() : location;
+ info = parser.getInfo(root);
+ update = parser.getUpdate(root);
+ resources = parser.getResources(root, false); // false == not a j2se/java resources section
+ launchType = parser.getLauncher(root);
+ security = parser.getSecurity(root);
+ }
+ catch (ParseException ex) {
+ throw ex;
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ throw new RuntimeException(ex.toString());
+ }
+ }
+
+ /**
+ *
+ * @return true if the JNLP file specifies things that can only be
+ * applied on a new vm (eg: different max heap memory)
+ */
+ public boolean needsNewVM() {
+
+ if (getNewVMArgs().size() == 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @return a list of args to pass to the new
+ * JVM based on this JNLP file
+ */
+ public List<String> getNewVMArgs() {
+
+ List<String> newVMArgs = new LinkedList<String>();
+
+ JREDesc[] jres = getResources().getJREs();
+ for (int jreIndex = 0; jreIndex < jres.length; jreIndex++) {
+ String initialHeapSize = jres[jreIndex].getInitialHeapSize();
+ if (initialHeapSize != null) {
+ newVMArgs.add("-Xms" + initialHeapSize);
+ }
+
+ String maxHeapSize = jres[jreIndex].getMaximumHeapSize();
+ if (maxHeapSize != null) {
+ newVMArgs.add("-Xmx" + maxHeapSize);
+ }
+
+ String vmArgsFromJre = jres[jreIndex].getVMArgs();
+ if (vmArgsFromJre != null) {
+ String[] args = vmArgsFromJre.split(" ");
+ newVMArgs.addAll(Arrays.asList(args));
+ }
+ }
+
+ return newVMArgs;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/JNLPSplashScreen.java b/netx/net/sourceforge/jnlp/JNLPSplashScreen.java
new file mode 100644
index 0000000..2e9134b
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/JNLPSplashScreen.java
@@ -0,0 +1,99 @@
+package net.sourceforge.jnlp;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+import javax.swing.JFrame;
+
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+public class JNLPSplashScreen extends JFrame {
+
+ String applicationTitle;
+ String applicationVendor;
+
+ ResourceTracker resourceTracker;
+
+ URL splashImageUrl;
+ Image splashImage;
+
+ public JNLPSplashScreen(ResourceTracker resourceTracker,
+ String applicationTitle, String applicationVendor) {
+
+ // If the JNLP file does not contain any icon images, the splash image
+ // will consist of the application's title and vendor, as taken from the
+ // JNLP file.
+
+ this.resourceTracker = resourceTracker;
+ this.applicationTitle = applicationTitle;
+ this.applicationVendor = applicationVendor;
+
+ }
+
+ public void setSplashImageURL(URL url) {
+ splashImageUrl = url;
+ splashImage = null;
+ try {
+ splashImage = ImageIO.read(resourceTracker
+ .getCacheFile(splashImageUrl));
+ if (splashImage == null) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Error loading splash image: " + url);
+ }
+ return;
+ }
+ } catch (IOException e) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Error loading splash image: " + url);
+ }
+ splashImage = null;
+ return;
+ } catch (IllegalArgumentException argumentException) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Error loading splash image: " + url);
+ }
+ splashImage = null;
+ return;
+ }
+
+ correctSize();
+ }
+
+ public boolean isSplashScreenValid() {
+ return (splashImage != null);
+ }
+
+ private void correctSize() {
+
+ Insets insets = getInsets();
+ int minimumWidth = splashImage.getWidth(null) + insets.left
+ + insets.right;
+ int minimumHeight = splashImage.getHeight(null) + insets.top
+ + insets.bottom;
+ setMinimumSize(new Dimension(minimumWidth, minimumHeight));
+
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ setLocation((screenSize.width - minimumWidth) / 2,
+ (screenSize.height - minimumHeight) / 2);
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ if (splashImage == null) {
+ return;
+ }
+
+ correctSize();
+ Graphics2D g2 = (Graphics2D) g;
+ g2.drawImage(splashImage, getInsets().left, getInsets().top, null);
+
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/JREDesc.java b/netx/net/sourceforge/jnlp/JREDesc.java
new file mode 100644
index 0000000..69a4c25
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/JREDesc.java
@@ -0,0 +1,165 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * The J2SE/Java element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public class JREDesc {
+
+ /** the platform version or the product version if location is not null */
+ private Version version;
+
+ /** the location of a JRE product or null */
+ private URL location;
+
+ /** inital heap size */
+ private String initialHeapSize;
+
+ /** maximum head size */
+ private String maximumHeapSize;
+
+ /** args to pass to the vm */
+ private String vmArgs;
+
+ /** list of ResourceDesc objects */
+ private List resources;
+
+
+ /**
+ * Create a JRE descriptor.
+ *
+ * @param version the platform version or the product version
+ * if location is not null
+ * @param location the location of a JRE product or null
+ * @param initialHeapSize inital heap size
+ * @param maximumHeadSize maximum head size
+ * @param resources list of ResourceDesc objects
+ */
+ public JREDesc(Version version, URL location,
+ String vmArgs, String initialHeapSize,
+ String maximumHeapSize, List resources) throws ParseException {
+ this.version = version;
+ this.location = location;
+ this.vmArgs = vmArgs;
+ checkHeapSize(initialHeapSize);
+ this.initialHeapSize = initialHeapSize;
+ checkHeapSize(maximumHeapSize);
+ this.maximumHeapSize = maximumHeapSize;
+ this.resources = resources;
+ }
+
+ /**
+ * Returns the JRE version. Use isPlatformVersion to
+ * determine if this version corresponds to a platform or
+ * product version.
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns true if the JRE version is a Java platform version
+ * (java.specification.version property) or false if it is a
+ * product version (java.version property).
+ */
+ public boolean isPlatformVersion() {
+ return getLocation() == null;
+ }
+
+ /**
+ * Returns the JRE version string.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the maximum heap size in bytes.
+ */
+ public String getMaximumHeapSize() {
+ return maximumHeapSize;
+ }
+
+ /**
+ * Returns the initial heap size in bytes.
+ */
+ public String getInitialHeapSize() {
+ return initialHeapSize;
+ }
+
+ /**
+ * Returns the resources defined for this JRE.
+ */
+ public List getResourcesDesc() {
+ return resources;
+ }
+
+ /**
+ * Returns the additional arguments to pass to the Java VM
+ * Can be null
+ */
+ public String getVMArgs() {
+ return vmArgs;
+ }
+
+ /**
+ * Check for valid heap size string
+ * @throws ParseException if heapSize is invalid
+ */
+ static private void checkHeapSize(String heapSize) throws ParseException {
+ // need to implement for completeness even though not used in netx
+ if (heapSize == null) {
+ return;
+ }
+
+ boolean lastCharacterIsDigit = true;
+ // the last character must be 0-9 or k/K/m/M
+ char lastChar = Character.toLowerCase(heapSize.charAt(heapSize.length()-1));
+ if ((lastChar < '0' || lastChar > '9')) {
+ lastCharacterIsDigit = false;
+ if (lastChar != 'k' && lastChar!= 'm' ) {
+ throw new ParseException(JNLPRuntime.getMessage("PBadHeapSize",new Object[] {heapSize}));
+ }
+ }
+
+ int indexOfLastDigit = heapSize.length() - 1;
+ if (!lastCharacterIsDigit) {
+ indexOfLastDigit = indexOfLastDigit - 1;
+ }
+
+ String size = heapSize.substring(0,indexOfLastDigit);
+ try {
+ // check that the number is a number!
+ Integer.valueOf(size);
+ } catch (NumberFormatException numberFormat) {
+ throw new ParseException(JNLPRuntime.getMessage("PBadHeapSize", new Object[] {heapSize}), numberFormat);
+ }
+
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/LaunchException.java b/netx/net/sourceforge/jnlp/LaunchException.java
new file mode 100644
index 0000000..2ddd0ad
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/LaunchException.java
@@ -0,0 +1,188 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.util.*;
+
+/**
+ * Thrown when a JNLP application, applet, or installer could not
+ * be created.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public class LaunchException extends Exception {
+
+ /** the original exception */
+ private Throwable cause = null;
+
+ /** the file being launched */
+ private JNLPFile file;
+
+ /** the category of the exception */
+ private String category;
+
+ /** summary */
+ private String summary;
+
+ /** description of the action that was taking place */
+ private String description;
+
+ /** severity of the warning/error */
+ private String severity;
+
+
+ /**
+ * Creates a LaunchException without detail message.
+ */
+ public LaunchException(JNLPFile file, Exception cause, String severity, String category, String summary, String description) {
+ super(severity + ": " + category + ": "+ summary);
+
+ this.file = file;
+ this.category = category;
+ this.summary = summary;
+ this.description = description;
+ this.severity = severity;
+
+ // replace with setCause when no longer 1.3 compatible
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a LaunchException with a cause.
+ */
+ public LaunchException(Throwable cause) {
+ this(cause.getMessage());
+
+ // replace with setCause when no longer 1.3 compatible
+ this.cause = cause;
+ }
+
+ /**
+ * Creates a LaunchException with a cause and detail message
+ */
+ public LaunchException(String message, Throwable cause) {
+ this(message+": "+cause.getMessage());
+
+ // replace with setCause when no longer 1.3 compatible
+ this.cause = cause;
+ }
+
+ /**
+ * Constructs a LaunchException with the specified detail
+ * message.
+ *
+ * @param message the detail message
+ */
+ public LaunchException(String message) {
+ super(message);
+ }
+
+ /**
+ * Returns the JNLPFile being launched.
+ */
+ public JNLPFile getFile() {
+ return file;
+ }
+
+ /**
+ * Returns the category string, a short description of the
+ * exception suitable for displaying in a window title.
+ */
+ public String getCategory() {
+ return category;
+ }
+
+ /**
+ * Returns a one-sentence summary of the problem.
+ */
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * Return a description of the exception and the action being
+ * performed when the exception occurred.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns a short description of the severity of the problem.
+ */
+ public String getSeverity() {
+ return severity;
+ }
+
+ /**
+ * Return the cause of the launch exception or null if there
+ * is no cause exception.
+ */
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /**
+ * Returns the causes for this exception. This method is
+ * useful on JRE 1.3 since getCause is not a standard method,
+ * and will be removed once netx no longer supports 1.3.
+ */
+ public Throwable[] getCauses() {
+ ArrayList result = new ArrayList();
+
+ Reflect r = new Reflect();
+ Throwable cause = this.cause;
+
+ while (cause != null) {
+ result.add(cause);
+ cause = (Throwable) r.invoke(cause, "getCause");
+ }
+
+ return (Throwable[]) result.toArray(new Throwable[0]);
+ }
+
+ /**
+ * Print the stack trace and the cause exception (1.3
+ * compatible)
+ */
+ public void printStackTrace(PrintStream stream) {
+ super.printStackTrace(stream);
+
+ if (cause != null) {
+ stream.println("Caused by: ");
+ cause.printStackTrace(stream);
+ }
+ }
+
+ /**
+ * Print the stack trace and the cause exception (1.3
+ * compatible)
+ */
+ public void printStackTrace(PrintWriter stream) {
+ super.printStackTrace(stream);
+
+ if (cause != null) {
+ stream.println("Caused by: ");
+ cause.printStackTrace(stream);
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/LaunchHandler.java b/netx/net/sourceforge/jnlp/LaunchHandler.java
new file mode 100644
index 0000000..5176fb0
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/LaunchHandler.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * This optional interface is used to handle conditions that occur
+ * while launching JNLP applications, applets, and installers.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public interface LaunchHandler {
+
+ /**
+ * Called when the application could not be launched due to a
+ * fatal error, such as the inability to find the main class or
+ * non-parseable XML.
+ */
+ public void launchError(LaunchException exception);
+
+ /**
+ * Called when launching the application can not be launched due
+ * to an error that is not fatal. For example a JNLP file that
+ * is not strictly correct yet does not necessarily prohibit the
+ * system from attempting to launch the application.
+ *
+ * @return true if the launch should continue, false to abort
+ */
+ public boolean launchWarning(LaunchException warning);
+
+ /**
+ * Called when a security validation error occurs while
+ * launching the application.
+ *
+ * @return true to allow the application to continue, false to stop it.
+ */
+ public boolean validationError(LaunchException security);
+ // this method will probably be replaced when real security
+ // controller is in place.
+
+ /**
+ * Called when an application, applet, or installer has been
+ * launched successfully (the main method or applet start method
+ * returned normally).
+ *
+ * @param application the launched application instance
+ */
+ public void launchCompleted(ApplicationInstance application);
+
+}
diff --git a/netx/net/sourceforge/jnlp/Launcher.java b/netx/net/sourceforge/jnlp/Launcher.java
new file mode 100644
index 0000000..fbd8a60
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/Launcher.java
@@ -0,0 +1,883 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.applet.Applet;
+import java.awt.Container;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.AppThreadGroup;
+import net.sourceforge.jnlp.runtime.AppletInstance;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.runtime.JNLPClassLoader;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.services.InstanceExistsException;
+import net.sourceforge.jnlp.services.ServiceUtil;
+import net.sourceforge.jnlp.util.Reflect;
+
+import javax.swing.SwingUtilities;
+
+import sun.awt.SunToolkit;
+
+/**
+ * Launches JNLPFiles either in the foreground or background.<p>
+ *
+ * An optional LaunchHandler can be specified that is notified of
+ * warning and error condition while launching and that indicates
+ * whether a launch may proceed after a warning has occurred. If
+ * specified, the LaunchHandler is notified regardless of whether
+ * the file is launched in the foreground or background.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.22 $
+ */
+public class Launcher {
+
+ // defines class Launcher.BgRunner, Launcher.TgThread
+
+ /** shortcut for resources */
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+
+ /** shared thread group */
+ /*package*/ static final ThreadGroup mainGroup = new ThreadGroup(R("LAllThreadGroup"));
+
+ /** the handler */
+ private LaunchHandler handler = null;
+
+ /** the update policy */
+ private UpdatePolicy updatePolicy = JNLPRuntime.getDefaultUpdatePolicy();
+
+ /** whether to create an AppContext (if possible) */
+ private boolean context = true;
+
+ /** If the application should call System.exit on fatal errors */
+ private boolean exitOnFailure = true;
+
+ /** a lock which is held to indicate that an instance of netx is running */
+ private FileLock fileLock;
+
+ /**
+ * Create a launcher with the runtime's default update policy
+ * and launch handler.
+ */
+ public Launcher() {
+ this(null, null);
+
+ if (handler == null)
+ handler = JNLPRuntime.getDefaultLaunchHandler();
+ }
+
+ /**
+ * Create a launcher with the runtime's default update policy
+ * and launch handler.
+ *
+ * @param exitOnError Exit if there is an error (usually default, but false when being used from the plugin)
+ */
+ public Launcher(boolean exitOnFailure) {
+ this(null, null);
+
+ if (handler == null)
+ handler = JNLPRuntime.getDefaultLaunchHandler();
+
+ this.exitOnFailure = exitOnFailure;
+ }
+
+ /**
+ * Create a launcher with the specified handler and the
+ * runtime's default update policy.
+ *
+ * @param handler the handler to use or null for no handler.
+ */
+ public Launcher(LaunchHandler handler) {
+ this(handler, null);
+ }
+
+ /**
+ * Create a launcher with an optional handler using the
+ * specified update policy and launch handler.
+ *
+ * @param handler the handler to use or null for no handler.
+ * @param policy the update policy to use or null for default policy.
+ */
+ public Launcher(LaunchHandler handler, UpdatePolicy policy) {
+ if (policy == null)
+ policy = JNLPRuntime.getDefaultUpdatePolicy();
+
+ this.handler = handler;
+ this.updatePolicy = policy;
+
+ }
+
+ /**
+ * Sets the update policy used by launched applications.
+ */
+ public void setUpdatePolicy(UpdatePolicy policy) {
+ if (policy == null)
+ throw new IllegalArgumentException(R("LNullUpdatePolicy"));
+
+ this.updatePolicy = policy;
+ }
+
+ /**
+ * Returns the update policy used when launching applications.
+ */
+ public UpdatePolicy getUpdatePolicy() {
+ return updatePolicy;
+ }
+
+ /**
+ * Sets whether to launch the application in a new AppContext
+ * (a separate event queue, look and feel, etc). If the
+ * sun.awt.SunToolkit class is not present then this method
+ * has no effect. The default value is true.
+ */
+ public void setCreateAppContext(boolean context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns whether applications are launched in their own
+ * AppContext.
+ */
+ public boolean isCreateAppContext() {
+ return this.context;
+ }
+
+ /**
+ * Launches a JNLP file by calling the launch method for the
+ * appropriate file type. The application will be started in
+ * a new window.
+ *
+ * @param file the JNLP file to launch
+ * @return the application instance
+ * @throws LaunchException if an error occurred while launching (also sent to handler)
+ */
+ public ApplicationInstance launch(JNLPFile file) throws LaunchException {
+ return launch(file, null);
+ }
+
+ /**
+ * Launches a JNLP file inside the given container if it is an applet. Specifying a
+ * container has no effect for Applcations and Installers.
+ *
+ * @param file the JNLP file to launch
+ * @param cont the container in which to place the application, if it is an applet
+ * @return the application instance
+ * @throws LaunchException if an error occurred while launching (also sent to handler)
+ */
+ public ApplicationInstance launch(JNLPFile file, Container cont) throws LaunchException {
+ TgThread tg;
+
+ //First checks whether offline-allowed tag is specified inside the jnlp
+ //file.
+ if (!file.getInformation().isOfflineAllowed()) {
+ try {
+ //Checks the offline/online status of the system.
+ //If system is offline do not launch.
+ InetAddress.getByName(file.getSourceLocation().getHost());
+
+ } catch (UnknownHostException ue) {
+ System.err.println("File cannot be launched because offline-allowed tag not specified and system currently offline.");
+ return null;
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ if (file instanceof PluginBridge && cont != null)
+ tg = new TgThread(file, cont, true);
+ else if (cont == null)
+ tg = new TgThread(file);
+ else
+ tg = new TgThread(file, cont);
+
+ tg.start();
+
+ try {
+ tg.join();
+ }
+ catch (InterruptedException ex) {
+ //By default, null is thrown here, and the message dialog is shown.
+ throw launchWarning(new LaunchException(file, ex, R("LSMinor"), R("LCSystem"), R("LThreadInterrupted"), R("LThreadInterruptedInfo")));
+ }
+
+ if (tg.getException() != null)
+ throw tg.getException(); // passed to handler when first created
+
+ if (handler != null)
+ handler.launchCompleted(tg.getApplication());
+
+ return tg.getApplication();
+ }
+
+ /**
+ * Launches a JNLP file by calling the launch method for the
+ * appropriate file type.
+ *
+ * @param location the URL of the JNLP file to launch
+ * @throws LaunchException if there was an exception
+ * @return the application instance
+ */
+ public ApplicationInstance launch(URL location) throws LaunchException {
+ return launch(toFile(location));
+ }
+
+ /**
+ * Launches a JNLP file by calling the launch method for the
+ * appropriate file type in a different thread.
+ *
+ * @param file the JNLP file to launch
+ */
+ public void launchBackground(JNLPFile file) {
+ BgRunner runner = new BgRunner(file, null);
+ new Thread(runner).start();
+ }
+
+ /**
+ * Launches the JNLP file at the specified location in the
+ * background by calling the launch method for its file type.
+ *
+ * @param location the location of the JNLP file
+ */
+ public void launchBackground(URL location) {
+ BgRunner runner = new BgRunner(null, location);
+ new Thread(runner).start();
+ }
+
+ /**
+ * Launches the JNLP file in a new JVM instance. The launched
+ * application's output is sent to the system out and it's
+ * standard input channel is closed.
+ *
+ * @param vmArgs the arguments to pass to the new JVM. Can be empty but
+ * must not be null.
+ * @param file the JNLP file to launch
+ * @param javawsArgs the arguments to pass to the javaws command. Can be
+ * an empty list but must not be null.
+ * @throws LaunchException if there was an exception
+ */
+ public void launchExternal(List<String> vmArgs, JNLPFile file, List<String> javawsArgs) throws LaunchException {
+ List<String> updatedArgs = new LinkedList<String>(javawsArgs);
+
+ if (file.getFileLocation() != null)
+ updatedArgs.add(file.getFileLocation().toString());
+ else if (file.getSourceLocation() != null)
+ updatedArgs.add(file.getFileLocation().toString());
+ else
+ launchError(new LaunchException(file, null, R("LSFatal"), R("LCExternalLaunch"), R("LNullLocation"), R("LNullLocationInfo")));
+
+ launchExternal(vmArgs, updatedArgs);
+
+ }
+
+ /**
+ * Launches the JNLP file in a new JVM instance. The launched
+ * application's output is sent to the system out and it's
+ * standard input channel is closed.
+ *
+ * @param url the URL of the JNLP file to launch
+ * @throws LaunchException if there was an exception
+ */
+ public void launchExternal(URL url) throws LaunchException {
+ List<String> javawsArgs = new LinkedList<String>();
+ javawsArgs.add(url.toString());
+ launchExternal(new LinkedList<String>(), javawsArgs);
+ }
+
+ /**
+ * Launches the JNLP file at the specified location in a new JVM
+ * instance. The launched application's output is sent to the
+ * system out and it's standard input channel is closed.
+ * @param vmArgs the arguments to pass to the jvm
+ * @param javawsArgs the arguments to pass to javaws (aka Netx)
+ * @throws LaunchException if there was an exception
+ */
+ public void launchExternal(List<String> vmArgs, List<String> javawsArgs) throws LaunchException {
+ try {
+
+ List<String> commands = new LinkedList<String>();
+
+ String pathToWebstartBinary = System.getProperty("java.home") +
+ File.separatorChar +
+ "bin" +
+ File.separatorChar +
+ "javaws";
+ commands.add(pathToWebstartBinary);
+ // use -Jargument format to pass arguments to the JVM through the launcher
+ for (String arg: vmArgs) {
+ commands.add("-J" + arg);
+ }
+ commands.addAll(javawsArgs);
+
+ String[] command = commands.toArray(new String[] {});
+
+ Process p = Runtime.getRuntime().exec(command);
+ new StreamEater(p.getErrorStream()).start();
+ new StreamEater(p.getInputStream()).start();
+ p.getOutputStream().close();
+
+ }
+ catch (NullPointerException ex) {
+ throw launchError(new LaunchException(null, null, R("LSFatal"), R("LCExternalLaunch"), R("LNetxJarMissing"), R("LNetxJarMissingInfo")));
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(null, ex, R("LSFatal"), R("LCExternalLaunch"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")));
+ }
+ }
+
+ /**
+ * Returns the JNLPFile for the URL, with error handling.
+ */
+ private JNLPFile toFile(URL location) throws LaunchException {
+ try {
+ JNLPFile file = null;
+
+ try {
+ file = new JNLPFile(location, (Version) null, true, updatePolicy); // strict
+ }
+ catch (ParseException ex) {
+ file = new JNLPFile(location, (Version) null, false, updatePolicy);
+
+ // only here if strict failed but lax did not fail
+ LaunchException lex =
+ launchWarning(new LaunchException(file, ex, R("LSMinor"), R("LCFileFormat"), R("LNotToSpec"), R("LNotToSpecInfo")));
+
+ if (lex != null)
+ throw lex;
+ }
+
+ return file;
+ }
+ catch (Exception ex) {
+ if (ex instanceof LaunchException)
+ throw (LaunchException) ex; // already sent to handler when first thrown
+ else // IO and Parse
+ throw launchError(new LaunchException(null, ex, R("LSFatal"), R("LCReadError"), R("LCantRead"), R("LCantReadInfo")));
+ }
+ }
+
+ /**
+ * Launches a JNLP application. This method should be called
+ * from a thread in the application's thread group.
+ */
+ protected ApplicationInstance launchApplication(JNLPFile file) throws LaunchException {
+ if (!file.isApplication())
+ throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplication"), R("LNotApplicationInfo")));
+
+ markNetxRunning();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() { markNetxStopped(); }
+ });
+
+ try {
+
+ try {
+ ServiceUtil.checkExistingSingleInstance(file);
+ } catch (InstanceExistsException e) {
+ return null;
+ }
+
+ if (JNLPRuntime.getForksAllowed() && file.needsNewVM()) {
+ List<String> netxArguments = new LinkedList<String>();
+ netxArguments.add("-Xnofork");
+ netxArguments.addAll(JNLPRuntime.getInitialArguments());
+ launchExternal(file.getNewVMArgs(), netxArguments);
+ return null;
+ }
+
+ final int preferredWidth = 500;
+ final int preferredHeight = 400;
+ JNLPSplashScreen splashScreen = null;
+ URL splashImageURL = file.getInformation().getIconLocation(
+ IconDesc.SPLASH, preferredWidth, preferredHeight);
+ if (splashImageURL != null) {
+ ResourceTracker resourceTracker = new ResourceTracker(true);
+ resourceTracker.addResource(splashImageURL, file.getFileVersion(), updatePolicy);
+ splashScreen = new JNLPSplashScreen(resourceTracker, null, null);
+ splashScreen.setSplashImageURL(splashImageURL);
+ if (splashScreen.isSplashScreenValid()) {
+ splashScreen.setVisible(true);
+ }
+ }
+
+
+ ApplicationInstance app = createApplication(file);
+ app.initialize();
+
+ String mainName = file.getApplication().getMainClass();
+
+ // When the application-desc field is empty, we should take a
+ // look at the main jar for the main class.
+ if (mainName == null) {
+ JARDesc mainJarDesc = file.getResources().getMainJAR();
+ File f = CacheUtil.getCacheFile(mainJarDesc.getLocation(), null);
+ if (f != null) {
+ JarFile mainJar = new JarFile(f);
+ mainName = mainJar.getManifest().
+ getMainAttributes().getValue("Main-Class");
+ }
+ }
+
+ if (mainName == null)
+ throw launchError(new LaunchException(file, null,
+ R("LSFatal"), R("LCClient"), R("LCantDetermineMainClass") ,
+ R("LCantDetermineMainClassInfo")));
+
+ Class mainClass = app.getClassLoader().loadClass(mainName);
+
+ Method main = mainClass.getMethod("main", new Class[] {String[].class} );
+ String args[] = file.getApplication().getArguments();
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ // dummy method to force Event Dispatch Thread creation
+ public void run(){}
+ });
+
+ setContextClassLoaderForAllThreads(app.getThreadGroup(), app.getClassLoader());
+
+ if (splashScreen != null) {
+ if (splashScreen.isSplashScreenValid()) {
+ splashScreen.setVisible(false);
+ }
+ splashScreen.dispose();
+ }
+
+ main.invoke(null, new Object[] { args } );
+
+ return app;
+ }
+ catch (LaunchException lex) {
+ throw launchError(lex);
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")));
+ }
+ }
+
+ /**
+ * Set the classloader as the context classloader for all threads in
+ * the given threadgroup. This is required to make some applications
+ * work. For example, an application that provides a custom Swing LnF
+ * may ask the swing thread to load resources from their JNLP, which
+ * would only work if the Swing thread knows about the JNLPClassLoader.
+ *
+ * @param tg The threadgroup for which the context classloader should be set
+ * @param classLoader the classloader to set as the context classloader
+ */
+ private void setContextClassLoaderForAllThreads(ThreadGroup tg, ClassLoader classLoader) {
+
+ /* be prepared for change in thread size */
+ int threadCountGuess = tg.activeCount();
+ Thread[] threads;
+ do {
+ threadCountGuess = threadCountGuess * 2;
+ threads = new Thread[threadCountGuess];
+ tg.enumerate(threads, true);
+ } while (threads[threadCountGuess-1] != null);
+
+
+ for (Thread thread: threads) {
+ if (thread != null) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Setting " + classLoader + " as the classloader for thread " + thread.getName());
+ }
+ thread.setContextClassLoader(classLoader);
+ }
+ }
+
+ }
+
+ /**
+ * Launches a JNLP applet. This method should be called from a
+ * thread in the application's thread group.<p>
+ *
+ * The enableCodeBase parameter adds the applet's codebase to
+ * the locations searched for resources and classes. This can
+ * slow down the applet loading but allows browser-style applets
+ * that don't use JAR files exclusively to be run from a applet
+ * JNLP file. If the applet JNLP file does not specify any
+ * resources then the code base will be enabled regardless of
+ * the specified value.<p>
+ *
+ * @param file the JNLP file
+ * @param enableCodeBase whether to add the codebase URL to the classloader
+ */
+ protected ApplicationInstance launchApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException {
+ if (!file.isApplet())
+ throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo")));
+
+ try {
+ AppletInstance applet = createApplet(file, enableCodeBase, cont);
+ applet.initialize();
+
+ applet.getAppletEnvironment().startApplet(); // this should be a direct call to applet instance
+ return applet;
+ }
+ catch (LaunchException lex) {
+ throw launchError(lex);
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")));
+ }
+ }
+
+ /**
+ * Gets an ApplicationInstance, but does not launch the applet.
+ */
+ protected ApplicationInstance getApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException {
+ if (!file.isApplet())
+ throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplet"), R("LNotAppletInfo")));
+
+ try {
+ AppletInstance applet = createApplet(file, enableCodeBase, cont);
+ applet.initialize();
+ return applet;
+ }
+ catch (LaunchException lex) {
+ throw launchError(lex);
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCLaunching"), R("LCouldNotLaunch"), R("LCouldNotLaunchInfo")));
+ }
+ }
+
+ /**
+ * Launches a JNLP installer. This method should be called from
+ * a thread in the application's thread group.
+ */
+ protected ApplicationInstance launchInstaller(JNLPFile file) throws LaunchException {
+ throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCNotSupported"), R("LNoInstallers"), R("LNoInstallersInfo")));
+ }
+
+ /**
+ * Create an AppletInstance.
+ *
+ * @param enableCodeBase whether to add the code base URL to the classloader
+ */
+ protected AppletInstance createApplet(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException {
+ try {
+ JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
+
+ if (enableCodeBase || file.getResources().getJARs().length == 0)
+ loader.enableCodeBase();
+
+ AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup();
+
+ String appletName = file.getApplet().getMainClass();
+
+ //Classloader chokes if there's '/' in the path to the main class.
+ //Must replace with '.' instead.
+ appletName = appletName.replace('/', '.');
+ Class appletClass = loader.loadClass(appletName);
+ Applet applet = (Applet) appletClass.newInstance();
+
+ AppletInstance appletInstance;
+ if (cont == null)
+ appletInstance = new AppletInstance(file, group, loader, applet);
+ else
+ appletInstance = new AppletInstance(file, group, loader, applet, cont);
+
+ group.setApplication(appletInstance);
+ loader.setApplication(appletInstance);
+
+ setContextClassLoaderForAllThreads(appletInstance.getThreadGroup(), appletInstance.getClassLoader());
+
+ return appletInstance;
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplet"), R("LInitAppletInfo")));
+ }
+ }
+
+ /**
+ * Creates an Applet object from a JNLPFile. This is mainly to be used with
+ * gcjwebplugin.
+ * @param file the PluginBridge to be used.
+ * @param enableCodeBase whether to add the code base URL to the classloader.
+ */
+ protected Applet createAppletObject(JNLPFile file, boolean enableCodeBase, Container cont) throws LaunchException {
+ try {
+ JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
+
+ if (enableCodeBase || file.getResources().getJARs().length == 0)
+ loader.enableCodeBase();
+
+ String appletName = file.getApplet().getMainClass();
+
+ //Classloader chokes if there's '/' in the path to the main class.
+ //Must replace with '.' instead.
+ appletName = appletName.replace('/', '.');
+ Class appletClass = loader.loadClass(appletName);
+ Applet applet = (Applet) appletClass.newInstance();
+
+ return applet;
+ }
+ catch (Exception ex) {
+ throw launchError(new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplet"), R("LInitAppletInfo")));
+ }
+ }
+
+ /**
+ * Creates an Application.
+ */
+ protected ApplicationInstance createApplication(JNLPFile file) throws LaunchException {
+ try {
+ JNLPClassLoader loader = JNLPClassLoader.getInstance(file, updatePolicy);
+ AppThreadGroup group = (AppThreadGroup) Thread.currentThread().getThreadGroup();
+
+ ApplicationInstance app = new ApplicationInstance(file, group, loader);
+ group.setApplication(app);
+ loader.setApplication(app);
+
+ return app;
+ }
+ catch (Exception ex) {
+ throw new LaunchException(file, ex, R("LSFatal"), R("LCInit"), R("LInitApplication"), R("LInitApplicationInfo"));
+ }
+ }
+
+ /**
+ * Create a thread group for the JNLP file.
+ *
+ * Note: if the JNLPFile is an applet (ie it is a subclass of PluginBridge)
+ * then this method simply returns the existing ThreadGroup. The applet
+ * ThreadGroup has to be created at an earlier point in the applet code.
+ */
+ protected AppThreadGroup createThreadGroup(JNLPFile file) {
+ AppThreadGroup appThreadGroup = null;
+
+ if (file instanceof PluginBridge) {
+ appThreadGroup = (AppThreadGroup) Thread.currentThread().getThreadGroup();
+ } else {
+ appThreadGroup = new AppThreadGroup(mainGroup, file.getTitle());
+ }
+
+ return appThreadGroup;
+ }
+
+ /**
+ * Send n launch error to the handler, if set, and also to the
+ * caller.
+ */
+ private LaunchException launchError(LaunchException ex) {
+ if (handler != null)
+ handler.launchError(ex);
+
+ return ex;
+ }
+
+ /**
+ * Send a launch error to the handler, if set, and to the
+ * caller only if the handler indicated that the launch should
+ * continue despite the warning.
+ *
+ * @return an exception to throw if the launch should be aborted, or null otherwise
+ */
+ private LaunchException launchWarning(LaunchException ex) {
+ if (handler != null)
+ if (!handler.launchWarning(ex))
+ // no need to destroy the app b/c it hasn't started
+ return ex; // chose to abort
+
+ return null; // chose to continue, or no handler
+ }
+
+ /**
+ * Indicate that netx is running by creating the {@link JNLPRuntime#INSTANCE_FILE} and
+ * acquiring a shared lock on it
+ */
+ private void markNetxRunning() {
+ try {
+ String message = "This file is used to check if netx is running";
+
+ File netxRunningFile = new File(JNLPRuntime.NETX_RUNNING_FILE);
+ netxRunningFile.getParentFile().mkdirs();
+ if (netxRunningFile.createNewFile()) {
+ FileOutputStream fos = new FileOutputStream(netxRunningFile);
+ try {
+ fos.write(message.getBytes());
+ } finally {
+ fos.close();
+ }
+ }
+
+ if (!netxRunningFile.isFile()) {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Unable to create instance file");
+ }
+ fileLock = null;
+ return;
+ }
+
+ FileInputStream is = new FileInputStream(netxRunningFile);
+ FileChannel channel = is.getChannel();
+ fileLock = channel.tryLock(0, Long.MAX_VALUE, true);
+ if (fileLock != null && fileLock.isShared()) {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Acquired shared lock on " +
+ JNLPRuntime.NETX_RUNNING_FILE + " to indicate javaws is running");
+ }
+ } else {
+ fileLock = null;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Indicate that netx is stopped by releasing the shared lock on
+ * {@link JNLPRuntime#INSTANCE_FILE}.
+ */
+ private void markNetxStopped() {
+ if (fileLock == null) {
+ return;
+ }
+ try {
+ fileLock.release();
+ fileLock.channel().close();
+ fileLock = null;
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Release shared lock on " + JNLPRuntime.NETX_RUNNING_FILE);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * This runnable is used to call the appropriate launch method
+ * for the application, applet, or installer in its thread group.
+ */
+ private class TgThread extends Thread { // ThreadGroupThread
+ private JNLPFile file;
+ private ApplicationInstance application;
+ private LaunchException exception;
+ private Container cont;
+ private boolean isPlugin = false;
+
+ TgThread(JNLPFile file) {
+ this(file, null);
+ }
+
+ TgThread(JNLPFile file, Container cont) {
+ super(createThreadGroup(file), file.getTitle());
+
+ this.file = file;
+ this.cont = cont;
+ }
+
+ TgThread(JNLPFile file, Container cont, boolean isPlugin) {
+ super(createThreadGroup(file), file.getTitle());
+ this.file = file;
+ this.cont = cont;
+ this.isPlugin = isPlugin;
+ }
+
+ public void run() {
+ try {
+ // Do not create new AppContext if we're using NetX and icedteaplugin.
+ // The plugin needs an AppContext too, but it has to be created earlier.
+ if (context && !isPlugin)
+ SunToolkit.createNewAppContext();
+
+ if (isPlugin) {
+ // Do not display download indicators if we're using gcjwebplugin.
+ JNLPRuntime.setDefaultDownloadIndicator(null);
+ application = getApplet(file, true, cont);
+ } else {
+ if (file.isApplication())
+ application = launchApplication(file);
+ else if (file.isApplet())
+ application = launchApplet(file, true, cont); // enable applet code base
+ else if (file.isInstaller())
+ application = launchInstaller(file);
+ else
+ throw launchError(new LaunchException(file, null,
+ R("LSFatal"), R("LCClient"), R("LNotLaunchable"),
+ R("LNotLaunchableInfo")));
+ }
+ }
+ catch (LaunchException ex) {
+ ex.printStackTrace();
+ exception = ex;
+ // Exit if we can't launch the application.
+ if (exitOnFailure)
+ System.exit(0);
+ }
+ }
+
+ public LaunchException getException() {
+ return exception;
+ }
+
+ public ApplicationInstance getApplication() {
+ return application;
+ }
+
+ };
+
+
+ /**
+ * This runnable is used by the <code>launchBackground</code>
+ * methods to launch a JNLP file from a separate thread.
+ */
+ private class BgRunner implements Runnable {
+ private JNLPFile file;
+ private URL location;
+
+ BgRunner(JNLPFile file, URL location) {
+ this.file = file;
+ this.location = location;
+ }
+
+ public void run() {
+ try {
+ if (file != null)
+ launch(file);
+ if (location != null)
+ launch(location);
+ }
+ catch (LaunchException ex) {
+ // launch method communicates error conditions to the
+ // handler if it exists, otherwise we don't care because
+ // there's nothing that can be done about the exception.
+ }
+ }
+ };
+
+}
diff --git a/netx/net/sourceforge/jnlp/MenuDesc.java b/netx/net/sourceforge/jnlp/MenuDesc.java
new file mode 100644
index 0000000..5f59af4
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/MenuDesc.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+public class MenuDesc {
+
+ /** the submenu for this menu entry */
+ private String subMenu;
+
+ /**
+ * Create a new menu descriptor
+ */
+ public MenuDesc(String subMenu) {
+ this.subMenu = subMenu;
+ }
+
+ /**
+ * Returns the submenu for this menu entry.
+ */
+ public String getSubMenu() {
+ return subMenu;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/NetxPanel.java b/netx/net/sourceforge/jnlp/NetxPanel.java
new file mode 100644
index 0000000..7c6cf3c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/NetxPanel.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ * This file is part of IcedTea, http://icedtea.classpath.org
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package net.sourceforge.jnlp;
+
+import net.sourceforge.jnlp.runtime.AppThreadGroup;
+import net.sourceforge.jnlp.runtime.AppletInstance;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+import java.net.URL;
+import java.util.Hashtable;
+
+import sun.applet.AppletViewerPanel;
+import sun.awt.SunToolkit;
+
+/**
+ * This panel calls into netx to run an applet, and pipes the display
+ * into a panel from gcjwebplugin.
+ *
+ * @author Francis Kung <[email protected]>
+ */
+public class NetxPanel extends AppletViewerPanel
+{
+ private PluginBridge bridge = null;
+ private boolean exitOnFailure = true;
+ private AppletInstance appInst = null;
+ private boolean appletAlive;
+
+ public NetxPanel(URL documentURL, Hashtable atts)
+ {
+ super(documentURL, atts);
+ }
+
+ // overloaded constructor, called when initialized via plugin
+ public NetxPanel(URL documentURL, Hashtable atts, boolean exitOnFailure)
+ {
+ this(documentURL, atts);
+ this.exitOnFailure = exitOnFailure;
+ this.appletAlive = true;
+ }
+
+ @Override
+ public void run() {
+ /*
+ * create an AppContext for this thread associated with this particular
+ * plugin instance (which runs in a different thread group from the rest
+ * of the plugin).
+ */
+ SunToolkit.createNewAppContext();
+
+ super.run();
+ }
+
+ //Overriding to use Netx classloader. You might need to relax visibility
+ //in sun.applet.AppletPanel for runLoader().
+ protected void runLoader() {
+
+ try {
+ bridge = new PluginBridge(baseURL,
+ getDocumentBase(),
+ getJarFiles(),
+ getCode(),
+ getWidth(),
+ getHeight(),
+ atts);
+
+ synchronized(JNLPRuntime.initMutex) {
+ //The custom NetX Policy and SecurityManager are set here.
+ if (!JNLPRuntime.isInitialized()) {
+ if (JNLPRuntime.isDebug())
+ System.out.println("initializing JNLPRuntime...");
+
+ JNLPRuntime.initialize(false);
+ } else {
+ if (JNLPRuntime.isDebug())
+ System.out.println("JNLPRuntime already initialized");
+ }
+ }
+
+ doInit = true;
+ dispatchAppletEvent(APPLET_LOADING, null);
+ status = APPLET_LOAD;
+
+ Launcher l = new Launcher(exitOnFailure);
+
+ try {
+ appInst = (AppletInstance) l.launch(bridge, this);
+ } catch (LaunchException e) {
+ // Assume user has indicated he does not trust the
+ // applet.
+ if (exitOnFailure)
+ System.exit(0);
+ }
+ applet = appInst.getApplet();
+
+ //On the other hand, if you create an applet this way, it'll work
+ //fine. Note that you might to open visibility in sun.applet.AppletPanel
+ //for this to work (the loader field, and getClassLoader).
+ //loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey());
+ //applet = createApplet(loader);
+
+ // This shows that when using NetX's JNLPClassLoader, keyboard input
+ // won't make it to the applet, whereas using sun.applet.AppletClassLoader
+ // works just fine.
+
+ dispatchAppletEvent(APPLET_LOADING_COMPLETED, null);
+
+ if (applet != null)
+ {
+ // Stick it in the frame
+ applet.setStub(this);
+ applet.setVisible(false);
+ add("Center", applet);
+ showAppletStatus("loaded");
+ validate();
+ }
+ } catch (Exception e) {
+ this.appletAlive = false;
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Creates a new Thread (in a new applet-specific ThreadGroup) for running
+ * the applet
+ */
+ // Reminder: Relax visibility in sun.applet.AppletPanel
+ protected synchronized void createAppletThread() {
+ // when this was being done (incorrectly) in Launcher, the call was
+ // new AppThreadGroup(mainGroup, file.getTitle());
+ ThreadGroup tg = new AppThreadGroup(Launcher.mainGroup,
+ this.documentURL.toString());
+ handler = new Thread(tg, this);
+ handler.start();
+ }
+
+ public void updateSizeInAtts(int height, int width) {
+ this.atts.put("height", Integer.toString(height));
+ this.atts.put("width", Integer.toString(width));
+ }
+
+ public ClassLoader getAppletClassLoader() {
+ return appInst.getClassLoader();
+ }
+
+ public boolean isAlive() {
+ return handler != null && handler.isAlive() && this.appletAlive;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/Node.java b/netx/net/sourceforge/jnlp/Node.java
new file mode 100644
index 0000000..0868103
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/Node.java
@@ -0,0 +1,146 @@
+package net.sourceforge.jnlp;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import net.sourceforge.nanoxml.XMLElement;
+
+// this class makes assumptions on how parser calls methods (such
+// as getFirstChild->getNextChild only called by a single loop at
+// a time, so no need for an iterator).
+
+/**
+ * This class converts the NanoXML's XMLElement nodes into the
+ * regular XML Node interface (for the methods used by Parser).
+ */
+/* NANO */
+class Node {
+ private XMLElement xml;
+ private Node next;
+ private Node children[];
+
+ Node(XMLElement xml) {
+ this.xml = xml;
+ }
+
+ Node getFirstChild() {
+ if (children == null)
+ getChildNodes();
+
+ if (children.length == 0)
+ return null;
+ else
+ return children[0];
+ }
+
+ Node getNextSibling() {
+ return next;
+ }
+
+ void normalize() {
+ }
+
+ String getNodeValue() {
+ return xml.getContent();
+ }
+
+ Node[] getChildNodes() {
+ if (children == null) {
+ List list = new ArrayList();
+
+ for (Enumeration e = xml.enumerateChildren(); e.hasMoreElements();)
+ list.add( new Node((XMLElement)e.nextElement()) );
+
+ children = (Node[]) list.toArray( new Node[list.size()] );
+
+ for (int i=0; i < children.length-1; i++)
+ children[i].next = children[i+1];
+ }
+
+ return children;
+ }
+
+ String getAttribute(String name) {
+ return (String)xml.getAttribute(name);
+ }
+
+ String getNodeName() {
+ if (xml.getName() == null)
+ return "";
+ else
+ return xml.getName();
+ }
+
+ public String toString() {
+ return getNodeName();
+ }
+}
+
+/**
+ * This class converts the TinyXML's ParsedXML nodes into the
+ * regular XML Node interface (for the methods used by Parser).
+ */
+/* TINY
+class Node {
+ private ParsedXML tinyNode;
+ private Node next;
+ private Node children[];
+
+ Node(ParsedXML tinyNode) {
+ this.tinyNode = tinyNode;
+ }
+
+ Node getFirstChild() {
+ if (children == null)
+ getChildNodes();
+
+ if (children.length == 0)
+ return null;
+ else
+ return children[0];
+ }
+
+ Node getNextSibling() {
+ return next;
+ }
+
+ void normalize() {
+ }
+
+ String getNodeValue() {
+ return tinyNode.getContent();
+ }
+
+ Node[] getChildNodes() {
+ if (children == null) {
+ List list = new ArrayList();
+
+ for (Enumeration e = tinyNode.elements(); e.hasMoreElements();) {
+ list.add( new Node((ParsedXML)e.nextElement()) );
+ }
+ children = (Node[]) list.toArray( new Node[list.size()] );
+
+ for (int i=0; i < children.length-1; i++)
+ children[i].next = children[i+1];
+ }
+
+ return children;
+ }
+
+ String getAttribute(String name) {
+ return tinyNode.getAttribute(name);
+ }
+
+ String getNodeName() {
+ if (tinyNode.getName() == null)
+ return "";
+ else
+ return tinyNode.getName();
+ }
+
+ public String toString() {
+ return getNodeName();
+ }
+}
+*/
diff --git a/netx/net/sourceforge/jnlp/PackageDesc.java b/netx/net/sourceforge/jnlp/PackageDesc.java
new file mode 100644
index 0000000..22822fb
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/PackageDesc.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The package element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class PackageDesc {
+
+ /** the package name */
+ private String name;
+
+ /** the part required by the package */
+ private String part;
+
+ /** whether the package includes subpackages */
+ private boolean recursive;
+
+
+ /**
+ * Create a package descriptor.
+ *
+ * @param name the package name
+ * @param part the part required by the package
+ * @param recursive whether the package includes subpackages
+ */
+ public PackageDesc(String name, String part, boolean recursive) {
+ this.name = name;
+ this.part = part;
+ this.recursive = recursive;
+ }
+
+ /**
+ * Returns whether the specified class is part of this package.
+ *
+ * @param className the fully qualified class name
+ */
+ public boolean matches(String className) {
+ // form 1: exact class
+ if (name.equals(className))
+ return true;
+
+ // form 2: package.*
+ if (name.endsWith(".*")) {
+ String pkName = name.substring(0, name.length()-1);
+
+ if (className.startsWith(pkName)) {
+ String postfix = className.substring(pkName.length()+1);
+
+ if (recursive || -1 == postfix.indexOf("."))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the package name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the part name.
+ */
+ public String getPart() {
+ return part;
+ }
+
+ /**
+ * Returns whether subpackages should be matched by this
+ * package.
+ */
+ public boolean isRecursive() {
+ return recursive;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ParseException.java b/netx/net/sourceforge/jnlp/ParseException.java
new file mode 100644
index 0000000..9ed86e1
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ParseException.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Thrown to indicate that an error has occurred while parsing a
+ * JNLP file.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+public class ParseException extends Exception {
+
+ // todo: add meaningful information, such as the invalid
+ // element, parse position, etc.
+
+ /** the original exception */
+ private Throwable cause = null;
+
+
+ /**
+ * Create a parse exception with the specified message.
+ */
+ public ParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a parse exception with the specified message and
+ * cause.
+ */
+ public ParseException(String message, Throwable cause) {
+ super(message);
+
+ // replace with setCause when no longer 1.3 compatible
+ this.cause = cause;
+ }
+
+ /**
+ * Return the cause of the launch exception or null if there
+ * is no cause exception.
+ */
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /**
+ * Print the stack trace and the cause exception (1.3
+ * compatible)
+ */
+ public void printStackTrace(PrintStream stream) {
+ super.printStackTrace(stream);
+
+ if (cause != null) {
+ stream.println("Caused by: ");
+ cause.printStackTrace(stream);
+ }
+ }
+
+ /**
+ * Print the stack trace and the cause exception (1.3
+ * compatible)
+ */
+ public void printStackTrace(PrintWriter stream) {
+ super.printStackTrace(stream);
+
+ if (cause != null) {
+ stream.println("Caused by: ");
+ cause.printStackTrace(stream);
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/Parser.java b/netx/net/sourceforge/jnlp/Parser.java
new file mode 100644
index 0000000..9c44f69
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/Parser.java
@@ -0,0 +1,1320 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+//import javax.xml.parsers.*; // commented to use right Node
+//import org.w3c.dom.*; // class for using Tiny XML | NanoXML
+//import org.xml.sax.*;
+//import gd.xml.tiny.*;
+import net.sourceforge.jnlp.UpdateDesc.Check;
+import net.sourceforge.jnlp.UpdateDesc.Policy;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.nanoxml.*;
+
+
+/**
+ * Contains methods to parse an XML document into a JNLPFile.
+ * Implements JNLP specification version 1.0.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.13 $
+ */
+class Parser {
+
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+ private static String R(String key, Object p1) { return R(key, p1, null); }
+ private static String R(String key, Object p1, Object p2) { return R(key, p1, p2, null); }
+ private static String R(String key, Object p1, Object p2, Object p3) { return JNLPRuntime.getMessage(key, new Object[] { p1, p2, p3 }); }
+
+
+ // defines netx.jnlp.Node class if using Tiny XML or Nano XML
+
+ // Currently uses the Nano XML parse. Search for "SAX" or
+ // "TINY" or "NANO" and uncomment those blocks and comment the
+ // active ones (if any) to switch XML parsers. Also
+ // (un)comment appropriate Node class at end of this file and
+ // do a clean build.
+
+ /**
+ * Ensure consistent error handling.
+ */
+ /* SAX
+ static ErrorHandler errorHandler = new ErrorHandler() {
+ public void error(SAXParseException exception) throws SAXParseException {
+ //throw exception;
+ }
+ public void fatalError(SAXParseException exception) throws SAXParseException {
+ //throw exception;
+ }
+ public void warning(SAXParseException exception) {
+ System.err.println("XML parse warning:");
+ exception.printStackTrace();
+ }
+ };
+ */
+
+
+ /** the supported JNLP file versions */
+ private static Version supportedVersions = new Version("1.0 1.5 1.6 6.0");
+
+ // fix: some descriptors need to use the jnlp file at a later
+ // date and having file ref lets us pass it to their
+ // constructors
+ //
+ /** the file reference */
+ private JNLPFile file; // do not use (uninitialized)
+
+ /** the root node */
+ private Node root;
+
+ /** the specification version */
+ private Version spec;
+
+ /** the base URL that all hrefs are relative to */
+ private URL base;
+
+ /** the codebase URL */
+ private URL codebase;
+
+ /** the file URL */
+ private URL fileLocation;
+
+ /** whether to throw errors on non-fatal errors. */
+ private boolean strict; // if strict==true parses a file with no error then strict==false should also
+
+ /** whether to allow extensions to the JNLP specification */
+ private boolean allowExtensions; // true if extensions to JNLP spec are ok
+
+
+ /**
+ * Create a parser for the JNLP file. If the location
+ * parameters is not null it is used as the default codebase
+ * (does not override value of jnlp element's href
+ * attribute).<p>
+ *
+ * The root node may be normalized as a side effect of this
+ * constructor.
+ *
+ * @param file the (uninitialized) file reference
+ * @param base if codebase is not specified, a default base for relative URLs
+ * @param root the root node
+ * @param strict whether to enforce strict compliance with the JNLP spec
+ * @param allowExtensions whether to allow extensions to the JNLP spec
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public Parser(JNLPFile file, URL base, Node root, boolean strict, boolean allowExtensions) throws ParseException {
+ this.file = file;
+ this.root = root;
+ this.strict = strict;
+ this.allowExtensions = allowExtensions;
+
+ // ensure it's a JNLP node
+ if (root == null || !root.getNodeName().equals("jnlp"))
+ throw new ParseException(R("PInvalidRoot"));
+
+ // JNLP tag information
+ this.spec = getVersion(root, "spec", "1.0+");
+ this.codebase = addSlash(getURL(root, "codebase", base));
+ this.base = (codebase!=null) ? codebase : base; // if codebase not specified use default codebase
+ fileLocation = getURL(root, "href", this.base);
+
+ // ensure version is supported
+ if (!supportedVersions.matchesAny(spec))
+ throw new ParseException(R("PSpecUnsupported", supportedVersions));
+
+ // normalize the text nodes
+ root.normalize();
+ }
+
+ /**
+ * Return the JNLP specification versions supported.
+ */
+ public static Version getSupportedVersions() {
+ return supportedVersions;
+ }
+
+ /**
+ * Returns the file version.
+ */
+ public Version getFileVersion() {
+ return getVersion(root, "version", null);
+ }
+
+ /**
+ * Returns the file location.
+ */
+ public URL getFileLocation() {
+ return fileLocation;
+ }
+
+ /**
+ * Returns the codebase.
+ */
+ public URL getCodeBase() {
+ return codebase;
+ }
+
+ /**
+ * Returns the specification version.
+ */
+ public Version getSpecVersion() {
+ return spec;
+ }
+
+ public UpdateDesc getUpdate(Node parent) throws ParseException {
+ UpdateDesc updateDesc = null;
+ Node child = parent.getFirstChild();
+ while (child != null) {
+ if (child.getNodeName().equals("update")) {
+ if (strict && updateDesc != null) {
+ throw new ParseException(R("PTwoUpdates"));
+ }
+
+ Node node = child;
+
+ Check check;
+ String checkValue = getAttribute(node, "check", "timeout");
+ if (checkValue.equals("always")) {
+ check = Check.ALWAYS;
+ } else if (checkValue.equals("timeout")) {
+ check = Check.TIMEOUT;
+ } else if (checkValue.equals("background")) {
+ check = Check.BACKGROUND;
+ } else {
+ check = Check.TIMEOUT;
+ }
+
+ String policyString = getAttribute(node, "policy", "always");
+ Policy policy;
+ if (policyString.equals("always")) {
+ policy = Policy.ALWAYS;
+ } else if (policyString.equals("prompt-update")) {
+ policy = Policy.PROMPT_UPDATE;
+ } else if (policyString.equals("prompt-run")) {
+ policy = Policy.PROMPT_RUN;
+ } else {
+ policy = Policy.ALWAYS;
+ }
+
+ updateDesc = new UpdateDesc(check, policy);
+ }
+
+ child = child.getNextSibling();
+ }
+
+ if (updateDesc == null) {
+ updateDesc = new UpdateDesc(Check.TIMEOUT, Policy.ALWAYS);
+ }
+ return updateDesc;
+ }
+
+ //
+ // This section loads the resources elements
+ //
+
+ /**
+ * Returns all of the ResourcesDesc elements under the specified
+ * node (jnlp or j2se).
+ *
+ * @param parent the parent node (either jnlp or j2se)
+ * @param j2se true if the resources are located under a j2se or java node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public List getResources(Node parent, boolean j2se) throws ParseException {
+ List result = new ArrayList();
+ Node resources[] = getChildNodes(parent, "resources");
+
+ // ensure that there are at least one information section present
+ if (resources.length == 0 && !j2se)
+ throw new ParseException(R("PNoResources"));
+
+ // create objects from the resources sections
+ for (int i=0; i < resources.length; i++)
+ result.add(getResourcesDesc(resources[i], j2se));
+
+ return result;
+ }
+
+ /**
+ * Returns the ResourcesDesc element at the specified node.
+ *
+ * @param node the resources node
+ * @param j2se true if the resources are located under a j2se or java node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public ResourcesDesc getResourcesDesc(Node node, boolean j2se) throws ParseException {
+ boolean mainFlag = false; // if found a main tag
+
+ // create resources
+ ResourcesDesc resources =
+ new ResourcesDesc(file,
+ getLocales(node),
+ splitString(getAttribute(node, "os", null)),
+ splitString(getAttribute(node, "arch", null)));
+
+ // step through the elements
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String name = child.getNodeName();
+
+ // check for nativelib but no trusted environment
+ if ("nativelib".equals(name))
+ if (!isTrustedEnvironment())
+ throw new ParseException(R("PUntrustedNative"));
+
+ if ("j2se".equals(name) || "java".equals(name)) {
+ if (getChildNode(root, "component-desc") != null)
+ if (strict)
+ throw new ParseException(R("PExtensionHasJ2SE"));
+ if (!j2se)
+ resources.addResource( getJRE(child) );
+ else
+ throw new ParseException(R("PInnerJ2SE"));
+ }
+
+ if ("jar".equals(name) || "nativelib".equals(name)) {
+ JARDesc jar = getJAR(child);
+
+ // check for duplicate main entries
+ if (jar.isMain()) {
+ if (mainFlag == true)
+ if (strict)
+ throw new ParseException(R("PTwoMains"));
+ mainFlag = true;
+ }
+
+ resources.addResource(jar);
+ }
+
+ if ("extension".equals(name))
+ resources.addResource( getExtension(child) );
+
+ if ("property".equals(name))
+ resources.addResource( getProperty(child) );
+
+ if ("package".equals(name))
+ resources.addResource( getPackage(child) );
+
+ child = child.getNextSibling();
+ }
+
+ return resources;
+ }
+
+ /**
+ * Returns the JRE element at the specified node.
+ *
+ * @param node the j2se/java node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public JREDesc getJRE(Node node) throws ParseException {
+ Version version = getVersion(node, "version", null);
+ URL location = getURL(node, "href", base);
+ String vmArgs = getAttribute(node, "java-vm-args",null);
+ try {
+ checkVMArgs(vmArgs);
+ } catch (IllegalArgumentException argumentException) {
+ vmArgs = null;
+ }
+ String initialHeap = getAttribute(node, "initial-heap-size", null);
+ String maxHeap = getAttribute(node, "max-heap-size", null);
+ List resources = getResources(node, true);
+
+ // require version attribute
+ getRequiredAttribute(node, "version", null);
+
+ return new JREDesc(version, location, vmArgs, initialHeap, maxHeap, resources);
+ }
+
+
+
+ /**
+ * Returns the JAR element at the specified node.
+ *
+ * @param node the jar or nativelib node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public JARDesc getJAR(Node node) throws ParseException {
+ boolean nativeJar = "nativelib".equals(node.getNodeName());
+ URL location = getRequiredURL(node, "href", base);
+ Version version = getVersion(node, "version", null);
+ String part = getAttribute(node, "part", null);
+ boolean main = "true".equals(getAttribute(node, "main", "false"));
+ boolean lazy = "lazy".equals(getAttribute(node, "download", "eager"));
+ int size = Integer.parseInt(getAttribute(node, "size", "0"));
+
+ if (nativeJar && main)
+ if (strict)
+ throw new ParseException(R("PNativeHasMain"));
+
+ return new JARDesc(location, version, part, lazy, main, nativeJar, true);
+
+ }
+
+ /**
+ * Returns the Extension element at the specified node.
+ *
+ * @param node the extension node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public ExtensionDesc getExtension(Node node) throws ParseException {
+ String name = getAttribute(node, "name", null);
+ Version version = getVersion(node, "version", null);
+ URL location = getRequiredURL(node, "href", base);
+
+ ExtensionDesc ext = new ExtensionDesc(name, version, location);
+
+ Node dload[] = getChildNodes(node, "ext-download");
+ for (int i=0; i < dload.length; i++) {
+ boolean lazy = "lazy".equals(getAttribute(dload[i], "download", "eager"));
+
+ ext.addPart(getRequiredAttribute(dload[i], "ext-part", null),
+ getAttribute(dload[i], "part", null),
+ lazy);
+ }
+
+ return ext;
+ }
+
+ /**
+ * Returns the Property element at the specified node.
+ *
+ * @param node the property node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public PropertyDesc getProperty(Node node) throws ParseException {
+ String name = getRequiredAttribute(node, "name", null);
+ String value = getRequiredAttribute(node, "value", "");
+
+ return new PropertyDesc(name, value);
+ }
+
+ /**
+ * Returns the Package element at the specified node.
+ *
+ * @param node the package node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public PackageDesc getPackage(Node node) throws ParseException {
+ String name = getRequiredAttribute(node, "name", null);
+ String part = getRequiredAttribute(node, "part", "");
+ boolean recursive = getAttribute(node, "recursive", "false").equals("true");
+
+ return new PackageDesc(name, part, recursive);
+ }
+
+ //
+ // This section loads the information elements
+ //
+
+ /**
+ * Returns all of the information elements under the specified
+ * node.
+ *
+ * @param parent the parent node (jnlp)
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public List getInfo(Node parent) throws ParseException {
+ List result = new ArrayList();
+ Node info[] = getChildNodes(parent, "information");
+
+ // ensure that there are at least one information section present
+ if (info.length == 0)
+ throw new ParseException(R("PNoInfoElement"));
+
+ // create objects from the info sections
+ for (int i=0; i < info.length; i++)
+ result.add(getInformationDesc(info[i]));
+
+ return result;
+ }
+
+ /**
+ * Returns the information element at the specified node.
+ *
+ * @param node the information node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public InformationDesc getInformationDesc(Node node) throws ParseException {
+ List descriptionsUsed = new ArrayList();
+
+ // locale
+ Locale locales[] = getLocales(node);
+
+ // create information
+ InformationDesc info = new InformationDesc(file, locales);
+
+ // step through the elements
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String name = child.getNodeName();
+
+ if ("title".equals(name))
+ addInfo(info, child, null, getSpanText(child));
+ if ("vendor".equals(name))
+ addInfo(info, child, null, getSpanText(child));
+ if ("description".equals(name)) {
+ String kind = getAttribute(child, "kind", "default");
+ if (descriptionsUsed.contains(kind))
+ if (strict)
+ throw new ParseException(R("PTwoDescriptions", kind));
+
+ descriptionsUsed.add(kind);
+ addInfo(info, child, kind, getSpanText(child));
+ }
+ if ("homepage".equals(name))
+ addInfo(info, child, null, getRequiredURL(child, "href", base));
+ if ("icon".equals(name))
+ addInfo(info, child, getAttribute(child, "kind", "default"), getIcon(child));
+ if ("offline-allowed".equals(name))
+ addInfo(info, child, null, Boolean.TRUE);
+ if ("sharing-allowed".equals(name)) {
+ if (strict && !allowExtensions)
+ throw new ParseException(R("PSharing"));
+ addInfo(info, child, null, Boolean.TRUE);
+ }
+ if ("association".equals(name)) {
+ addInfo(info, child, null, getAssociation(child));
+ }
+ if ("shortcut".equals(name)) {
+ addInfo(info, child, null, getShortcut(child));
+ }
+ if ("related-content".equals(name)) {
+ addInfo(info, child, null, getRelatedContent(child));
+ }
+
+ child = child.getNextSibling();
+ }
+
+ return info;
+ }
+
+ /**
+ * Adds a key,value pair to the information object.
+ *
+ * @param info the information object
+ * @param node node name to be used as the key
+ * @param mod key name appended with "-"+mod if not null
+ * @param value the info object to add (icon or string)
+ */
+ protected void addInfo(InformationDesc info, Node node, String mod, Object value) {
+ String modStr = (mod == null) ? "" : "-"+mod;
+
+ if (node == null)
+ return;
+
+ info.addItem(node.getNodeName()+modStr, value);
+ }
+
+ /**
+ * Returns the icon element at the specified node.
+ *
+ * @param node the icon node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public IconDesc getIcon(Node node) throws ParseException {
+ int width = Integer.parseInt(getAttribute(node, "width", "-1"));
+ int height = Integer.parseInt(getAttribute(node, "height", "-1"));
+ int size = Integer.parseInt(getAttribute(node, "size", "-1"));
+ int depth = Integer.parseInt(getAttribute(node, "depth", "-1"));
+ URL location = getRequiredURL(node, "href", base);
+ Object kind = getAttribute(node, "kind", "default");
+
+ return new IconDesc(location, kind, width, height, depth, size);
+ }
+
+ //
+ // This section loads the security descriptor element
+ //
+
+ /**
+ * Returns the security descriptor element. If no security
+ * element was specified in the JNLP file then a SecurityDesc
+ * with applet permissions is returned.
+ *
+ * @param parent the parent node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public SecurityDesc getSecurity(Node parent) throws ParseException {
+ Node nodes[] = getChildNodes(parent, "security");
+
+ // test for too many security elements
+ if (nodes.length > 1)
+ if (strict)
+ throw new ParseException(R("PTwoSecurity"));
+
+ Object type = SecurityDesc.SANDBOX_PERMISSIONS;
+
+ if (nodes.length == 0)
+ type = SecurityDesc.SANDBOX_PERMISSIONS;
+ else if (null != getChildNode(nodes[0], "all-permissions"))
+ type = SecurityDesc.ALL_PERMISSIONS;
+ else if (null != getChildNode(nodes[0], "j2ee-application-client-permissions"))
+ type = SecurityDesc.J2EE_PERMISSIONS;
+ else if (strict)
+ throw new ParseException(R("PEmptySecurity"));
+
+ if (base != null)
+ return new SecurityDesc(file, type, base.getHost());
+ else
+ return new SecurityDesc(file, type, null);
+ }
+
+ /**
+ * Returns whether the JNLP file requests a trusted execution
+ * environment.
+ */
+ protected boolean isTrustedEnvironment() {
+ Node security = getChildNode(root, "security");
+
+ if (security != null)
+ if (getChildNode(security, "all-permissions") != null
+ || getChildNode(security, "j2ee-application-client-permissions") != null)
+ return true;
+
+ return false;
+ }
+
+ //
+ // This section loads the launch descriptor element
+ //
+
+ /**
+ * Returns the launch descriptor element, either AppletDesc,
+ * ApplicationDesc, ComponentDesc, or InstallerDesc.
+ *
+ * @param parent the parent node
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public Object getLauncher(Node parent) throws ParseException {
+ // check for other than one application type
+ if (1 != getChildNodes(parent, "applet-desc").length
+ + getChildNodes(parent, "application-desc").length
+ + getChildNodes(parent, "component-desc").length
+ + getChildNodes(parent, "installer-desc").length)
+ throw new ParseException(R("PTwoDescriptors"));
+
+ Node child = parent.getFirstChild();
+ while (child != null) {
+ String name = child.getNodeName();
+
+ if ("applet-desc".equals(name))
+ return getApplet(child);
+ if ("application-desc".equals(name))
+ return getApplication(child);
+ if ("component-desc".equals(name))
+ return getComponent(child);
+ if ("installer-desc".equals(name))
+ return getInstaller(child);
+
+ child = child.getNextSibling();
+ }
+
+ // not reached
+ return null;
+ }
+
+ /**
+ * Returns the applet descriptor.
+ *
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public AppletDesc getApplet(Node node) throws ParseException {
+ String name = getRequiredAttribute(node, "name", R("PUnknownApplet"));
+ String main = getRequiredAttribute(node, "main-class", null);
+ URL docbase = getURL(node, "documentbase", base);
+ Map paramMap = new HashMap();
+ int width = 0;
+ int height = 0;
+
+ try {
+ width = Integer.parseInt(getRequiredAttribute(node, "width", "100"));
+ height = Integer.parseInt(getRequiredAttribute(node, "height", "100"));
+ }
+ catch (NumberFormatException nfe) {
+ if (width <= 0)
+ throw new ParseException(R("PBadWidth"));
+ throw new ParseException(R("PBadWidth"));
+ }
+
+ // read params
+ Node params[] = getChildNodes(node, "param");
+ for (int i=0; i < params.length; i++) {
+ paramMap.put(getRequiredAttribute(params[i], "name", null),
+ getRequiredAttribute(params[i], "value", ""));
+ }
+
+ return new AppletDesc(name, main, docbase, width, height, paramMap);
+ }
+
+ /**
+ * Returns the application descriptor.
+ *
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public ApplicationDesc getApplication(Node node) throws ParseException {
+ String main = getAttribute(node, "main-class", null);
+ List argsList = new ArrayList();
+
+ // if (main == null)
+ // only ok if can be found in main jar file (can't check here but make a note)
+
+ // read parameters
+ Node args[] = getChildNodes(node, "argument");
+ for (int i=0; i < args.length; i++) {
+ //argsList.add( args[i].getNodeValue() );
+
+ //This approach was not finding the argument text
+ argsList.add( getSpanText(args[i]) );
+ }
+
+ String argStrings[] =
+ (String[]) argsList.toArray( new String[argsList.size()] );
+
+ return new ApplicationDesc(main, argStrings);
+ }
+
+ /**
+ * Returns the component descriptor.
+ */
+ public ComponentDesc getComponent(Node node) {
+ return new ComponentDesc();
+ }
+
+ /**
+ * Returns the installer descriptor.
+ */
+ public InstallerDesc getInstaller(Node node) {
+ String main = getAttribute(node, "main-class", null);
+
+ return new InstallerDesc(main);
+ }
+
+ /**
+ * Returns the association descriptor.
+ */
+ public AssociationDesc getAssociation(Node node) throws ParseException {
+ String[] extensions = getRequiredAttribute(node, "extensions", null).split(" ");
+ String mimeType = getRequiredAttribute(node, "mime-type", null);
+
+ return new AssociationDesc(mimeType, extensions);
+ }
+
+ /**
+ * Returns the shortcut descriptor.
+ */
+ public ShortcutDesc getShortcut(Node node) throws ParseException {
+
+ String online = getAttribute(node, "online", "true");
+ boolean shortcutIsOnline = Boolean.valueOf(online);
+
+ boolean showOnDesktop = false;
+ MenuDesc menu = null;
+
+ // step through the elements
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String name = child.getNodeName();
+
+ if ("desktop".equals(name)) {
+ if (showOnDesktop && strict) {
+ throw new ParseException(R("PTwoDesktops"));
+ }
+ showOnDesktop = true;
+ } else if ("menu".equals(name)){
+ if (menu != null && strict) {
+ throw new ParseException(R("PTwoMenus"));
+ }
+ menu = getMenu(child);
+ }
+
+ child = child.getNextSibling();
+ }
+
+ ShortcutDesc shortcut = new ShortcutDesc(shortcutIsOnline, showOnDesktop);
+ if (menu != null) {
+ shortcut.addMenu(menu);
+ }
+ return shortcut;
+ }
+
+ /**
+ * Returns the menu descriptor.
+ */
+ public MenuDesc getMenu(Node node) {
+ String subMenu = getAttribute(node, "submenu", null);
+
+ return new MenuDesc(subMenu);
+ }
+
+
+ /**
+ * Returns the related-content descriptor.
+ */
+ public RelatedContentDesc getRelatedContent(Node node) throws ParseException {
+
+ getRequiredAttribute(node, "href", null);
+ URL location = getURL(node, "href", base);
+
+ String title = null;
+ String description = null;
+ IconDesc icon = null;
+
+ // step through the elements
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String name = child.getNodeName();
+
+ if ("title".equals(name)) {
+ if (title != null && strict) {
+ throw new ParseException(R("PTwoTitles"));
+ }
+ title = getSpanText(child);
+ } else if ("description".equals(name)) {
+ if (description != null && strict) {
+ throw new ParseException(R("PTwoDescriptions"));
+ }
+ description = getSpanText(child);
+ } else if ("icon".equals(name)) {
+ if (icon != null && strict) {
+ throw new ParseException(R("PTwoIcons"));
+ }
+ icon = getIcon(child);
+ }
+
+ child = child.getNextSibling();
+ }
+
+ RelatedContentDesc relatedContent = new RelatedContentDesc(location);
+ relatedContent.setDescription(description);
+ relatedContent.setIconDesc(icon);
+ relatedContent.setTitle(title);
+
+ return relatedContent;
+
+ }
+
+ // other methods
+
+ /**
+ * Returns an array of substrings seperated by spaces (spaces
+ * escaped with backslash do not separate strings). This method
+ * splits strings as per the spec except that it does replace
+ * escaped other characters with their own value.
+ */
+ public String[] splitString(String source) {
+ if (source == null)
+ return new String[0];
+
+ List result = new ArrayList();
+ StringTokenizer st = new StringTokenizer(source, " ");
+ StringBuffer part = new StringBuffer();
+ while (st.hasMoreTokens()) {
+ part.setLength(0);
+
+ // tack together tokens joined by backslash
+ while (true) {
+ part.append(st.nextToken());
+
+ if (st.hasMoreTokens() && part.charAt(part.length()-1) == '\\')
+ part.setCharAt(part.length()-1, ' '); // join with the space
+ else
+ break; // bizarre while format gets \ at end of string right (no extra space added at end)
+ }
+
+ // delete \ quote chars
+ for (int i = part.length(); i-- > 0;) // sweet syntax for reverse loop
+ if (part.charAt(i) == '\\')
+ part.deleteCharAt(i--); // and skip previous char so \\ becomes \
+
+ result.add( part.toString() );
+ }
+
+ return (String[]) result.toArray(new String[result.size()] );
+ }
+
+ /**
+ * Returns the Locale object(s) from a node's locale attribute.
+ *
+ * @param node the node with a locale attribute
+ */
+ public Locale[] getLocales(Node node) {
+ List locales = new ArrayList();
+ String localeParts[] =
+ splitString(getAttribute(node, "locale", ""));
+
+ for (int i=0; i < localeParts.length; i++) {
+ Locale l = getLocale( localeParts[i] );
+ if (l != null)
+ locales.add(l);
+ }
+
+ return (Locale[]) locales.toArray(new Locale[locales.size()] );
+ }
+
+ /**
+ * Returns a Locale from a single locale.
+ *
+ * @param locale the locale string
+ */
+ public Locale getLocale(String localeStr) {
+ if (localeStr.length() < 2)
+ return null;
+
+ String language = localeStr.substring(0, 2);
+ String country = (localeStr.length()<5) ? "" : localeStr.substring(3, 5);
+ String variant = (localeStr.length()<7) ? "" : localeStr.substring(6, 8);
+
+ // null is not allowed n locale but "" is
+ return new Locale(language, country, variant);
+ }
+
+
+
+ // XML junk
+
+ /**
+ * Returns the implied text under a node, for example "text" in
+ * "<description>text</description>".
+ *
+ * @param node the node with text under it
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public String getSpanText(Node node) throws ParseException {
+ if (node == null)
+ return null;
+
+ // NANO
+ return node.getNodeValue();
+
+ /* TINY
+ Node child = node.getFirstChild();
+
+ if (child == null) {
+ if (strict)
+ // not sure if this is an error or whether "" is proper
+ throw new ParseException("No text specified (node="+node.getNodeName()+")");
+ else
+ return "";
+ }
+
+ return child.getNodeValue();
+ */
+ }
+
+ /**
+ * Returns the first child node with the specified name.
+ */
+ public static Node getChildNode(Node node, String name) {
+ Node[] result = getChildNodes(node, name);
+ if (result.length == 0)
+ return null;
+ else
+ return result[0];
+ }
+
+ /**
+ * Returns all child nodes with the specified name.
+ */
+ public static Node[] getChildNodes(Node node, String name) {
+ List result = new ArrayList();
+
+ Node child = node.getFirstChild();
+ while (child != null) {
+ if (child.getNodeName().equals(name))
+ result.add(child);
+ child = child.getNextSibling();
+ }
+
+ return (Node[]) result.toArray( new Node[result.size()] );
+ }
+
+
+ /**
+ * Returns a URL with a trailing / appended to it if there is no
+ * trailing slash on the specifed URL.
+ */
+ private URL addSlash(URL source) {
+ if (source == null)
+ return null;
+
+ if (!source.toString().endsWith("/")) {
+ try {
+ source = new URL(source.toString()+"/");
+ }
+ catch (MalformedURLException ex) {
+ }
+ }
+
+ return source;
+ }
+
+
+ /**
+ * Returns the same result as getURL except that a
+ * ParseException is thrown if the attribute is null or empty.
+ *
+ * @param node the node
+ * @param name the attribute containing an href
+ * @param base the base URL
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public URL getRequiredURL(Node node, String name, URL base) throws ParseException {
+ // probably should change "" to null so that url is always
+ // required even if !strict
+ getRequiredAttribute(node, name, "");
+
+ return getURL(node, name, base);
+ }
+
+
+ /**
+ * Returns a URL object from a href string relative to the
+ * code base. If the href denotes a relative URL, it must
+ * reference a location that is a subdirectory of the
+ * codebase.<p>
+ *
+ * @param node the node
+ * @param name the attribute containing an href
+ * @param base the base URL
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public URL getURL(Node node, String name, URL base) throws ParseException {
+ String href = getAttribute(node, name, null);
+ if (href == null)
+ return null; // so that code can throw an exception if attribute was required
+
+ try {
+ if (base == null)
+ return new URL(href);
+ else {
+ try {
+ return new URL(href);
+ }
+ catch (MalformedURLException ex) {
+ // is relative
+ }
+
+ URL result = new URL(base, href);
+
+ // check for going above the codebase
+ if (! result.toString().startsWith( base.toString()) )
+ if (strict)
+ throw new ParseException(R("PUrlNotInCodebase", node.getNodeName(), href, base));
+
+ return result;
+ }
+
+ }
+ catch (MalformedURLException ex) {
+ if (base == null)
+ throw new ParseException(R("PBadNonrelativeUrl", node.getNodeName(), href));
+ else
+ throw new ParseException(R("PBadRelativeUrl", node.getNodeName(), href, base));
+ }
+ }
+
+ /**
+ * Returns a Version from the specified attribute and default
+ * value.
+ *
+ * @param node the node
+ * @param name the attribute
+ * @param defaultValue default if no such attribute
+ * @return a Version, or null if no such attribute and default is null
+ */
+ public Version getVersion(Node node, String name, String defaultValue) {
+ String version = getAttribute(node, name, defaultValue);
+ if (version == null)
+ return null;
+ else
+ return new Version(version);
+ }
+
+ /**
+ * Check that the VM args are valid and safe
+ * @param vmArgs a string containing the args
+ * @throws ParseException if the VM arguments are invalid or dangerous
+ */
+ private void checkVMArgs(String vmArgs) throws IllegalArgumentException {
+ if (vmArgs == null) {
+ return;
+ }
+
+ List<String> validArguments = Arrays.asList(getValidVMArguments());
+ List<String> validStartingArguments = Arrays.asList(getValidStartingVMArguments());
+
+ String[] arguments = vmArgs.split(" ");
+ boolean argumentIsValid = false;
+ for (String argument: arguments) {
+ argumentIsValid = false;
+
+ if (validArguments.contains(argument)) {
+ argumentIsValid = true;
+ } else {
+ for (String validStartingArgument: validStartingArguments) {
+ if (argument.startsWith(validStartingArgument)) {
+ argumentIsValid = true;
+ break;
+ }
+ }
+ }
+
+ if (!argumentIsValid) {
+ throw new IllegalArgumentException(argument);
+ }
+ }
+
+ }
+
+ /**
+ * Returns an array of valid (ie safe and supported) arguments for the JVM
+ *
+ * Based on http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/syntax.html
+ */
+ private String[] getValidVMArguments() {
+ return new String[] {
+ "-d32", /* use a 32-bit data model if available */
+ "-client", /* to select the client VM */
+ "-server", /* to select the server VM */
+ "-verbose", /* enable verbose output */
+ "-version", /* print product version and exit */
+ "-showversion", /* print product version and continue */
+ "-help", /* print this help message */
+ "-X", /* print help on non-standard options */
+ "-ea", /* enable assertions */
+ "-enableassertions", /* enable assertions */
+ "-da", /* disable assertions */
+ "-disableassertions", /* disable assertions */
+ "-esa", /* enable system assertions */
+ "-enablesystemassertions", /* enable system assertions */
+ "-dsa", /* disable system assertione */
+ "-disablesystemassertions", /* disable system assertione */
+ "-Xmixed", /* mixed mode execution (default) */
+ "-Xint", /* interpreted mode execution only */
+ "-Xnoclassgc", /* disable class garbage collection */
+ "-Xincgc", /* enable incremental garbage collection */
+ "-Xbatch", /* disable background compilation */
+ "-Xprof", /* output cpu profiling data */
+ "-Xdebug", /* enable remote debugging */
+ "-Xfuture", /* enable strictest checks, anticipating future default */
+ "-Xrs", /* reduce use of OS signals by Java/VM (see documentation) */
+ "-XX:+ForceTimeHighResolution", /* use high resolution timer */
+ "-XX:-ForceTimeHighResolution", /* use low resolution (default) */
+ };
+ }
+
+ /**
+ * Returns an array containing the starts of valid (ie safe and supported)
+ * arguments for the JVM
+ *
+ * Based on http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/syntax.html
+ */
+ private String[] getValidStartingVMArguments() {
+ return new String[] {
+ "-ea", /* enable assertions for classes */
+ "-enableassertions", /* enable assertions for classes */
+ "-da", /* disable assertions for classes */
+ "-disableassertions", /* disable assertions for classes */
+ "-verbose", /* enable verbose output */
+ "-Xms", /* set initial Java heap size */
+ "-Xmx", /* set maximum Java heap size */
+ "-Xss", /* set java thread stack size */
+ "-XX:NewRatio", /* set Ratio of new/old gen sizes */
+ "-XX:NewSize", /* set initial size of new generation */
+ "-XX:MaxNewSize", /* set max size of new generation */
+ "-XX:PermSize", /* set initial size of permanent gen */
+ "-XX:MaxPermSize", /* set max size of permanent gen */
+ "-XX:MaxHeapFreeRatio", /* heap free percentage (default 70) */
+ "-XX:MinHeapFreeRatio", /* heap free percentage (default 40) */
+ "-XX:UseSerialGC", /* use serial garbage collection */
+ "-XX:ThreadStackSize", /* thread stack size (in KB) */
+ "-XX:MaxInlineSize", /* set max num of bytecodes to inline */
+ "-XX:ReservedCodeCacheSize", /* Reserved code cache size (bytes) */
+ "-XX:MaxDirectMemorySize",
+
+ };
+ }
+
+ /**
+ * Returns the same result as getAttribute except that if strict
+ * mode is enabled or the default value is null a parse
+ * exception is thrown instead of returning the default value.
+ *
+ * @param node the node
+ * @param name the attribute
+ * @param defaultValue default value
+ * @throws ParseException if the attribute does not exist or is empty
+ */
+ public String getRequiredAttribute(Node node, String name, String defaultValue) throws ParseException {
+ String result = getAttribute(node, name, null);
+
+ if (result == null || result.length() == 0)
+ if (strict || defaultValue == null)
+ throw new ParseException(R("PNeedsAttribute", node.getNodeName(), name));
+
+ if (result == null)
+ return defaultValue;
+ else
+ return result;
+ }
+
+ /**
+ * Retuns an attribute or the specified defaultValue if there is
+ * no such attribute.
+ *
+ * @param node the node
+ * @param name the attribute
+ * @param defaultValue default if no such attribute
+ */
+ public String getAttribute(Node node, String name, String defaultValue) {
+ // SAX
+ // String result = ((Element) node).getAttribute(name);
+ String result = node.getAttribute(name);
+
+ if (result == null || result.length()==0)
+ return defaultValue;
+
+ return result;
+ }
+
+ /**
+ * Return the root node from the XML document in the specified
+ * input stream.
+ *
+ * @throws ParseException if the JNLP file is invalid
+ */
+ public static Node getRootNode(InputStream input) throws ParseException {
+ try {
+ /* SAX
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(false);
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ builder.setErrorHandler(errorHandler);
+
+ Document doc = builder.parse(input);
+ return doc.getDocumentElement();
+ */
+
+ /* TINY
+ Node document = new Node(TinyParser.parseXML(input));
+ Node jnlpNode = getChildNode(document, "jnlp"); // skip comments
+ */
+
+ //A BufferedInputStream is used to allow marking and reseting
+ //of a stream.
+ BufferedInputStream bs = new BufferedInputStream(input);
+
+ /* NANO */
+ final XMLElement xml = new XMLElement();
+ final PipedInputStream pin = new PipedInputStream();
+ final PipedOutputStream pout = new PipedOutputStream(pin);
+ final InputStreamReader isr = new InputStreamReader(bs, getEncoding(bs));
+ // Clean the jnlp xml file of all comments before passing
+ // it to the parser.
+ new Thread(
+ new Runnable(){
+ public void run(){
+ (new XMLElement()).sanitizeInput(isr, pout);
+ try {
+ pout.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+ ).start();
+ xml.parseFromReader(new InputStreamReader(pin));
+ Node jnlpNode = new Node(xml);
+ return jnlpNode;
+ }
+ catch(Exception ex) {
+ throw new ParseException(R("PBadXML"), ex);
+ }
+ }
+
+ /**
+ * Returns the name of the encoding used in this InputStream.
+ *
+ * @param input the InputStream
+ * @return a String representation of encoding
+ */
+ private static String getEncoding(InputStream input) throws IOException{
+ //Fixme: This only recognizes UTF-8, UTF-16, and
+ //UTF-32, which is enough to parse the prolog portion of xml to
+ //find out the exact encoding (if it exists). The reason being
+ //there could be other encodings, such as ISO 8859 which is 8-bits
+ //but it supports latin characters.
+ //So what needs to be done is to parse the prolog and retrieve
+ //the exact encoding from it.
+
+ int[] s = new int[4];
+ String encoding = "UTF-8";
+
+ //Determine what the first four bytes are and store
+ //them into an int array.
+ input.mark(4);
+ for (int i = 0; i < 4; i++) {
+ s[i] = input.read();
+ }
+ input.reset();
+
+ //Set the encoding base on what the first four bytes of the
+ //inputstream turn out to be (following the information from
+ //www.w3.org/TR/REC-xml/#sec-guessing).
+ if (s[0] == 255) {
+ if (s[1] == 254) {
+ if (s[2] != 0 || s[3] != 0) {
+ encoding = "UnicodeLittle";
+ } else {
+ encoding = "X-UTF-32LE-BOM";
+ }
+ }
+ } else if (s[0] == 254 && s[1] == 255 && (s[2] != 0 ||
+ s[3] != 0)) {
+ encoding = "UTF-16";
+
+ } else if (s[0] == 0 && s[1] == 0 && s[2] == 254 &&
+ s[3] == 255) {
+ encoding = "X-UTF-32BE-BOM";
+
+ } else if (s[0] == 0 && s[1] == 0 && s[2] == 0 &&
+ s[3] == 60) {
+ encoding = "UTF-32BE";
+
+ } else if (s[0] == 60 && s[1] == 0 && s[2] == 0 &&
+ s[3] == 0) {
+ encoding = "UTF-32LE";
+
+ } else if (s[0] == 0 && s[1] == 60 && s[2] == 0 &&
+ s[3] == 63) {
+ encoding = "UTF-16BE";
+ } else if (s[0] == 60 && s[1] == 0 && s[2] == 63 &&
+ s[3] == 0) {
+ encoding = "UTF-16LE";
+ }
+
+ return encoding;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/PluginBridge.java b/netx/net/sourceforge/jnlp/PluginBridge.java
new file mode 100644
index 0000000..4ae100a
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/PluginBridge.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ * This file is part of IcedTea, http://icedtea.classpath.org
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package net.sourceforge.jnlp;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Calendar;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.List;
+import java.util.ArrayList;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+
+public class PluginBridge extends JNLPFile
+{
+
+ String name;
+ String[] jars = new String[0];
+ String[] cache_jars = new String[0];
+ String[] cache_ex_jars = new String[0];
+ Hashtable atts;
+
+ public PluginBridge(URL codebase, URL documentBase, String jar, String main,
+ int width, int height, Hashtable atts)
+ throws Exception
+ {
+ specVersion = new Version("1.0");
+ fileVersion = new Version("1.1");
+ this.codeBase = codebase;
+ this.sourceLocation = documentBase;
+
+ // also, see if cache_archive is specified
+ if (atts.get("cache_archive") != null && ((String) atts.get("cache_archive")).length() > 0) {
+
+ String[] versions = new String[0];
+
+ // are there accompanying versions?
+ if (atts.get("cache_version") != null) {
+ versions = ((String) atts.get("cache_version")).split(",");
+ }
+
+ String[] jars = ((String) atts.get("cache_archive")).split(",");
+ cache_jars = new String[jars.length];
+
+ for (int i=0; i < jars.length; i++) {
+
+ cache_jars[i] = jars[i].trim();
+
+ if (versions.length > 0) {
+ cache_jars[i] += ";" + versions[i].trim();
+ }
+ }
+ }
+
+ if (atts.get("cache_archive_ex") != null && ((String) atts.get("cache_archive_ex")).length() > 0) {
+ cache_ex_jars = ((String) atts.get("cache_archive_ex")).split(",");
+ }
+
+ if (jar != null && jar.length() > 0) {
+ this.jars = jar.split(",");
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Jar string: " + jar);
+ System.err.println("jars length: " + jars.length);
+ }
+ }
+ this.atts = atts;
+
+ name = (String) atts.get("name");
+ if (name == null)
+ name = "Applet";
+ else
+ name = name + " applet";
+
+ if (main.endsWith(".class"))
+ main = main.substring(0, main.length() - 6);
+
+ launchType = new AppletDesc(name, main, documentBase, width,
+ height, atts);
+
+ if (main.endsWith(".class")) //single class file only
+ security = new SecurityDesc(this, SecurityDesc.SANDBOX_PERMISSIONS,
+ codebase.getHost());
+ else
+ security = null;
+
+ this.uniqueKey = Calendar.getInstance().getTimeInMillis() + "-" +
+ Math.abs(((new java.util.Random()).nextInt())) + "-" +
+ documentBase;
+ }
+
+ public String getTitle()
+ {
+ return name;
+ }
+
+ public InformationDesc getInformation(final Locale locale)
+ {
+ return new InformationDesc(this, new Locale[] {locale}) {
+ protected List getItems(Object key)
+ {
+ // Should we populate this list with applet attribute tags?
+ List result = new ArrayList();
+ return result;
+ }
+ };
+ }
+
+ public ResourcesDesc getResources(final Locale locale, final String os,
+ final String arch)
+ {
+ return new ResourcesDesc(this, new Locale[] {locale}, new String[] {os},
+ new String[] {arch}) {
+ public List getResources(Class launchType)
+ {
+ List result = new ArrayList();
+ result.addAll(sharedResources.getResources(launchType));
+
+ // Need to add the JAR manually...
+ //should this be done to sharedResources on init?
+ try
+ {
+ if (launchType.equals(JARDesc.class))
+ {
+ for (int i = 0; i < jars.length; i++)
+ if (jars[i].length() > 0)
+ result.add(new JARDesc(new URL(codeBase, jars[i]),
+ null, null, false, true, false, true));
+
+ boolean cacheable = true;
+
+ if (atts.get("cache_option") != null &&
+ ((String) atts.get("cache_option")).equalsIgnoreCase("no"))
+ cacheable = false;
+
+ for (int i = 0; i < cache_jars.length; i++) {
+
+ String[] jar_and_ver = cache_jars[i].split(";");
+
+ String jar = jar_and_ver[0];
+ Version version = null;
+
+ if (jar.length() == 0)
+ continue;
+
+ if (jar_and_ver.length > 1) {
+ version = new Version(jar_and_ver[1]);
+ }
+
+ result.add(new JARDesc(new URL(codeBase, jar),
+ version, null, false, true, false, cacheable));
+ }
+
+ for (int i = 0; i < cache_ex_jars.length; i++) {
+
+ if (cache_ex_jars[i].length() == 0)
+ continue;
+
+ String[] jar_info = cache_ex_jars[i].split(";");
+
+ String jar = jar_info[0].trim();
+ Version version = null;
+ boolean lazy = true;
+
+ if (jar_info.length > 1) {
+
+ // format is name[[;preload];version]
+
+ if (jar_info[1].equals("preload")) {
+ lazy = false;
+ } else {
+ version = new Version(jar_info[1].trim());
+ }
+
+ if (jar_info.length > 2) {
+ lazy = false;
+ version = new Version(jar_info[2].trim());
+ }
+ }
+
+ result.add(new JARDesc(new URL(codeBase, jar),
+ version, null, lazy, true, false, false));
+ }
+ }
+ }
+ catch (MalformedURLException ex)
+ { }
+ return result;
+ }
+
+ public JARDesc[] getJARs() {
+ List resources = getResources(JARDesc.class);
+ ArrayList<JARDesc> jars = new ArrayList<JARDesc>();
+
+ //Only get the JARDescs
+ for (int i = 0; i < resources.size(); i++) {
+ Object resource = resources.get(i);
+ if (resource instanceof JARDesc)
+ jars.add((JARDesc) resource);
+ }
+
+ Object[] objectArray = jars.toArray();
+ JARDesc[] jarArray = new JARDesc[objectArray.length];
+
+ for (int i = 0; i < objectArray.length; i++)
+ jarArray[i] = (JARDesc) objectArray[i];
+
+ return jarArray;
+ }
+
+ public void addResource(Object resource)
+ {
+ // todo: honor the current locale, os, arch values
+ sharedResources.addResource(resource);
+ }
+
+ };
+ }
+
+ public boolean isApplet() {
+ return true;
+ }
+ public boolean isApplication() {
+ return false;
+ }
+ public boolean isComponent() {
+ return false;
+ }
+ public boolean isInstaller() {
+ return false;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/PropertyDesc.java b/netx/net/sourceforge/jnlp/PropertyDesc.java
new file mode 100644
index 0000000..c96b748
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/PropertyDesc.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The property element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class PropertyDesc {
+
+ /** the key name */
+ private String key;
+
+ /** the value */
+ private String value;
+
+
+ /**
+ * Creates a property descriptor.
+ *
+ * @param key the key name
+ * @param value the value
+ */
+ public PropertyDesc(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * Returns the property's key
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the property's value
+ */
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/RelatedContentDesc.java b/netx/net/sourceforge/jnlp/RelatedContentDesc.java
new file mode 100644
index 0000000..e649ccf
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/RelatedContentDesc.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import java.net.URL;
+
+public class RelatedContentDesc {
+
+ /** title of the content */
+ private String title = null;;
+
+ /** the description of the content */
+ private String description = null;
+
+ /** the location of the content */
+ private URL location = null;
+
+ /** the icon for this related content */
+ private IconDesc icon = null;
+
+ /**
+ * Create a related-content descriptor
+ * @param href the url of the related content
+ */
+ public RelatedContentDesc(URL href) {
+ this.location = href;
+ }
+
+ /**
+ * Set the title of this content
+ * @param title the title of this content
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * Returns the title of this content..
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Set the description of this related content
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns the description of the related content
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the location of the related content. Not null
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Set the icon for this related content
+ */
+ public void setIconDesc(IconDesc icon) {
+ this.icon = icon;
+ }
+
+ /**
+ * Returns the icon descriptor for the realted content
+ */
+ public IconDesc getIcon() {
+ return icon;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ResourcesDesc.java b/netx/net/sourceforge/jnlp/ResourcesDesc.java
new file mode 100644
index 0000000..1d7c94c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ResourcesDesc.java
@@ -0,0 +1,230 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.awt.Dimension;
+import java.net.*;
+import java.util.*;
+
+/**
+ * The resources element.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+public class ResourcesDesc {
+
+ /** the locales of these resources */
+ private Locale locales[];
+
+ /** the OS for these resources */
+ private String os[];
+
+ /** the arch for these resources */
+ private String arch[];
+
+ /** the JNLPFile this information is for */
+ private JNLPFile jnlpFile;
+
+ /** list of jars, packages, properties, and extensions */
+ private List resources = new ArrayList(); // mixed list makes easier for lookup code
+
+
+ /**
+ * Create a representation of one information section of the
+ * JNLP File.
+ *
+ * @param jnlpFile JNLP file the resources are for
+ * @param locales the locales of these resources
+ * @param os the os of these resources
+ * @param arch the arch of these resources
+ */
+ public ResourcesDesc(JNLPFile jnlpFile, Locale locales[], String os[], String arch[]) {
+ this.jnlpFile = jnlpFile;
+ this.locales = locales;
+ this.os = os;
+ this.arch = arch;
+ }
+
+ /**
+ * Returns the JVMs.
+ */
+ public JREDesc[] getJREs() {
+ List resources = getResources(JREDesc.class);
+ return (JREDesc[]) resources.toArray( new JREDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the main JAR for these resources. There first JAR
+ * is returned if no JARs are specified as the main JAR, and if
+ * there are no JARs defined then null is returned.
+ */
+ public JARDesc getMainJAR() {
+ JARDesc jars[] = getJARs();
+
+ for (int i=0; i < jars.length; i++)
+ if (jars[i].isMain())
+ return jars[i];
+
+ if (jars.length > 0)
+ return jars[0];
+ else
+ return null;
+ }
+
+ /**
+ * Returns all of the JARs.
+ */
+ public JARDesc[] getJARs() {
+ List resources = getResources(JARDesc.class);
+ return (JARDesc[]) resources.toArray( new JARDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the JARs with the specified part name.
+ *
+ * @param partName the part name, null and "" equivalent
+ */
+ public JARDesc[] getJARs(String partName) {
+ List resources = getResources(JARDesc.class);
+
+ for (int i = resources.size(); i-- > 0;) {
+ JARDesc jar = (JARDesc) resources.get(i);
+
+ if (!(""+jar.getPart()).equals(""+partName))
+ resources.remove(i);
+ }
+
+ return (JARDesc[]) resources.toArray( new JARDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the Extensions.
+ */
+ public ExtensionDesc[] getExtensions() {
+ List resources = getResources(ExtensionDesc.class);
+ return (ExtensionDesc[]) resources.toArray( new ExtensionDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the Packages.
+ */
+ public PackageDesc[] getPackages() {
+ List resources = getResources(PackageDesc.class);
+ return (PackageDesc[]) resources.toArray( new PackageDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the Packages that match the specified class name.
+ *
+ * @param className the fully qualified class name
+ * @return the PackageDesc objects matching the class name
+ */
+ public PackageDesc[] getPackages(String className) {
+ List resources = getResources(PackageDesc.class);
+
+ for (int i = resources.size(); i-- > 0;) {
+ PackageDesc pk = (PackageDesc) resources.get(i);
+
+ if (!pk.matches(className))
+ resources.remove(i);
+ }
+
+ return (PackageDesc[]) resources.toArray( new PackageDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the Properties as a list.
+ */
+ public PropertyDesc[] getProperties() {
+ List resources = getResources(PropertyDesc.class);
+ return (PropertyDesc[]) resources.toArray( new PropertyDesc[resources.size()] );
+ }
+
+ /**
+ * Returns the properties as a map.
+ */
+ public Map getPropertiesMap() {
+ Properties properties = new Properties();
+ List resources = getResources(PropertyDesc.class);
+ for (int i=0; i < resources.size(); i++) {
+ PropertyDesc prop = (PropertyDesc) resources.get(i);
+ properties.put( prop.getKey(), prop.getValue() );
+ }
+
+ return properties;
+ }
+
+ /**
+ * Returns the os required by these resources, or null if no
+ * locale was specified in the JNLP file.
+ */
+ public String[] getOS() {
+ return os;
+ }
+
+ /**
+ * Returns the architecture required by these resources, or null
+ * if no locale was specified in the JNLP file.
+ */
+ public String[] getArch() {
+ return arch;
+ }
+
+ /**
+ * Returns the locale required by these resources, or null if no
+ * locale was specified in the JNLP file.
+ */
+ public Locale[] getLocales() {
+ return locales;
+ }
+
+ /**
+ * Returns the JNLPFile the resources are for.
+ */
+ public JNLPFile getJNLPFile() {
+ return jnlpFile;
+ }
+
+ /**
+ * Returns all resources of the specified type.
+ */
+ public List getResources(Class type) {
+ List result = new ArrayList();
+
+ for (int i=0; i < resources.size(); i++)
+ if ( type.isAssignableFrom(resources.get(i).getClass()) )
+ result.add(resources.get(i));
+
+ return result;
+ }
+
+ /**
+ * Add a resource.
+ */
+ public void addResource(Object resource) {
+ // if this is going to stay public it should probably take an
+ // interface instead of an Object
+ if (resource == null)
+ throw new IllegalArgumentException("null resource");
+
+ resources.add(resource);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/SecurityDesc.java b/netx/net/sourceforge/jnlp/SecurityDesc.java
new file mode 100644
index 0000000..d2ccde6
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/SecurityDesc.java
@@ -0,0 +1,201 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.security.*;
+import java.awt.AWTPermission;
+
+/**
+ * The security element.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+public class SecurityDesc {
+
+ /*
+ * We do not verify security here, the classloader deals with security
+ */
+
+ /** All permissions. */
+ public static final Object ALL_PERMISSIONS = "All";
+
+ /** Applet permissions. */
+ public static final Object SANDBOX_PERMISSIONS = "Sandbox";
+
+ /** J2EE permissions. */
+ public static final Object J2EE_PERMISSIONS = "J2SE";
+
+ /** permissions type */
+ private Object type;
+
+ /** the download host */
+ private String downloadHost;
+
+ /** the JNLP file */
+ private JNLPFile file;
+
+ // We go by the rules here:
+ // http://java.sun.com/docs/books/tutorial/deployment/doingMoreWithRIA/properties.html
+
+ // Since this is security sensitive, take a conservative approach:
+ // Allow only what is specifically allowed, and deny everything else
+
+ /** basic permissions for restricted mode */
+ private static Permission j2eePermissions[] = {
+ new AWTPermission("accessClipboard"),
+ // disabled because we can't at this time prevent an
+ // application from accessing other applications' event
+ // queues, or even prevent access to security dialog queues.
+ //
+ // new AWTPermission("accessEventQueue"),
+ new AWTPermission("showWindowWithoutWarningBanner"),
+ new RuntimePermission("exitVM"),
+ new RuntimePermission("loadLibrary"),
+ new RuntimePermission("queuePrintJob"),
+ new SocketPermission("*", "connect"),
+ new SocketPermission("localhost:1024-", "accept, listen"),
+ new FilePermission("*", "read, write"),
+ new PropertyPermission("*", "read"),
+ };
+
+ /** basic permissions for restricted mode */
+ private static Permission sandboxPermissions[] = {
+ new SocketPermission("localhost:1024-", "listen"),
+ // new SocketPermission("<DownloadHost>", "connect, accept"), // added by code
+ new PropertyPermission("java.version", "read"),
+ new PropertyPermission("java.vendor", "read"),
+ new PropertyPermission("java.vendor.url", "read"),
+ new PropertyPermission("java.class.version", "read"),
+ new PropertyPermission("os.name", "read"),
+ new PropertyPermission("os.version", "read"),
+ new PropertyPermission("os.arch", "read"),
+ new PropertyPermission("file.separator", "read"),
+ new PropertyPermission("path.separator", "read"),
+ new PropertyPermission("line.separator", "read"),
+ new PropertyPermission("java.specification.version", "read"),
+ new PropertyPermission("java.specification.vendor", "read"),
+ new PropertyPermission("java.specification.name", "read"),
+ new PropertyPermission("java.vm.specification.vendor", "read"),
+ new PropertyPermission("java.vm.specification.name", "read"),
+ new PropertyPermission("java.vm.version", "read"),
+ new PropertyPermission("java.vm.vendor", "read"),
+ new PropertyPermission("java.vm.name", "read"),
+ new PropertyPermission("javawebstart.version", "read"),
+ new PropertyPermission("javaplugin.*", "read"),
+ new PropertyPermission("jnlp.*", "read,write"),
+ new PropertyPermission("javaws.*", "read,write"),
+ new RuntimePermission("exitVM"),
+ new RuntimePermission("stopThread"),
+ new AWTPermission("showWindowWithoutWarningBanner"),
+ // disabled because we can't at this time prevent an
+ // application from accessing other applications' event
+ // queues, or even prevent access to security dialog queues.
+ //
+ // new AWTPermission("accessEventQueue"),
+ };
+
+ /** basic permissions for restricted mode */
+ private static Permission jnlpRIAPermissions[] = {
+ new PropertyPermission("awt.useSystemAAFontSettings", "read,write"),
+ new PropertyPermission("http.agent", "read,write"),
+ new PropertyPermission("http.keepAlive", "read,write"),
+ new PropertyPermission("java.awt.syncLWRequests", "read,write"),
+ new PropertyPermission("java.awt.Window.locationByPlatform", "read,write"),
+ new PropertyPermission("javaws.cfg.jauthenticator", "read,write"),
+ new PropertyPermission("javax.swing.defaultlf", "read,write"),
+ new PropertyPermission("sun.awt.noerasebackground", "read,write"),
+ new PropertyPermission("sun.awt.erasebackgroundonresize", "read,write"),
+ new PropertyPermission("sun.java2d.d3d", "read,write"),
+ new PropertyPermission("sun.java2d.dpiaware", "read,write"),
+ new PropertyPermission("sun.java2d.noddraw", "read,write"),
+ new PropertyPermission("sun.java2d.opengl", "read,write"),
+ new PropertyPermission("swing.boldMetal", "read,write"),
+ new PropertyPermission("swing.metalTheme", "read,write"),
+ new PropertyPermission("swing.noxp", "read,write"),
+ new PropertyPermission("swing.useSystemFontSettings", "read,write"),
+ };
+
+ /**
+ * Create a security descriptor.
+ *
+ * @param file the JNLP file
+ * @param type the type of security
+ * @param downloadHost the download host (can always connect to)
+ */
+ public SecurityDesc(JNLPFile file, Object type, String downloadHost) {
+ this.file = file;
+ this.type = type;
+ this.downloadHost = downloadHost;
+ }
+
+ /**
+ * Returns the permissions type, one of: ALL_PERMISSIONS,
+ * SANDBOX_PERMISSIONS, J2EE_PERMISSIONS.
+ */
+ public Object getSecurityType() {
+ return type;
+ }
+
+ /**
+ * Returns a PermissionCollection containing the basic
+ * permissions granted depending on the security type.
+ */
+ public PermissionCollection getPermissions() {
+ PermissionCollection permissions = getSandBoxPermissions();
+
+ // discard sandbox, give all
+ if (type == ALL_PERMISSIONS) {
+ permissions = new Permissions();
+ permissions.add(new AllPermission());
+ return permissions;
+ }
+
+ // add j2ee to sandbox if needed
+ if (type == J2EE_PERMISSIONS)
+ for (int i=0; i < j2eePermissions.length; i++)
+ permissions.add(j2eePermissions[i]);
+
+ return permissions;
+ }
+
+ /**
+ * Returns a PermissionCollection containing the sandbox permissions
+ */
+ public PermissionCollection getSandBoxPermissions() {
+
+ Permissions permissions = new Permissions();
+
+ for (int i=0; i < sandboxPermissions.length; i++)
+ permissions.add(sandboxPermissions[i]);
+
+ if (file.isApplication())
+ for (int i=0; i < jnlpRIAPermissions.length; i++)
+ permissions.add(jnlpRIAPermissions[i]);
+
+ if (downloadHost != null)
+ permissions.add(new SocketPermission(downloadHost,
+ "connect, accept"));
+
+ return permissions;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/ShortcutDesc.java b/netx/net/sourceforge/jnlp/ShortcutDesc.java
new file mode 100644
index 0000000..6635dc7
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/ShortcutDesc.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+public final class ShortcutDesc {
+
+ /** the application wants to be placed on the desktop */
+ private boolean onDesktop = false;
+
+ /** the application needs to be launched online */
+ private boolean requiresOnline = true;
+
+ /** the menu descriptor */
+ private MenuDesc menu = null;
+
+ /**
+ * Create a new Shortcut descriptor
+ * @param requiresOnline whether the shortcut requires connectivity
+ * @param onDesktop whether the shortcut wants to be placed on the desktop
+ */
+ public ShortcutDesc(boolean requiresOnline, boolean onDesktop) {
+ this.requiresOnline = requiresOnline;
+ this.onDesktop = onDesktop;
+ }
+
+ /**
+ * Returns whether the shortcut requires being online
+ */
+ public boolean isOnline() {
+ return requiresOnline;
+ }
+
+ /**
+ * Return whether the shortcut should be placed on the desktop
+ */
+ public boolean onDesktop() {
+ return onDesktop;
+ }
+
+ /**
+ * Add a shortcut to the 'start menu'
+ * (whatever that means on gnome/kde/other ...)
+ * @param menu if/what menu this shortcut should be added to
+ */
+ public void addMenu(MenuDesc menu) {
+ this.menu = menu;
+ }
+
+ /**
+ * Returns the menu this shortcut should be added to
+ */
+ public MenuDesc getMenu() {
+ return menu;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/StreamEater.java b/netx/net/sourceforge/jnlp/StreamEater.java
new file mode 100644
index 0000000..4cd3485
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/StreamEater.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class reads the output from a launched process and writes it to stdout.
+ */
+public class StreamEater extends Thread {
+ private InputStream stream;
+
+ public StreamEater(InputStream stream) {
+ this.stream = new BufferedInputStream(stream);
+ }
+
+ public void run() {
+ try {
+ while (true) {
+ int c = stream.read();
+ if (c == -1)
+ break;
+
+ System.out.write(c);
+ }
+ } catch (IOException ex) {
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/UpdateDesc.java b/netx/net/sourceforge/jnlp/UpdateDesc.java
new file mode 100644
index 0000000..727efe6
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/UpdateDesc.java
@@ -0,0 +1,70 @@
+package net.sourceforge.jnlp;
+
+/**
+ * Represents an 'update' element in a JNLP file. This element describes when to
+ * check for updates and what actions to take if updates are available
+ *
+ * @see Check
+ * @see Policy
+ */
+public class UpdateDesc {
+
+ /**
+ * Describes when/how long to check for updates.
+ */
+ public enum Check {
+ /** Always check for updates before launching the application */
+ ALWAYS,
+
+ /**
+ * Default. Check for updates until a certain timeout. If the update
+ * check is not completed by timeout, launch the cached application and
+ * continue updating in the background
+ */
+ TIMEOUT,
+
+ /** Check for application updates in the background */
+ BACKGROUND
+ }
+
+ /**
+ * Describes what to do when the Runtime knows there is an applicatFion
+ * update before the application is launched.
+ */
+ public enum Policy {
+ /**
+ * Default. Always download updates without any user prompt and then launch the
+ * application
+ */
+ ALWAYS,
+
+ /**
+ * Prompt the user asking whether the user wants to download and run the
+ * updated application or run the version in the cache
+ */
+ PROMPT_UPDATE,
+
+ /**
+ * Prompts the user asking to download and run the latest version of the
+ * application or abort running
+ */
+ PROMPT_RUN,
+ }
+
+ private Check check;
+ private Policy policy;
+
+ public UpdateDesc(Check check, Policy policy) {
+ this.check = check;
+ this.policy = policy;
+ }
+
+ public Check getCheck() {
+ return this.check;
+ }
+
+ public Policy getPolicy() {
+ return this.policy;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/Version.java b/netx/net/sourceforge/jnlp/Version.java
new file mode 100644
index 0000000..40178e8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/Version.java
@@ -0,0 +1,352 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * A JNLP Version string in the form "1.2-3_abc" followed by an
+ * optional + (includes all later versions) or * (matches any
+ * suffixes on versions). More than one version can be included
+ * in a string by separating them with spaces.<p>
+ *
+ * Version strings are divided by "._-" charecters into parts.
+ * These parts are compared numerically if they can be parsed as
+ * integers or lexographically as strings otherwise. If the
+ * number of parts is different between two version strings then
+ * the smaller one is padded with zero or the empty string. Note
+ * that the padding in this version means that 1.2+ matches
+ * 1.4.0-beta1, but may not in future versions.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public class Version {
+
+ // to do: web start does not match versions with a "-" like
+ // "1.4-beta1" using the + modifier, change to mimic that
+ // behavior.
+
+ // also refactor into Version and VersionID classes so that
+ // individual version ids can be easily modified to add/remove
+ // "*" and "+" modifiers.
+
+ /** separates parts of a version string */
+ private static String seperators = ".-_";
+
+ /** magic key for whether a version part was created due to normalization */
+ private static String emptyString = new String("<EMPTY>"); // not intern'ed
+
+ /** contains all the versions matched */
+ private String versionString;
+
+
+ /**
+ * Create a Version object based on a version string (ie,
+ * "1.2.3+ 4.56*").
+ */
+ public Version(String versions) {
+ versionString = versions;
+ }
+
+ /**
+ * Returns true if the version represents a <i>version-id</i> (a
+ * single version number such as 1.2) and false otherwise.
+ */
+ public boolean isVersionId() {
+ if (-1 != versionString.indexOf(" "))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns true if all of this version's version-ids match one
+ * or more of the specifed version's version-id.
+ *
+ * @param version a version string
+ */
+ public boolean matches(String version) {
+ return matches(new Version(version));
+ }
+
+ /**
+ * Returns true if all of this version's version-ids match one
+ * or more of the specifed version's version-id.
+ *
+ * @param version a Version object
+ */
+ public boolean matches(Version version) {
+ List versionStrings = version.getVersionStrings();
+
+ for (int i=0; i < versionStrings.size(); i++) {
+ if (!this.matchesSingle( (String)versionStrings.get(i) ))
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if any of this version's version-ids match one
+ * or more of the specifed version's version-id.
+ *
+ * @param version a version string
+ */
+ public boolean matchesAny(String version) {
+ return matches(new Version(version));
+ }
+
+
+ /**
+ * Returns true if any of this version's version-ids match one
+ * or more of the specifed version's version-id.
+ *
+ * @param version a Version object
+ */
+ public boolean matchesAny(Version version) {
+ List versionStrings = version.getVersionStrings();
+
+ for (int i=0; i < versionStrings.size(); i++) {
+ if (this.matchesSingle( (String)versionStrings.get(i) ))
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether a single version string is supported by this
+ * Version.
+ *
+ * @param version a non-compound version of the form "1.2.3[+*]"
+ */
+ private boolean matchesSingle(String version) {
+ List versionStrings = this.getVersionStrings();
+ for (int i=0; i < versionStrings.size(); i++) {
+ if ( matches(version, (String)versionStrings.get(i)) )
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns whether a single version string is supported by
+ * another single version string.
+ *
+ * @param subversion a non-compound version without "+" or "*"
+ * @param version a non-compound version optionally with "+" or "*"
+ */
+ private boolean matches(String subversion, String version) {
+ List subparts = getParts(subversion);
+ List parts = getParts(version);
+
+ int maxLength = Math.max(subversion.length(), version.length());
+ if (version.endsWith("*")) // star means rest of parts irrelevant: truncate them
+ maxLength = parts.size();
+
+ normalize(new List[] {subparts, parts}, maxLength);
+
+ if (equal(subparts, parts))
+ return true;
+
+ if (version.endsWith("+") && greater(subparts, parts))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Returns whether the parts of one version are equal to the
+ * parts of another version.
+ *
+ * @param parts1 normalized version parts
+ * @param parts2 normalized version parts
+ */
+ protected boolean equal(List parts1, List parts2) {
+ for (int i=0; i < parts1.size(); i++) {
+ if ( 0 != compare((String)parts1.get(i), (String)parts2.get(i)) )
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns whether the parts of one version are greater than
+ * the parts of another version.
+ *
+ * @param parts1 normalized version parts
+ * @param parts2 normalized version parts
+ */
+ protected boolean greater(List parts1, List parts2) {
+ //if (true) return false;
+
+ for (int i=0; i < parts1.size(); i++) {
+ // if part1 > part2 then it's a later version, so return true
+ if (compare((String)parts1.get(i), (String)parts2.get(i)) > 0)
+ return true;
+
+ // if part1 < part2 then it's a ealier version, so return false
+ if (compare((String)parts1.get(i), (String)parts2.get(i)) < 0)
+ return false;
+
+ // if equal go to next part
+ }
+
+ // all parts were equal
+ return false; // not greater than
+ }
+
+ /**
+ * Compares two parts of a version string, by value if both can
+ * be interpreted as integers or lexically otherwise. If a part
+ * is the result of normalization then it can be the Integer
+ * zero or an empty string.
+ *
+ * Returns a value equivalent to part1.compareTo(part2);
+ *
+ * @param part1 a part of a version string
+ * @param part2 a part of a version string
+ * @return comparison of the two parts
+ */
+ protected int compare(String part1, String part2) {
+ Integer number1 = new Integer(0);
+ Integer number2 = new Integer(0);
+
+ // compare as integers
+ try {
+ if (!(part1 == emptyString)) // compare to magic normalization key
+ number1 = Integer.valueOf(part1);
+
+ if (!(part2 == emptyString)) // compare to magic normalization key
+ number2 = Integer.valueOf(part2);
+
+ return number1.compareTo(number2);
+ }
+ catch (NumberFormatException ex) {
+ // means to compare as strings
+ }
+
+ if (part1 == emptyString)
+ part1 = "";
+ if (part2 == emptyString)
+ part2 = "";
+
+ return part1.compareTo(part2);
+ }
+
+ /**
+ * Normalize version strings so that they contain the same
+ * number of constituent parts.
+ *
+ * @param versions list array of parts of a version string
+ * @param maxLength truncate lists to this maximum length
+ */
+ protected void normalize(List versions[], int maxLength) {
+ int length = 0;
+ for (int i=0; i < versions.length; i++)
+ length = Math.max(length, versions[i].size());
+
+ if (length > maxLength)
+ length = maxLength;
+
+ for (int i=0; i < versions.length; i++) {
+ // remove excess elements
+ while (versions[i].size() > length)
+ versions[i].remove( versions[i].size()-1 );
+
+ // add in empty pad elements
+ while (versions[i].size() < length)
+ versions[i].add( emptyString );
+ }
+ }
+
+ /**
+ * Return the individual version strings that make up a Version.
+ */
+ protected List getVersionStrings() {
+ ArrayList strings = new ArrayList();
+
+ StringTokenizer st = new StringTokenizer(versionString, " ");
+ while (st.hasMoreTokens())
+ strings.add( st.nextToken() );
+
+ return strings;
+ }
+
+ /**
+ * Return the constituent parts of a version string.
+ *
+ * @param oneVersion a single version id string (not compound)
+ */
+ protected List getParts(String oneVersion) {
+ ArrayList strings = new ArrayList();
+
+ StringTokenizer st = new StringTokenizer(oneVersion, seperators+"+*");
+ while (st.hasMoreTokens()) {
+ strings.add( st.nextToken() );
+ }
+
+ return strings;
+ }
+
+ public String toString() {
+ return versionString;
+ }
+
+ /**
+ * Test.
+ */
+ /*
+ public static void main(String args[]) {
+ Version jvms[] = {
+ new Version("1.1* 1.3*"),
+ new Version("1.2+"),
+ };
+
+ Version versions[] = {
+ new Version("1.1"),
+ new Version("1.1.8"),
+ new Version("1.2"),
+ new Version("1.3"),
+ new Version("2.0"),
+ new Version("1.3.1"),
+ new Version("1.2.1"),
+ new Version("1.3.1-beta"),
+ new Version("1.1 1.2"),
+ new Version("1.2 1.3"),
+ };
+
+ for (int j = 0; j < jvms.length; j++) {
+ for (int v = 0; v < versions.length; v++) {
+ System.out.print( jvms[j].toString() + " " );
+ if (!jvms[j].matches(versions[v]))
+ System.out.print( "!" );
+ System.out.println( "matches " + versions[v].toString() );
+ }
+ }
+
+ System.out.println("Test completed");
+ }
+ */
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/CacheEntry.java b/netx/net/sourceforge/jnlp/cache/CacheEntry.java
new file mode 100644
index 0000000..7906e0c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/CacheEntry.java
@@ -0,0 +1,172 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.reflect.*;
+import java.security.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.util.*;
+
+/**
+ * Describes an entry in the cache.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.10 $
+ */
+public class CacheEntry {
+
+ /** the remote resource location */
+ private URL location;
+
+ /** the requested version */
+ private Version version;
+
+ /** info about the cached file */
+ private PropertiesFile properties;
+
+
+ /**
+ * Create a CacheEntry for the resources specified as a remote
+ * URL.
+ *
+ * @param location the remote resource location
+ * @param version the version of the resource
+ */
+ public CacheEntry(URL location, Version version) {
+ this.location = location;
+ this.version = version;
+
+ File infoFile = CacheUtil.getCacheFile(location, version);
+ infoFile = new File(infoFile.getPath()+".info"); // replace with something that can't be clobbered
+
+ properties = new PropertiesFile(infoFile, JNLPRuntime.getMessage("CAutoGen"));
+ }
+
+ /**
+ * Initialize the cache entry data from a connection to the
+ * remote resource (does not store data).
+ */
+ void initialize(URLConnection connection) {
+ long modified = connection.getLastModified();
+ long length = connection.getContentLength(); // an int
+
+ properties.setProperty("content-length", Long.toString(length));
+ properties.setProperty("last-modified", Long.toString(modified));
+ }
+
+ /**
+ * Returns the remote location this entry caches.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the time in the local system clock that the file was
+ * most recently checked for an update.
+ */
+ public long getLastUpdated() {
+ try {
+ return Long.parseLong(properties.getProperty("last-updated"));
+ }
+ catch (Exception ex) {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the time in the local system clock that the file was
+ * most recently checked for an update.
+ */
+ public void setLastUpdated(long updatedTime) {
+ properties.setProperty("last-updated", Long.toString(updatedTime));
+ }
+
+ /**
+ * Returns whether there is a version of the URL contents in
+ * the cache and it is up to date. This method may not return
+ * immediately.
+ *
+ * @param connection a connection to the remote URL
+ * @return whether the cache contains the version
+ */
+ public boolean isCurrent(URLConnection connection) {
+ boolean cached = isCached();
+
+ if (!cached)
+ return false;
+
+ try {
+ long remoteModified = connection.getLastModified();
+ long cachedModified = Long.parseLong(properties.getProperty("last-modified"));
+
+ if (remoteModified > 0 && remoteModified <= cachedModified)
+ return true;
+ else
+ return false;
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ return cached; // if can't connect return whether already in cache
+ }
+ }
+
+ /**
+ * Returns true if the cache has a local copy of the contents
+ * of the URL matching the specified version string.
+ *
+ * @return true if the resource is in the cache
+ */
+ public boolean isCached() {
+ File localFile = CacheUtil.getCacheFile(location, version);
+ if (!localFile.exists())
+ return false;
+
+ try {
+ long cachedLength = localFile.length();
+ long remoteLength = Long.parseLong(properties.getProperty("content-length", "-1"));
+
+ if (remoteLength >= 0 && cachedLength != remoteLength)
+ return false;
+ else
+ return true;
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ return false; // should throw?
+ }
+ }
+
+ /**
+ * Save the current information for the cache entry.
+ */
+ protected void store() {
+ properties.store();
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/CacheUtil.java b/netx/net/sourceforge/jnlp/cache/CacheUtil.java
new file mode 100644
index 0000000..9623edb
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/CacheUtil.java
@@ -0,0 +1,450 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.FileChannel;
+import java.util.*;
+import java.lang.reflect.*;
+import java.security.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.util.FileUtils;
+
+/**
+ * Provides static methods to interact with the cache, download
+ * indicator, and other utility methods.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.17 $
+ */
+public class CacheUtil {
+
+ private static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ private static String R(String key, Object param) {
+ return JNLPRuntime.getMessage(key, new Object[] {param});
+ }
+
+ /**
+ * Compares a URL using string compare of its protocol, host,
+ * port, path, query, and anchor. This method avoids the host
+ * name lookup that URL.equals does for http: protocol URLs.
+ * It may not return the same value as the URL.equals method
+ * (different hostnames that resolve to the same IP address,
+ * ie sourceforge.net and www.sourceforge.net).
+ */
+ public static boolean urlEquals(URL u1, URL u2) {
+ if (u1==u2)
+ return true;
+ if (u1==null || u2==null)
+ return false;
+
+ if (!compare(u1.getProtocol(), u2.getProtocol(), true) ||
+ !compare(u1.getHost(), u2.getHost(), true) ||
+ //u1.getDefaultPort() != u2.getDefaultPort() || // only in 1.4
+ !compare(u1.getPath(), u2.getPath(), false) ||
+ !compare(u1.getQuery(), u2.getQuery(), false) ||
+ !compare(u1.getRef(), u2.getRef(), false))
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Caches a resource and returns a URL for it in the cache;
+ * blocks until resource is cached. If the resource location is
+ * not cacheable (points to a local file, etc) then the original
+ * URL is returned.<p>
+ *
+ * @param location location of the resource
+ * @param version the version, or null
+ * @return either the location in the cache or the original location
+ */
+ public static URL getCachedResource(URL location, Version version, UpdatePolicy policy) {
+ ResourceTracker rt = new ResourceTracker();
+ rt.addResource(location, version, policy);
+ try {
+ File f = rt.getCacheFile(location);
+ return f.toURL();
+ }
+ catch (MalformedURLException ex) {
+ return location;
+ }
+ }
+
+ /**
+ * Compare strings that can be null.
+ */
+ private static boolean compare(String s1, String s2, boolean ignore) {
+ if (s1==s2)
+ return true;
+ if (s1==null || s2==null)
+ return false;
+
+ if (ignore)
+ return s1.equalsIgnoreCase(s2);
+ else
+ return s1.equals(s2);
+ }
+
+ /**
+ * Returns the Permission object necessary to access the
+ * resource, or null if no permission is needed.
+ */
+ public static Permission getReadPermission(URL location, Version version) {
+ if (CacheUtil.isCacheable(location, version)) {
+ File file = CacheUtil.getCacheFile(location, version);
+
+ return new FilePermission(file.getPath(), "read");
+ }
+ else {
+ try {
+ // this is what URLClassLoader does
+ return location.openConnection().getPermission();
+ }
+ catch (java.io.IOException ioe) {
+ // should try to figure out the permission
+ if (JNLPRuntime.isDebug())
+ ioe.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Clears the cache by deleting all the Netx cache files
+ *
+ * Note: Because of how our caching system works, deleting jars of another javaws
+ * process is using them can be quite disasterous. Hence why Launcher creates lock files
+ * and we check for those by calling {@link #okToClearCache()}
+ */
+ public static void clearCache() {
+
+ if (!okToClearCache()) {
+ System.err.println(R("CCannotClearCache"));
+ return;
+ }
+
+ File cacheDir = new File(JNLPRuntime.getBaseDir() + File.separator + "cache");
+ if (!(cacheDir.isDirectory())) {
+ return;
+ }
+
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Clearing cache directory: " + cacheDir);
+ }
+ try {
+ FileUtils.recursiveDelete(cacheDir, JNLPRuntime.getBaseDir());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns a boolean indicating if it ok to clear the netx application cache at this point
+ * @return true if the cache can be cleared at this time without problems
+ */
+ private static boolean okToClearCache() {
+ File otherJavawsRunning = new File(JNLPRuntime.NETX_RUNNING_FILE);
+ try {
+ if (otherJavawsRunning.isFile()) {
+ FileOutputStream fis = new FileOutputStream(otherJavawsRunning);
+ try {
+ FileChannel channel = fis.getChannel();
+ if (channel.tryLock() == null) {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Other instances of netx are running");
+ }
+ return false;
+ }
+
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("No other instances of netx are running");
+ }
+ return true;
+
+ } finally {
+ fis.close();
+ }
+ } else {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("No instance file found");
+ }
+ return true;
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns whether there is a version of the URL contents in the
+ * cache and it is up to date. This method may not return
+ * immediately.
+ *
+ * @param source the source URL
+ * @param version the versions to check for
+ * @param connection a connection to the URL, or null
+ * @return whether the cache contains the version
+ * @throws IllegalArgumentException if the source is not cacheable
+ */
+ public static boolean isCurrent(URL source, Version version, URLConnection connection) {
+
+ if (!isCacheable(source, version))
+ throw new IllegalArgumentException(R("CNotCacheable", source));
+
+ try {
+ if (connection == null)
+ connection = source.openConnection();
+
+ connection.connect();
+
+ CacheEntry entry = new CacheEntry(source, version); // could pool this
+ boolean result = entry.isCurrent(connection);
+
+ if (JNLPRuntime.isDebug())
+ System.out.println("isCurrent: "+source+" = "+result);
+
+ return result;
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ return isCached(source, version); // if can't connect return whether already in cache
+ }
+ }
+
+ /**
+ * Returns true if the cache has a local copy of the contents of
+ * the URL matching the specified version string.
+ *
+ * @param source the source URL
+ * @param version the versions to check for
+ * @return true if the source is in the cache
+ * @throws IllegalArgumentException if the source is not cacheable
+ */
+ public static boolean isCached(URL source, Version version) {
+ if (!isCacheable(source, version))
+ throw new IllegalArgumentException(R("CNotCacheable", source));
+
+ CacheEntry entry = new CacheEntry(source, version); // could pool this
+ boolean result = entry.isCached();
+
+ if (JNLPRuntime.isDebug())
+ System.out.println("isCached: "+source+" = "+result);
+
+ return result;
+ }
+
+ /**
+ * Returns whether the resource can be cached as a local file;
+ * if not, then URLConnection.openStream can be used to obtain
+ * the contents.
+ */
+ public static boolean isCacheable(URL source, Version version) {
+ if (source == null)
+ return false;
+
+ if (source.getProtocol().equals("file"))
+ return false;
+
+ if (source.getProtocol().equals("jar"))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns the file for the locally cached contents of the
+ * source. This method returns the file location only and does
+ * not download the resource. The latest version of the
+ * resource that matches the specified version will be returned.
+ *
+ * @param source the source URL
+ * @param version the version id of the local file
+ * @return the file location in the cache, or null if no versions cached
+ * @throws IllegalArgumentException if the source is not cacheable
+ */
+ public static File getCacheFile(URL source, Version version) {
+ // ensure that version is an version id not version string
+
+ if (!isCacheable(source, version))
+ throw new IllegalArgumentException(R("CNotCacheable", source));
+
+ try {
+ File localFile = urlToPath(source, "cache");
+ localFile.getParentFile().mkdirs();
+
+ return localFile;
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ return null;
+ }
+ }
+
+ /**
+ * Returns a buffered output stream open for writing to the
+ * cache file.
+ *
+ * @param source the remote location
+ * @param version the file version to write to
+ */
+ public static OutputStream getOutputStream(URL source, Version version) throws IOException {
+ File localFile = getCacheFile(source, version);
+ OutputStream out = new FileOutputStream(localFile);
+
+ return new BufferedOutputStream(out);
+ }
+
+ /**
+ * Copies from an input stream to an output stream. On
+ * completion, both streams will be closed. Streams are
+ * buffered automatically.
+ */
+ public static void streamCopy(InputStream is, OutputStream os) throws IOException {
+ if (!(is instanceof BufferedInputStream))
+ is = new BufferedInputStream(is);
+
+ if (!(os instanceof BufferedOutputStream))
+ os = new BufferedOutputStream(os);
+
+ try {
+ byte b[] = new byte[4096];
+ while (true) {
+ int c = is.read(b, 0, b.length);
+ if (c == -1)
+ break;
+
+ os.write(b, 0, c);
+ }
+ }
+ finally {
+ is.close();
+ os.close();
+ }
+ }
+
+ /**
+ * Converts a URL into a local path string within the runtime's
+ * base directory.
+ *
+ * @param location the url
+ * @param subdir subdirectory under the base directory
+ * @return the file
+ */
+ public static File urlToPath(URL location, String subdir) {
+ StringBuffer path = new StringBuffer();
+
+ if (subdir != null) {
+ path.append(subdir);
+ path.append(File.separatorChar);
+ }
+
+ path.append(location.getProtocol());
+ path.append(File.separatorChar);
+ path.append(location.getHost());
+ path.append(File.separatorChar);
+ path.append(location.getPath().replace('/', File.separatorChar));
+
+ return new File(JNLPRuntime.getBaseDir(), FileUtils.sanitizePath(path.toString()));
+ }
+
+
+ /**
+ * Waits until the resources are downloaded, while showing a
+ * progress indicator.
+ *
+ * @param tracker the resource tracker
+ * @param resources the resources to wait for
+ * @param title name of the download
+ */
+ public static void waitForResources(ApplicationInstance app, ResourceTracker tracker, URL resources[], String title) {
+ DownloadIndicator indicator = JNLPRuntime.getDefaultDownloadIndicator();
+ DownloadServiceListener listener = null;
+
+ try {
+ if (indicator == null) {
+ tracker.waitForResources(resources, 0);
+ return;
+ }
+
+ // see if resources can be downloaded very quickly; avoids
+ // overhead of creating display components for the resources
+ if (tracker.waitForResources(resources, indicator.getInitialDelay()))
+ return;
+
+ // only resources not starting out downloaded are displayed
+ List urlList = new ArrayList();
+ for (int i=0; i < resources.length; i++) {
+ if (!tracker.checkResource(resources[i]))
+ urlList.add(resources[i]);
+ }
+ URL undownloaded[] = (URL[]) urlList.toArray( new URL[urlList.size()] );
+
+ listener = indicator.getListener(app, title, undownloaded);
+
+ do {
+ long read = 0;
+ long total = 0;
+
+ for (int i=0; i < undownloaded.length; i++) {
+ // add in any -1's; they're insignificant
+ total += tracker.getTotalSize(undownloaded[i]);
+ read += tracker.getAmountRead(undownloaded[i]);
+ }
+
+ int percent = (int)( (100*read)/Math.max(1,total) );
+
+ for (int i=0; i < undownloaded.length; i++)
+ listener.progress(undownloaded[i], "version",
+ tracker.getAmountRead(undownloaded[i]),
+ tracker.getTotalSize(undownloaded[i]),
+ percent);
+ }
+ while (!tracker.waitForResources(resources, indicator.getUpdateRate()));
+
+ // make sure they read 100% until indicator closes
+ for (int i=0; i < undownloaded.length; i++)
+ listener.progress(undownloaded[i], "version",
+ tracker.getTotalSize(undownloaded[i]),
+ tracker.getTotalSize(undownloaded[i]),
+ 100);
+
+ }
+ catch (InterruptedException ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+ finally {
+ if (listener != null)
+ indicator.disposeListener(listener);
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java b/netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java
new file mode 100644
index 0000000..bd1cbd9
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java
@@ -0,0 +1,320 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.util.*;
+import java.util.List;
+import javax.swing.*;
+import javax.swing.Timer;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * Show the progress of downloads.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.3 $
+ */
+public class DefaultDownloadIndicator implements DownloadIndicator {
+
+ // todo: rewrite this to cut down on size/complexity; smarter
+ // panels (JList, renderer) understand resources instead of
+ // nested panels and grid-bag mess.
+
+ // todo: fix bug where user closes download box and it
+ // never(?) reappears.
+
+ // todo: UI for user to cancel/restart download
+
+ // todo: this should be synchronized at some point but conflicts
+ // aren't very likely.
+
+ private static String downloading = JNLPRuntime.getMessage("CDownloading");
+ private static String complete = JNLPRuntime.getMessage("CComplete");
+
+ /** time to wait after completing but before window closes */
+ private static final int CLOSE_DELAY = 750;
+
+ /** the display window */
+ private static JFrame frame;
+
+ /** shared constraint */
+ static GridBagConstraints vertical;
+ static GridBagConstraints verticalIndent;
+ static {
+ vertical = new GridBagConstraints();
+ vertical.gridwidth = GridBagConstraints.REMAINDER;
+ vertical.weightx = 1.0;
+ vertical.fill = GridBagConstraints.HORIZONTAL;
+ vertical.anchor = GridBagConstraints.WEST;
+
+ verticalIndent = (GridBagConstraints) vertical.clone();
+ verticalIndent.insets = new Insets(0, 10, 3, 0);
+ }
+
+ /**
+ * Return the update rate.
+ */
+ public int getUpdateRate() {
+ return 150; //ms
+ }
+
+ /**
+ * Return the initial delay before obtaining a listener.
+ */
+ public int getInitialDelay() {
+ return 300; //ms
+ }
+
+ /**
+ * Return a download service listener that displays the progress
+ * in a shared download info window.
+ *
+ * @param app the downloading application, or null if N/A
+ * @param downloadName name identifying the download to the user
+ * @param resources initial urls to display (not required)
+ */
+ public DownloadServiceListener getListener(ApplicationInstance app, String downloadName, URL resources[]) {
+ DownloadPanel result = new DownloadPanel(downloadName);
+
+ if (frame == null) {
+ frame = new JFrame(downloading+"...");
+ frame.getContentPane().setLayout(new GridBagLayout());
+ }
+
+ if (resources != null)
+ for (int i=0; i < resources.length; i++)
+ result.addProgressPanel(resources[i], null);
+
+ frame.getContentPane().add(result, vertical);
+ frame.pack();
+
+ if (!frame.isVisible()) {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(frame.getGraphicsConfiguration());
+ Dimension screen = new Dimension(screenSize.width - insets.left ,
+ screenSize.height - insets.top);
+ frame.setLocation(screen.width-frame.getWidth(),
+ screen.height-frame.getHeight());
+ }
+
+ frame.show();
+
+ return result;
+ }
+
+ /**
+ * Remove a download service listener that was obtained by
+ * calling the getDownloadListener method from the shared
+ * download info window.
+ */
+ public void disposeListener(final DownloadServiceListener listener) {
+ if (!(listener instanceof DownloadPanel))
+ return;
+
+ ActionListener hider = new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ if (frame.getContentPane().getComponentCount() == 1)
+ frame.hide();
+
+ frame.getContentPane().remove((DownloadPanel) listener);
+ frame.pack();
+ }
+ };
+
+ Timer timer = new Timer(CLOSE_DELAY, hider);
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+
+
+ /**
+ * Groups the url progress in a panel.
+ */
+ static class DownloadPanel extends JPanel implements DownloadServiceListener {
+
+ /** the download name */
+ private String downloadName;
+
+ /** Downloading part: */
+ private JLabel header = new JLabel();
+
+ /** list of URLs being downloaded */
+ private List urls = new ArrayList();
+
+ /** list of ProgressPanels */
+ private List panels = new ArrayList();
+
+
+ /**
+ * Create a new download panel for with the specified download
+ * name.
+ */
+ protected DownloadPanel(String downloadName) {
+ setLayout(new GridBagLayout());
+
+ this.downloadName = downloadName;
+ this.add(header, vertical);
+ header.setFont(header.getFont().deriveFont(Font.BOLD));
+
+ setOverallPercent(0);
+ }
+
+ /**
+ * Add a ProgressPanel for a URL.
+ */
+ protected void addProgressPanel(URL url, String version) {
+ if (!urls.contains(url)) {
+ ProgressPanel panel = new ProgressPanel(url, version);
+
+ add(panel, verticalIndent);
+ frame.pack();
+
+ urls.add(url);
+ panels.add(panel);
+ }
+ }
+
+ /**
+ * Update the download progress of a url.
+ */
+ protected void update(final URL url, final String version, final long readSoFar, final long total, final int overallPercent) {
+ Runnable r = new Runnable() {
+ public void run() {
+ if (!urls.contains(url))
+ addProgressPanel(url, version);
+
+ setOverallPercent(overallPercent);
+
+ ProgressPanel panel = (ProgressPanel) panels.get(urls.indexOf(url));
+ panel.setProgress(readSoFar, total);
+ panel.repaint();
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ /**
+ * Sets the overall percent completed.
+ */
+ public void setOverallPercent(int percent) {
+ // don't get whole string from resource and sub in
+ // values because it'll be doing a MessageFormat for
+ // each update.
+ header.setText(downloading+" "+downloadName+": "+percent+"% "+complete+".");
+ }
+
+ /**
+ * Called when a download failed.
+ */
+ public void downloadFailed(URL url, String version) {
+ update(url, version, -1, -1, -1);
+ }
+
+ /**
+ * Called when a download has progressed.
+ */
+ public void progress(URL url, String version, long readSoFar, long total, int overallPercent) {
+ update(url, version, readSoFar, total, overallPercent);
+ }
+
+ /**
+ * Called when an archive is patched.
+ */
+ public void upgradingArchive(URL url, String version, int patchPercent, int overallPercent) {
+ update(url, version, patchPercent, 100, overallPercent);
+ }
+
+ /**
+ * Called when a download is being validated.
+ */
+ public void validating(URL url, String version, long entry, long total, int overallPercent) {
+ update(url, version, entry, total, overallPercent);
+ }
+
+ };
+
+
+
+ /**
+ * A progress bar with the URL next to it.
+ */
+ static class ProgressPanel extends JPanel {
+ private JPanel bar = new JPanel();
+
+ private long total;
+ private long readSoFar;
+
+ ProgressPanel(URL url, String version) {
+ JLabel location = new JLabel(" "+url.getHost()+"/"+url.getFile());
+
+ bar.setMinimumSize(new Dimension(80,15));
+ bar.setPreferredSize(new Dimension(80,15));
+ bar.setOpaque(false);
+
+ setLayout(new GridBagLayout());
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.weightx = 0.0;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.gridwidth = GridBagConstraints.RELATIVE;
+ add(bar, gbc);
+
+ gbc.insets = new Insets(0, 3, 0, 0);
+ gbc.weightx = 1.0;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.anchor = GridBagConstraints.WEST;
+ add(location, gbc);
+ }
+
+ public void setProgress(long readSoFar, long total) {
+ this.readSoFar = readSoFar;
+ this.total = total;
+ }
+
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+
+ int x = bar.getX();
+ int y = bar.getY();
+ int h = bar.getHeight();
+ int w = bar.getWidth();
+
+ if (readSoFar <= 0 || total <= 0) {
+ // make barber pole
+ }
+ else {
+ double progress = (double)readSoFar / (double)total;
+ int divide = (int)(w * progress);
+
+ g.setColor(Color.white);
+ g.fillRect(x, y, w, h);
+ g.setColor(Color.blue);
+ g.fillRect(x+1, y+1, divide-1, h-1);
+ }
+ }
+ };
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/DownloadIndicator.java b/netx/net/sourceforge/jnlp/cache/DownloadIndicator.java
new file mode 100644
index 0000000..a6eecb8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/DownloadIndicator.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.util.*;
+import java.util.List;
+import javax.swing.*;
+import javax.swing.Timer;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * A DownloadIndicator creates DownloadServiceListeners that are
+ * notified of resources being transferred and their progress.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.8 $
+ */
+public interface DownloadIndicator {
+
+ /**
+ * Return a download service listener that displays the progress
+ * of downloading resources. Update messages may be reported
+ * for URLs that are not included initially.<p>
+ *
+ * Progress messages are sent as if the DownloadServiceListener
+ * were listening to a DownloadService request. The listener
+ * will receive progress messages from time to time during the
+ * download. <p>
+ *
+ * @param app JNLP application downloading the files, or null if not applicable
+ * @param downloadName name identifying the download to the user
+ * @param resources initial urls to display, empty if none known at start
+ */
+ public DownloadServiceListener getListener(ApplicationInstance app,
+ String downloadName,
+ URL resources[]);
+
+ /**
+ * Indicates that a download service listener that was obtained
+ * from the getDownloadListener method will no longer be used.
+ * This method can be used to ensure that progress dialogs are
+ * properly removed once a particular download is finished.
+ *
+ * @param listener the listener that is no longer in use
+ */
+ public void disposeListener(DownloadServiceListener listener);
+
+ /**
+ * Return the desired time in milliseconds between updates.
+ * Updates are not guarenteed to occur based on this value; for
+ * example, they may occur based on the download percent or some
+ * other factor.
+ *
+ * @return rate in milliseconds, must be &gt;= 0
+ */
+ public int getUpdateRate();
+
+ /**
+ * Return a time in milliseconds to wait for a download to
+ * complete before obtaining a listener for the download. This
+ * value can be used to skip lengthy operations, such as
+ * initializing a GUI, for downloads that complete quickly. The
+ * getListener method is not called if the download completes
+ * in less time than the returned delay.
+ *
+ * @return delay in milliseconds, must be &gt;= 0
+ */
+ public int getInitialDelay();
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/Resource.java b/netx/net/sourceforge/jnlp/cache/Resource.java
new file mode 100644
index 0000000..172220f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/Resource.java
@@ -0,0 +1,269 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.util.*;
+
+/**
+ * Information about a single resource to download.
+ * This class tracks the downloading of various resources of a
+ * JNLP file to local files. It can be used to download icons,
+ * jnlp and extension files, jars, and jardiff files using the
+ * version based protocol or any file using the basic download
+ * protocol.<p>
+ *
+ * Resources can be put into download groups by specifying a part
+ * name for the resource. The resource tracker can also be
+ * configured to prefetch resources, which are downloaded in the
+ * order added to the media tracker.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public class Resource {
+
+ // todo: fix resources to handle different versions
+
+ // todo: IIRC, any resource is checked for being up-to-date
+ // only once, regardless of UpdatePolicy. verify and fix.
+
+ /** status bits */
+ public static final int UNINITIALIZED = 0;
+ public static final int CONNECT = 1;
+ public static final int CONNECTING = 2;
+ public static final int CONNECTED = 4;
+ public static final int DOWNLOAD = 8;
+ public static final int DOWNLOADING = 16;
+ public static final int DOWNLOADED = 32;
+ public static final int ERROR = 64;
+ public static final int STARTED = 128; // enqueued or being worked on
+
+ /** list of weak references of resources currently in use */
+ private static WeakList resources = new WeakList();
+
+ /** weak list of trackers monitoring this resource */
+ private WeakList trackers = new WeakList();
+
+ /** the remote location of the resource */
+ URL location;
+
+ /** the local file downloaded to */
+ File localFile;
+
+ /** the requested version */
+ Version requestVersion;
+
+ /** the version downloaded from server */
+ Version downloadVersion;
+
+ /** connection to resource */
+ URLConnection connection;
+
+ /** amount in bytes transferred */
+ long transferred = 0;
+
+ /** total size of the resource, or -1 if unknown */
+ long size = -1;
+
+ /** the status of the resource */
+ int status = UNINITIALIZED;
+
+ /** Update policy for this resource */
+ UpdatePolicy updatePolicy;
+
+ /**
+ * Create a resource.
+ */
+ private Resource(URL location, Version requestVersion, UpdatePolicy updatePolicy) {
+ this.location = location;
+ this.requestVersion = requestVersion;
+ this.updatePolicy = updatePolicy;
+ }
+
+ /**
+ * Return a shared Resource object representing the given
+ * location and version.
+ */
+ public static Resource getResource(URL location, Version requestVersion, UpdatePolicy updatePolicy) {
+ synchronized (resources) {
+ Resource resource = new Resource(location, requestVersion, updatePolicy);
+
+ int index = resources.indexOf(resource);
+ if (index >= 0) { // return existing object
+ Resource result = (Resource) resources.get(index);
+ if (result != null)
+ return result;
+ }
+
+ resources.add(resource);
+ resources.trimToSize();
+
+ return resource;
+ }
+ }
+
+ /**
+ * Returns the remote location of the resource.
+ */
+ public URL getLocation() {
+ return location;
+ }
+
+ /**
+ * Returns the tracker that first created or monitored the
+ * resource, or null if no trackers are monitoring the resource.
+ */
+ ResourceTracker getTracker() {
+ synchronized (trackers) {
+ List t = trackers.hardList();
+ if (t.size() > 0)
+ return (ResourceTracker) t.get(0);
+
+ return null;
+ }
+ }
+
+ /**
+ * Returns true if any of the specified flags are set.
+ */
+ public boolean isSet(int flag) {
+ if (flag == UNINITIALIZED)
+ return status == UNINITIALIZED;
+ else
+ return (status & flag) != 0;
+ }
+
+ /**
+ * Returns the update policy for this resource
+ *
+ * @return The update policy
+ */
+ public UpdatePolicy getUpdatePolicy() {
+ return this.updatePolicy;
+ }
+
+ /**
+ * Returns a human-readable status string.
+ */
+ private String getStatusString(int flag) {
+ StringBuffer result = new StringBuffer();
+
+ if (flag == 0) result.append("<> ");
+ if ((flag & CONNECT) != 0) result.append("CONNECT ");
+ if ((flag & CONNECTING) != 0) result.append("CONNECTING ");
+ if ((flag & CONNECTED) != 0) result.append("CONNECTED ");
+ if ((flag & DOWNLOAD) != 0) result.append("DOWNLOAD ");
+ if ((flag & DOWNLOADING) != 0) result.append("DOWNLOADING ");
+ if ((flag & DOWNLOADED) != 0) result.append("DOWNLOADED ");
+ if ((flag & ERROR) != 0) result.append("ERROR ");
+ if ((flag & STARTED) != 0) result.append("STARTED ");
+
+ return result.deleteCharAt(result.length()-1).toString();
+ }
+
+ /**
+ * Changes the status by clearing the flags in the first
+ * parameter and setting the flags in the second. This method
+ * is synchronized on this resource.
+ */
+ public void changeStatus(int clear, int add) {
+ int orig = 0;
+
+ synchronized(this) {
+ orig = status;
+
+ this.status &= ~clear;
+ this.status |= add;
+ }
+
+ if (JNLPRuntime.isDebug())
+ if (status != orig) {
+ System.out.print("Status: "+getStatusString(status));
+ if ((status & ~orig) != 0)
+ System.out.print(" +("+getStatusString(status & ~orig)+")");
+ if ((~status & orig) != 0)
+ System.out.print(" -("+getStatusString(~status & orig)+")");
+ System.out.println(" @ "+location.getPath());
+ }
+ }
+
+ /**
+ * Removes the tracker to the list of trackers monitoring this
+ * resource.
+ */
+ public void removeTracker(ResourceTracker tracker) {
+ synchronized (trackers) {
+ trackers.remove(tracker);
+ trackers.trimToSize();
+ }
+ }
+
+ /**
+ * Adds the tracker to the list of trackers monitoring this
+ * resource.
+ */
+ public void addTracker(ResourceTracker tracker) {
+ synchronized (trackers) {
+ List t = trackers.hardList(); // prevent GC between contains and add
+ if (!t.contains(tracker))
+ trackers.add(tracker);
+
+ trackers.trimToSize();
+ }
+ }
+
+ /**
+ * Instructs the trackers monitoring this resource to fire a
+ * download event.
+ */
+ protected void fireDownloadEvent() {
+ List send;
+
+ synchronized (trackers) {
+ send = trackers.hardList();
+ }
+
+ for (int i=0; i < send.size(); i++) {
+ ResourceTracker rt = (ResourceTracker) send.get(i);
+ rt.fireDownloadEvent(this);
+ }
+ }
+
+ public boolean equals(Object other) {
+ if (other instanceof Resource) {
+ // this prevents the URL handler from looking up the IP
+ // address and doing name resolution; much faster so less
+ // time spent in synchronized addResource determining if
+ // Resource is already in a tracker, and better for offline
+ // mode on some OS.
+ return CacheUtil.urlEquals(location, ((Resource)other).location);
+ }
+ return false;
+ }
+
+ public String toString() {
+ return "location="+location.toString() + " state="+getStatusString(status);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/ResourceTracker.java b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java
new file mode 100644
index 0000000..d65c840
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java
@@ -0,0 +1,1049 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Pack200;
+import java.util.jar.Pack200.Unpacker;
+import java.util.zip.GZIPInputStream;
+
+import net.sourceforge.jnlp.Version;
+import net.sourceforge.jnlp.event.DownloadEvent;
+import net.sourceforge.jnlp.event.DownloadListener;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.WeakList;
+
+/**
+ * This class tracks the downloading of various resources of a
+ * JNLP file to local files in the cache. It can be used to
+ * download icons, jnlp and extension files, jars, and jardiff
+ * files using the version based protocol or any file using the
+ * basic download protocol (jardiff and version not implemented
+ * yet).<p>
+ *
+ * The resource tracker can be configured to prefetch resources,
+ * which are downloaded in the order added to the media
+ * tracker.<p>
+ *
+ * Multiple threads are used to download and cache resources that
+ * are actively being waited for (blocking a caller) or those that
+ * have been started downloading by calling the startDownload
+ * method. Resources that are prefetched are downloaded one at a
+ * time and only if no other trackers have requested downloads.
+ * This allows the tracker to start downloading many items without
+ * using many system resources, but still quickly download items
+ * as needed.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.22 $
+ */
+public class ResourceTracker {
+
+ // todo: use event listener arrays instead of lists
+
+ // todo: see if there is a way to set the socket options just
+ // for use by the tracker so checks for updates don't hang for
+ // a long time.
+
+ // todo: ability to restart/retry a hung download
+
+ // todo: move resource downloading/processing code into Resource
+ // class, threading stays in ResourceTracker
+
+ // todo: get status method? and some way to convey error status
+ // to the caller.
+
+ // todo: might make a tracker be able to download more than one
+ // version of a resource, but probably not very useful.
+
+
+ // defines
+ // ResourceTracker.Downloader (download threads)
+
+ // separately locks on (in order of aquire order, ie, sync on prefetch never syncs on lock):
+ // lock, prefetch, this.resources, each resource, listeners
+
+ /** notified on initialization or download of a resource */
+ private static Object lock = new Integer(0); // used to lock static structures
+
+ // shortcuts
+ private static final int UNINITIALIZED = Resource.UNINITIALIZED;
+ private static final int CONNECT = Resource.CONNECT;
+ private static final int CONNECTING = Resource.CONNECTING;
+ private static final int CONNECTED = Resource.CONNECTED;
+ private static final int DOWNLOAD = Resource.DOWNLOAD;
+ private static final int DOWNLOADING = Resource.DOWNLOADING;
+ private static final int DOWNLOADED = Resource.DOWNLOADED;
+ private static final int ERROR = Resource.ERROR;
+ private static final int STARTED = Resource.STARTED;
+
+ /** max threads */
+ private static final int maxThreads = 5;
+
+ /** running threads */
+ private static int threads = 0;
+
+ /** weak list of resource trackers with resources to prefetch */
+ private static WeakList prefetchTrackers = new WeakList();
+
+ /** resources requested to be downloaded */
+ private static ArrayList queue = new ArrayList();
+
+ /** resource trackers threads are working for (used for load balancing across multi-tracker downloads) */
+ private static ArrayList active = new ArrayList(); //
+
+ /** the resources known about by this resource tracker */
+ private List resources = new ArrayList();
+
+ /** download listeners for this tracker */
+ private List listeners = new ArrayList();
+
+ /** whether to download parts before requested */
+ private boolean prefetch;
+
+
+ /**
+ * Creates a resource tracker that does not prefetch resources.
+ */
+ public ResourceTracker() {
+ this(false);
+ }
+
+ /**
+ * Creates a resource tracker.
+ *
+ * @param prefetch whether to download resources before requested.
+ */
+ public ResourceTracker(boolean prefetch) {
+ this.prefetch = prefetch;
+
+ if (prefetch) {
+ synchronized (prefetchTrackers) {
+ prefetchTrackers.add(this);
+ prefetchTrackers.trimToSize();
+ }
+ }
+ }
+
+ /**
+ * Add a resource identified by the specified location and
+ * version. The tracker only downloads one version of a given
+ * resource per instance (ie cannot download both versions 1 and
+ * 2 of a resource in the same tracker).
+ *
+ * @param location the location of the resource
+ * @param version the resource version
+ * @param updatePolicy whether to check for updates if already in cache
+ */
+ public void addResource(URL location, Version version, UpdatePolicy updatePolicy) {
+ if (location == null)
+ throw new IllegalArgumentException("location==null");
+
+ Resource resource = Resource.getResource(location, version, updatePolicy);
+ boolean downloaded = false;
+
+ synchronized (resources) {
+ if (resources.contains(resource))
+ return;
+ resource.addTracker(this);
+ resources.add(resource);
+ }
+
+ // checkCache may take a while (loads properties file). this
+ // should really be synchronized on resources, but the worst
+ // case should be that the resource will be updated once even
+ // if unnecessary.
+ downloaded = checkCache(resource, updatePolicy);
+
+ synchronized (lock) {
+ if (!downloaded)
+ if (prefetch && threads == 0) // existing threads do pre-fetch when queue empty
+ startThread();
+ }
+ }
+
+ /**
+ * Removes a resource from the tracker. This method is useful
+ * to allow memory to be reclaimed, but calling this method is
+ * not required as resources are reclaimed when the tracker is
+ * collected.
+ *
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public void removeResource(URL location) {
+ synchronized (resources) {
+ Resource resource = getResource(location);
+
+ if (resource != null) {
+ resources.remove(resource);
+ resource.removeTracker(this);
+ }
+
+ // should remove from queue? probably doesn't matter
+ }
+ }
+
+ /**
+ * Check the cache for a resource, and initialize the resource
+ * as already downloaded if found. <p>
+ *
+ * @param updatePolicy whether to check for updates if already in cache
+ * @return whether the resource are already downloaded
+ */
+ private boolean checkCache(Resource resource, UpdatePolicy updatePolicy) {
+ if (!CacheUtil.isCacheable(resource.location, resource.downloadVersion)) {
+ // pretend that they are already downloaded; essentially
+ // they will just 'pass through' the tracker as if they were
+ // never added (for example, not affecting the total download size).
+ synchronized (resource) {
+ resource.changeStatus(0, DOWNLOADED|CONNECTED|STARTED);
+ }
+ fireDownloadEvent(resource);
+ return true;
+ }
+
+ if (updatePolicy != UpdatePolicy.ALWAYS && updatePolicy != UpdatePolicy.FORCE) { // save loading entry props file
+ CacheEntry entry = new CacheEntry(resource.location, resource.downloadVersion);
+
+ if (entry.isCached() && !updatePolicy.shouldUpdate(entry)) {
+ if (JNLPRuntime.isDebug())
+ System.out.println("not updating: "+resource.location);
+
+ synchronized (resource) {
+ resource.localFile = CacheUtil.getCacheFile(resource.location, resource.downloadVersion);
+ resource.size = resource.localFile.length();
+ resource.transferred = resource.localFile.length();
+ resource.changeStatus(0, DOWNLOADED|CONNECTED|STARTED);
+ }
+ fireDownloadEvent(resource);
+ return true;
+ }
+ }
+
+ if (updatePolicy == UpdatePolicy.FORCE) { // ALWAYS update
+ // When we are "always" updating, we update for each instance. Reset resource status.
+ resource.changeStatus(Integer.MAX_VALUE, 0);
+ }
+
+ // may or may not be cached, but check update when connection
+ // is open to possibly save network communication time if it
+ // has to be downloaded, and allow this call to return quickly
+ return false;
+ }
+
+ /**
+ * Adds the listener to the list of objects interested in
+ * receivind DownloadEvents.<p>
+ *
+ * @param location the resource to add a callback for
+ * @param runnable the runnable to call when resource is completed
+ */
+ public void addDownloadListener(DownloadListener listener) {
+ synchronized (listeners) {
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a download listener.
+ */
+ public void removeDownloadListener(DownloadListener listener) {
+ synchronized (listeners) {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Fires the download event corresponding to the resource's
+ * state. This method is typicall called by the Resource itself
+ * on each tracker that is monitoring the resource. Do not call
+ * this method with any locks because the listeners may call
+ * back to this ResourceTracker.
+ */
+ protected void fireDownloadEvent(Resource resource) {
+ DownloadListener l[] = null;
+ synchronized (listeners) {
+ l = (DownloadListener[]) listeners.toArray(new DownloadListener[0]);
+ }
+
+ int status;
+ synchronized (resource) {
+ status = resource.status;
+ }
+
+ DownloadEvent event = new DownloadEvent(this, resource);
+ for (int i=0; i < l.length; i++) {
+ if (0 != ((ERROR|DOWNLOADED) & status))
+ l[i].downloadCompleted(event);
+ else if (0 != (DOWNLOADING & status))
+ l[i].downloadStarted(event);
+ else if (0 != (CONNECTING & status))
+ l[i].updateStarted(event);
+ }
+ }
+
+ /**
+ * Returns a URL pointing to the cached location of the
+ * resource, or the resource itself if it is a non-cacheable
+ * resource.<p>
+ *
+ * If the resource has not downloaded yet, the method will block
+ * until it has been transferred to the cache.<p>
+ *
+ * @param location the resource location
+ * @return the resource, or null if it could not be downloaded
+ * @throws IllegalArgumentException if the resource is not being tracked
+ * @see CacheUtil#isCacheable
+ */
+ public URL getCacheURL(URL location) {
+ try {
+ File f = getCacheFile(location);
+ if (f != null)
+ return f.toURL();
+ }
+ catch (MalformedURLException ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+
+ return location;
+ }
+
+ /**
+ * Returns a file containing the downloaded resource. If the
+ * resource is non-cacheable then null is returned unless the
+ * resource is a local file (the original file is returned).<p>
+ *
+ * If the resource has not downloaded yet, the method will block
+ * until it has been transferred to the cache.<p>
+ *
+ * @param location the resource location
+ * @return a local file containing the resource, or null
+ * @throws IllegalArgumentException if the resource is not being tracked
+ * @see CacheUtil#isCacheable
+ */
+ public File getCacheFile(URL location) {
+ try {
+ Resource resource = getResource(location);
+ if (!resource.isSet(DOWNLOADED|ERROR))
+ waitForResource(location, 0);
+
+ if (resource.isSet(ERROR))
+ return null;
+
+ if (resource.localFile != null)
+ return resource.localFile;
+
+ if (location.getProtocol().equalsIgnoreCase("file")) {
+ File file = new File(location.getFile());
+ if (file.exists())
+ return file;
+ }
+
+ return null;
+ }
+ catch (InterruptedException ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ return null; // need an error exception to throw
+ }
+ }
+
+ /**
+ * Returns an input stream that reads the contents of the
+ * resource. For non-cacheable resources, an InputStream that
+ * reads from the source location is returned. Otherwise the
+ * InputStream reads the cached resource.<p>
+ *
+ * This method will block while the resource is downloaded to
+ * the cache.
+ *
+ * @throws IOException if there was an error opening the stream
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public InputStream getInputStream(URL location) throws IOException {
+ try {
+ Resource resource = getResource(location);
+ if (!resource.isSet(DOWNLOADED|ERROR))
+ waitForResource(location, 0);
+
+ if (resource.localFile != null)
+ return new FileInputStream(resource.localFile);
+
+ return resource.location.openStream();
+ }
+ catch (InterruptedException ex) {
+ throw new IOException("wait was interrupted");
+ }
+ }
+
+ /**
+ * Wait for a group of resources to be downloaded and made
+ * available locally.
+ *
+ * @param urls the resources to wait for
+ * @param timeout the time in ms to wait before returning, 0 for no timeout
+ * @return whether the resources downloaded before the timeout
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public boolean waitForResources(URL urls[], long timeout) throws InterruptedException {
+ Resource resources[] = new Resource[ urls.length ];
+
+ synchronized(resources) {
+ // keep the lock so getResource doesn't have to aquire it each time
+ for (int i=0; i < urls.length; i++)
+ resources[i] = getResource(urls[i]);
+ }
+
+ if (resources.length > 0)
+ return wait(resources, timeout);
+
+ return true;
+ }
+
+ /**
+ * Wait for a particular resource to be downloaded and made
+ * available.
+ *
+ * @param location the resource to wait for
+ * @param timeout the timeout, or 0 to wait until completed
+ * @return whether the resource downloaded before the timeout
+ * @throws InterruptedException if another thread interrupted the wait
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public boolean waitForResource(URL location, long timeout) throws InterruptedException {
+ return wait(new Resource[] { getResource(location) }, timeout);
+ }
+
+ /**
+ * Returns the number of bytes downloaded for a resource.
+ *
+ * @param location the resource location
+ * @return the number of bytes transferred
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public long getAmountRead(URL location) {
+ // not atomic b/c transferred is a long, but so what (each
+ // byte atomic? so probably won't affect anything...)
+ return getResource(location).transferred;
+ }
+
+ /**
+ * Returns whether a resource is available for use (ie, can be
+ * accessed with the getCacheFile method).
+ *
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public boolean checkResource(URL location) {
+ return getResource(location).isSet(DOWNLOADED|ERROR); // isSet atomic
+ }
+
+ /**
+ * Starts loading the resource if it is not already being
+ * downloaded or already cached. Resources started downloading
+ * using this method may download faster than those prefetched
+ * by the tracker because the tracker will only prefetch one
+ * resource at a time to conserve system resources.
+ *
+ * @return true if the resource is already downloaded (or an error occurred)
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public boolean startResource(URL location) {
+ Resource resource = getResource(location);
+
+ return startResource(resource);
+ }
+
+ /**
+ * Sets the resource status to connect and download, and
+ * enqueues the resource if not already started.
+ *
+ * @return true if the resource is already downloaded (or an error occurred)
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ private boolean startResource(Resource resource) {
+ boolean enqueue = false;
+
+ synchronized (resource) {
+ if (resource.isSet(ERROR))
+ return true;
+
+ enqueue = !resource.isSet(STARTED);
+
+ if (!resource.isSet(CONNECTED | CONNECTING))
+ resource.changeStatus(0, CONNECT|STARTED);
+ if (!resource.isSet(DOWNLOADED | DOWNLOADING))
+ resource.changeStatus(0, DOWNLOAD|STARTED);
+
+ if (!resource.isSet(DOWNLOAD|CONNECT))
+ enqueue = false;
+ }
+
+ if (enqueue)
+ queueResource(resource);
+
+ return !enqueue;
+ }
+
+ /**
+ * Returns the number of total size in bytes of a resource, or
+ * -1 it the size is not known.
+ *
+ * @param location the resource location
+ * @return the number of bytes, or -1
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ public long getTotalSize(URL location) {
+ return getResource(location).size; // atomic
+ }
+
+ /**
+ * Start a new download thread if there are not too many threads
+ * already running.<p>
+ *
+ * Calls to this method should be synchronized on lock.
+ */
+ protected void startThread() {
+ if (threads < maxThreads) {
+ threads++;
+
+ Thread thread = new Thread(new Downloader());
+ thread.start();
+ }
+ }
+
+ /**
+ * A thread is ending, called by the thread itself.<p>
+ *
+ * Calls to this method should be synchronized.
+ */
+ private void endThread() {
+ threads--;
+
+ if (threads < 0) {
+ // this should never happen but try to recover
+ threads = 0;
+
+ if (queue.size() > 0) // if any on queue make sure a thread is running
+ startThread(); // look into whether this could create a loop
+
+ throw new RuntimeException("tracker threads < 0");
+ }
+
+ if (threads == 0) {
+ synchronized (prefetchTrackers) {
+ queue.trimToSize(); // these only accessed by threads so no sync needed
+ active.clear(); // no threads so no trackers actively downloading
+ active.trimToSize();
+ prefetchTrackers.trimToSize();
+ }
+ }
+ }
+
+ /**
+ * Add a resource to the queue and start a thread to download or
+ * initialize it.
+ */
+ private void queueResource(Resource resource) {
+ synchronized (lock) {
+ if (!resource.isSet(CONNECT|DOWNLOAD))
+ throw new IllegalArgumentException("Invalid resource state (resource: "+resource+")");
+
+ queue.add(resource);
+ startThread();
+ }
+ }
+
+ /**
+ * Process the resource by either downloading it or initializing
+ * it.
+ */
+ private void processResource(Resource resource) {
+ boolean doConnect = false;
+ boolean doDownload = false;
+
+ synchronized (resource) {
+ if (resource.isSet(CONNECTING))
+ doConnect = true;
+ }
+ if (doConnect)
+ initializeResource(resource);
+
+ synchronized (resource) {
+ // return to queue if we just initalized but it still needs
+ // to download (not cached locally / out of date)
+ if (resource.isSet(DOWNLOAD)) // would be DOWNLOADING if connected before this method
+ queueResource(resource);
+
+ if (resource.isSet(DOWNLOADING))
+ doDownload = true;
+ }
+ if (doDownload)
+ downloadResource(resource);
+ }
+
+ /**
+ * Downloads a resource to a file, uncompressing it if required
+ *
+ * @param resource the resource to download
+ */
+ private void downloadResource(Resource resource) {
+ resource.fireDownloadEvent(); // fire DOWNLOADING
+
+ try {
+ // create out second in case in does not exist
+ URLConnection con = getVersionedResourceURL(resource).openConnection();
+ con.addRequestProperty("Accept-Encoding", "pack200-gzip, gzip");
+
+ con.connect();
+
+ /*
+ * We dont really know what we are downloading. If we ask for
+ * foo.jar, the server might send us foo.jar.pack.gz or foo.jar.gz
+ * instead. So we save the file with the appropriate extension
+ */
+ URL downloadLocation = resource.location;
+
+ String contentEncoding = con.getContentEncoding();
+
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Content encoding for " + resource.location + ": "
+ + contentEncoding);
+ }
+
+ if (contentEncoding != null) {
+ if (contentEncoding.equals("gzip")) {
+ downloadLocation = new URL(downloadLocation.toString() + ".gz");
+ } else if (contentEncoding.equals("pack200-gzip")) {
+ downloadLocation = new URL(downloadLocation.toString() + ".pack.gz");
+ }
+ }
+
+ InputStream in = new BufferedInputStream(con.getInputStream());
+ OutputStream out = CacheUtil.getOutputStream(downloadLocation, resource.downloadVersion);
+ byte buf[] = new byte[1024];
+ int rlen;
+
+ while (-1 != (rlen = in.read(buf))) {
+ resource.transferred += rlen;
+ out.write(buf, 0, rlen);
+ }
+
+ in.close();
+ out.close();
+
+ // explicitly close the URLConnection.
+ if (con instanceof HttpURLConnection)
+ ((HttpURLConnection)con).disconnect();
+
+ /*
+ * If the file was compressed, uncompress it.
+ */
+ if (contentEncoding != null) {
+ if (contentEncoding.equals("gzip")) {
+ GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream(CacheUtil
+ .getCacheFile(downloadLocation, resource.downloadVersion)));
+ InputStream inputStream = new BufferedInputStream(gzInputStream);
+
+ BufferedOutputStream outputStream = new BufferedOutputStream(
+ new FileOutputStream(CacheUtil.getCacheFile(resource.location,
+ resource.downloadVersion)));
+
+ while (-1 != (rlen = inputStream.read(buf))) {
+ outputStream.write(buf, 0, rlen);
+ }
+
+ outputStream.close();
+ inputStream.close();
+ gzInputStream.close();
+
+ } else if (contentEncoding.equals("pack200-gzip")) {
+ GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream(
+ CacheUtil.getCacheFile(downloadLocation, resource.downloadVersion)));
+ InputStream inputStream = new BufferedInputStream(gzInputStream);
+
+ JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(
+ CacheUtil.getCacheFile(resource.location, resource.downloadVersion)));
+
+ Unpacker unpacker = Pack200.newUnpacker();
+ unpacker.unpack(inputStream, outputStream);
+
+ outputStream.close();
+ inputStream.close();
+ gzInputStream.close();
+ }
+ }
+
+ resource.changeStatus(DOWNLOADING, DOWNLOADED);
+ synchronized(lock) {
+ lock.notifyAll(); // wake up wait's to check for completion
+ }
+ resource.fireDownloadEvent(); // fire DOWNLOADED
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ resource.changeStatus(0, ERROR);
+ synchronized(lock) {
+ lock.notifyAll(); // wake up wait's to check for completion
+ }
+ resource.fireDownloadEvent(); // fire ERROR
+ }
+ }
+
+ /**
+ * Open a URL connection and get the content length and other
+ * fields.
+ */
+ private void initializeResource(Resource resource) {
+ resource.fireDownloadEvent(); // fire CONNECTING
+
+ try {
+ File localFile = CacheUtil.getCacheFile(resource.location, resource.downloadVersion);
+
+ // connect
+ URLConnection connection = getVersionedResourceURL(resource).openConnection(); // this won't change so should be okay unsynchronized
+ connection.addRequestProperty("Accept-Encoding", "pack200-gzip, gzip");
+
+ int size = connection.getContentLength();
+ boolean current = CacheUtil.isCurrent(resource.location, resource.requestVersion, connection) && resource.getUpdatePolicy() != UpdatePolicy.FORCE;
+
+ synchronized(resource) {
+ resource.localFile = localFile;
+ // resource.connection = connection;
+ resource.size = size;
+ resource.changeStatus(CONNECT|CONNECTING, CONNECTED);
+
+ // check if up-to-date; if so set as downloaded
+ if (current)
+ resource.changeStatus(DOWNLOAD|DOWNLOADING, DOWNLOADED);
+ }
+
+ // update cache entry
+ CacheEntry entry = new CacheEntry(resource.location, resource.requestVersion);
+ if (!current)
+ entry.initialize(connection);
+
+ entry.setLastUpdated(System.currentTimeMillis());
+ entry.store();
+
+ synchronized(lock) {
+ lock.notifyAll(); // wake up wait's to check for completion
+ }
+ resource.fireDownloadEvent(); // fire CONNECTED
+
+ // explicitly close the URLConnection.
+ if (connection instanceof HttpURLConnection)
+ ((HttpURLConnection)connection).disconnect();
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ resource.changeStatus(0, ERROR);
+ synchronized(lock) {
+ lock.notifyAll(); // wake up wait's to check for completion
+ }
+ resource.fireDownloadEvent(); // fire ERROR
+ }
+ }
+
+ /**
+ * Returns the versioned url for a resource
+ * @param resource the resource to get the url for
+ */
+ private URL getVersionedResourceURL(Resource resource) {
+ String actualLocation = resource.location.getProtocol() + "://"
+ + resource.location.getHost();
+ if (resource.location.getPort() != -1) {
+ actualLocation += ":" + resource.location.getPort();
+ }
+ actualLocation += resource.location.getPath();
+ if (resource.requestVersion != null
+ && resource.requestVersion.isVersionId()) {
+ actualLocation += "?version-id=" + resource.requestVersion;
+ }
+ URL versionedURL;
+ try {
+ versionedURL = new URL(actualLocation);
+ } catch (MalformedURLException e) {
+ return resource.location;
+ }
+ return versionedURL;
+ }
+
+
+ /**
+ * Pick the next resource to download or initialize. If there
+ * are no more resources requested then one is taken from a
+ * resource tracker with prefetch enabled.<p>
+ *
+ * The resource state is advanced before it is returned
+ * (CONNECT-&gt;CONNECTING).<p>
+ *
+ * Calls to this method should be synchronized on lock.<p>
+ *
+ * @return the resource to initialize or download, or null
+ */
+ private static Resource selectNextResource() {
+ Resource result;
+
+ // pick from queue
+ result = selectByFlag(queue, CONNECT, ERROR); // connect but not error
+ if (result == null)
+ result = selectByFlag(queue, DOWNLOAD, ERROR|CONNECT|CONNECTING);
+
+ // remove from queue if found
+ if (result != null)
+ queue.remove(result);
+
+ // prefetch if nothing found so far and this is the last thread
+ if (result == null && threads == 1)
+ result = getPrefetch();
+
+ if (result == null)
+ return null;
+
+ synchronized (result) {
+ if (result.isSet(CONNECT)) {
+ result.changeStatus(CONNECT, CONNECTING);
+ }
+ else if (result.isSet(DOWNLOAD)) {
+ // only download if *not* connecting, when done connecting
+ // select next will pick up the download part. This makes
+ // all requested connects happen before any downloads, so
+ // the size is known as early as possible.
+ result.changeStatus(DOWNLOAD, DOWNLOADING);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the next resource to be prefetched before
+ * requested.<p>
+ *
+ * Calls to this method should be synchronized on lock.<p>
+ */
+ private static Resource getPrefetch() {
+ Resource result = null;
+ Resource alternate = null;
+
+ // first find one to initialize
+ synchronized (prefetchTrackers) {
+ for (int i=0; i < prefetchTrackers.size() && result == null; i++) {
+ ResourceTracker tracker = (ResourceTracker) prefetchTrackers.get(i);
+ if (tracker == null)
+ continue;
+
+ synchronized (tracker.resources) {
+ result = selectByFlag(tracker.resources, UNINITIALIZED, ERROR);
+
+ if (result == null && alternate == null)
+ alternate = selectByFlag(tracker.resources, CONNECTED, ERROR|DOWNLOADED|DOWNLOADING|DOWNLOAD);
+ }
+ }
+ }
+
+ // if none to initialize, switch to download
+ if (result == null)
+ result = alternate;
+
+ if (result == null)
+ return null;
+
+ synchronized (result) {
+ ResourceTracker tracker = result.getTracker();
+ if (tracker == null)
+ return null; // GC of tracker happened between above code and here
+
+ // prevents startResource from putting it on queue since
+ // we're going to return it.
+ result.changeStatus(0, STARTED);
+
+ tracker.startResource(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Selects a resource from the source list that has the
+ * specified flag set.<p>
+ *
+ * Calls to this method should be synchronized on lock and
+ * source list.<p>
+ */
+ private static Resource selectByFlag(List source, int flag, int notflag) {
+ Resource result = null;
+ int score = Integer.MAX_VALUE;
+
+ for (int i=0; i < source.size(); i++) {
+ Resource resource = (Resource) source.get(i);
+ boolean selectable = false;
+
+ synchronized (resource) {
+ if (resource.isSet(flag) && !resource.isSet(notflag))
+ selectable = true;
+ }
+
+ if (selectable) {
+ int activeCount = 0;
+
+ for (int j=0; j < active.size(); j++)
+ if ((ResourceTracker)active.get(j) == resource.getTracker())
+ activeCount++;
+
+ // try to spread out the downloads so that a slow host
+ // won't monopolize the downloads
+ if (activeCount < score) {
+ result = resource;
+ score = activeCount;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the resource matching the specified URL.
+ *
+ * @throws IllegalArgumentException if the resource is not being tracked
+ */
+ private Resource getResource(URL location) {
+ synchronized (resources) {
+ for (int i=0; i < resources.size(); i++) {
+ Resource resource = (Resource) resources.get(i);
+
+ if (CacheUtil.urlEquals(resource.location, location))
+ return resource;
+ }
+ }
+
+ throw new IllegalArgumentException("Location does not specify a resource being tracked.");
+ }
+
+ /**
+ * Wait for some resources.
+ *
+ * @param resources the resources to wait for
+ * @param timeout the timeout, or 0 to wait until completed
+ * @returns true if the resources were downloaded or had errors,
+ * false if the timeout was reached
+ * @throws InterruptedException if another thread interrupted the wait
+ */
+ private boolean wait(Resource resources[], long timeout) throws InterruptedException {
+ long startTime = System.currentTimeMillis();
+
+ // start them downloading / connecting in background
+ for (int i=0; i < resources.length; i++)
+ startResource(resources[i]);
+
+ // wait for completion
+ while (true) {
+ boolean finished = true;
+
+ synchronized (lock) {
+ // check for completion
+ for (int i=0; i < resources.length; i++) {
+ //NetX Deadlocking may be solved by removing this
+ //synch block.
+ synchronized (resources[i]) {
+ if (!resources[i].isSet(DOWNLOADED | ERROR)) {
+ finished = false;
+ break;
+ }
+ }
+ }
+ if (finished)
+ return true;
+
+ // wait
+ long waitTime = 0;
+
+ if (timeout > 0) {
+ waitTime = timeout - (System.currentTimeMillis()-startTime);
+ if (waitTime <= 0)
+ return false;
+ }
+
+ lock.wait(waitTime);
+ }
+ }
+ }
+
+
+ // inner classes
+
+ /**
+ * This class downloads and initializes the queued resources.
+ */
+ class Downloader implements Runnable {
+ Resource resource = null;
+
+ public void run() {
+ while (true) {
+ synchronized (lock) {
+ // remove from active list, used for load balancing
+ if (resource != null)
+ active.remove(resource.getTracker());
+
+ resource = selectNextResource();
+
+ if (resource == null) {
+ endThread();
+ break;
+ }
+
+ // add to active list, used for load balancing
+ active.add(resource.getTracker());
+ }
+
+ try {
+ processResource(resource);
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+ }
+ // should have a finally in case some exception is thrown by
+ // selectNextResource();
+ }
+ };
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/UpdatePolicy.java b/netx/net/sourceforge/jnlp/cache/UpdatePolicy.java
new file mode 100644
index 0000000..157e38c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/UpdatePolicy.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2002 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.cache;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.reflect.*;
+import java.security.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.util.*;
+/**
+ * A policy that determines when a resource should be checked for
+ * an updated version.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.3 $
+ */
+public class UpdatePolicy {
+
+ // todo: implement session updating
+
+ // todo: doesn't seem to work in the same JVM, probably because
+ // Resource is being held by a tracker so it isn't collected;
+ // then next time a tracker adds the resource even if
+ // shouldUpdate==true it's state is already marked
+ // CONNECTED|DOWNLOADED. Let the resource be collected or reset
+ // to UNINITIALIZED.
+
+ public static UpdatePolicy ALWAYS = new UpdatePolicy(0);
+ public static UpdatePolicy SESSION = new UpdatePolicy(-1);
+ public static UpdatePolicy FORCE = new UpdatePolicy(Long.MIN_VALUE);
+ public static UpdatePolicy NEVER = new UpdatePolicy(Long.MAX_VALUE);
+
+ private long timeDiff = -1;
+
+
+ /**
+ * Create a new update policy; this policy always updates the
+ * entry unless the shouldUpdate method is overridden.
+ */
+ public UpdatePolicy() {
+ }
+
+ /**
+ * Create an update policy that only checks a file for being
+ * updated if it has not been checked for longer than the
+ * specified time.
+ *
+ * @param timeDiff how long in ms until update needed
+ */
+ public UpdatePolicy(long timeDiff) {
+ this.timeDiff = timeDiff;
+ }
+
+ /**
+ * Returns whether the resource should be checked for being
+ * up-to-date.
+ */
+ public boolean shouldUpdate(CacheEntry entry) {
+ long updated = entry.getLastUpdated();
+ long current = System.currentTimeMillis();
+
+ if (current - updated >= timeDiff)
+ return true;
+ else
+ return false;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/cache/package.html b/netx/net/sourceforge/jnlp/cache/package.html
new file mode 100644
index 0000000..4911ec0
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/cache/package.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+
+This package contains the JNLP cache.
+
+<h2>Package Specification</h2>
+
+<ul>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/download-spec.html">JNLP specification</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+<li><a target="_top" href="http://jnlp.sourceforge.net/netx/">Netx JNLP Client</a>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/">Java Web Start JNLP Client</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
+
+
diff --git a/netx/net/sourceforge/jnlp/event/ApplicationEvent.java b/netx/net/sourceforge/jnlp/event/ApplicationEvent.java
new file mode 100644
index 0000000..10d6844
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/event/ApplicationEvent.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2002 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.event;
+
+import java.util.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * This event is sent when an application is terminated.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public class ApplicationEvent extends EventObject {
+
+ /** the application instance */
+ private ApplicationInstance application;
+
+
+ /**
+ * Creates a launch event for the specified application
+ * instance.
+ *
+ * @param source the application instance
+ */
+ public ApplicationEvent(ApplicationInstance source) {
+ super(source);
+
+ this.application = source;
+ }
+
+ /**
+ * Returns the application instance.
+ */
+ public ApplicationInstance getApplication() {
+ return application;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/event/ApplicationListener.java b/netx/net/sourceforge/jnlp/event/ApplicationListener.java
new file mode 100644
index 0000000..b000b2c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/event/ApplicationListener.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2002 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.event;
+
+import java.util.*;
+
+
+/**
+ * The listener that is notified when an application instance is
+ * terminated.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public interface ApplicationListener extends EventListener {
+
+ /**
+ * Invoked when the application is destroyed.
+ */
+ public void applicationDestroyed(ApplicationEvent applicationEvent);
+
+}
diff --git a/netx/net/sourceforge/jnlp/event/DownloadEvent.java b/netx/net/sourceforge/jnlp/event/DownloadEvent.java
new file mode 100644
index 0000000..a5697d1
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/event/DownloadEvent.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2002 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.event;
+
+import java.net.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.cache.*;
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * This event is sent during the launch of an
+ * application.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.3 $
+ */
+public class DownloadEvent extends EventObject {
+
+ /** the tracker */
+ private ResourceTracker tracker;
+
+ /** the resource */
+ private Resource resource;
+
+
+ /**
+ * Creates a launch event for the specified application
+ * instance.
+ *
+ * @param source the resource tracker
+ * @param resource the resource
+ */
+ public DownloadEvent(ResourceTracker source, Resource resource) {
+ super(source);
+
+ this.tracker = source;
+ this.resource = resource;
+ }
+
+ /**
+ * Returns the tracker that owns the resource.
+ */
+ public ResourceTracker getTracker() {
+ return tracker;
+ }
+
+ /**
+ * Returns the location of the resource being downloaded.
+ */
+ public URL getResourceLocation() {
+ return resource.getLocation();
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/event/DownloadListener.java b/netx/net/sourceforge/jnlp/event/DownloadListener.java
new file mode 100644
index 0000000..b9816b9
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/event/DownloadListener.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2002 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.event;
+
+import java.util.*;
+
+
+/**
+ * The listener that is notified of the state of resources being
+ * downloaded by a ResourceTracker. Events may be delivered on a
+ * background thread, and the event methods should complete
+ * quickly so that they do not slow down other downloading in
+ * progress by tying up a thread.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.3 $
+ */
+public interface DownloadListener extends EventListener {
+
+ /**
+ * Called when a resource is checked for being up-to-date.
+ */
+ public void updateStarted(DownloadEvent downloadEvent);
+
+ /**
+ * Called when a download starts.
+ */
+ public void downloadStarted(DownloadEvent downloadEvent);
+
+ /**
+ * Called when a download completed or there was an error.
+ */
+ public void downloadCompleted(DownloadEvent downloadEvent);
+
+}
diff --git a/netx/net/sourceforge/jnlp/event/package.html b/netx/net/sourceforge/jnlp/event/package.html
new file mode 100644
index 0000000..834ad07
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/event/package.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+
+This package contains the JNLP events.
+
+<h2>Package Specification</h2>
+
+<ul>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/download-spec.html">JNLP specification</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+<li><a target="_top" href="http://jnlp.sourceforge.net/netx/">Netx JNLP Client</a>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/">Java Web Start JNLP Client</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
+
+
diff --git a/netx/net/sourceforge/jnlp/package.html b/netx/net/sourceforge/jnlp/package.html
new file mode 100644
index 0000000..6e5240f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/package.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+
+This package contains the classes that represent the parts of a
+Java Network Launching Protocol (JNLP) file as objects, and a way
+to launch a JNLP file as an application, applet, or installer.
+
+<h2>Package Specification</h2>
+
+<ul>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/download-spec.html">JNLP specification</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+<li><a target="_top" href="http://jnlp.sourceforge.net/netx/">Netx JNLP Client</a>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/">Java Web Start JNLP Client</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
+
+
diff --git a/netx/net/sourceforge/jnlp/resources/Manifest.mf b/netx/net/sourceforge/jnlp/resources/Manifest.mf
new file mode 100644
index 0000000..b647e13
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/Manifest.mf
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Created-By: jnlp.sourceforge.net
+Specification-Title: netx JNLP client
+Sealed: true
+Main-Class: net.sourceforge.jnlp.runtime.Boot13
+
diff --git a/netx/net/sourceforge/jnlp/resources/Messages.properties b/netx/net/sourceforge/jnlp/resources/Messages.properties
new file mode 100644
index 0000000..a962916
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties
@@ -0,0 +1,182 @@
+# Default (English) UI messages for netx
+# L=Launcher, B=Boot, P=Parser, C=cache S=security
+#
+# General
+NullParameter=Null parameter
+ButOk=OK
+ButCancel=\ Cancel\
+ButBrowse=Browse...
+AFileOnTheMachine=a file on the machine
+
+# LS - Severity
+LSMinor=Minor
+LSFatal=Fatal
+
+# LC - Category
+LCSystem=System Error
+LCExternalLaunch=External Launch Error
+LCFileFormat=File Format Error
+LCReadError=Read Error
+LCClient=Application Error
+LCLaunching=Launch Error
+LCNotSupported=Unsupported Feature
+LCInit=Initialization Error
+
+LAllThreadGroup=All JNLP applications
+LNullUpdatePolicy=Update policy cannot be null.
+
+LThreadInterrupted=Thread interrupted while waiting for file to launch.
+LThreadInterruptedInfo=
+LCouldNotLaunch=Could not launch JNLP file.
+LCouldNotLaunchInfo=
+LCantRead=Could not read or parse the JNLP file.
+LCantReadInfo=
+LNullLocation=Could not determine .jnlp file location.
+LNullLocationInfo=An attempt was made to launch a JNLP file in another JVM, but the file could not be located. In order to launch in an external JVM, the runtime must be able to locate the .jnlp file either in the local filesystem or on a server.
+LNetxJarMissing=Could not determine location of netx.jar.
+LNetxJarMissingInfo=An attempt was made to lauch a JNLP file in another JVM, but the netx.jar could not be located. In order to launch in an external JVM, the runtime must be able to locate the netx.jar file.
+LNotToSpec=JNLP file not strictly to spec.
+LNotToSpecInfo=The JNLP file contains data that is prohibited by the JNLP specification. The runtime can attempt to ignore the invalid information and continue launching the file.
+LNotApplication=Not an application file.
+LNotApplicationInfo=An attempt was made to load a non-application file as an application.
+LNotApplet=Not an applet file.
+LNotAppletInfo=An attempt was made to load a non-applet file as an applet.
+LNoInstallers=Installers not supported.
+LNoInstallersInfo=JNLP installer files are not yet supported.
+LInitApplet=Could not initialize applet.
+LInitAppletInfo=
+LInitApplication=Could not initialize application.
+LInitApplicationInfo=
+LNotLaunchable=Not a launchable JNLP file.
+LNotLaunchableInfo=File must be a JNLP application, applet, or installer type.
+LCantDetermineMainClass=Unknown Main-Class.
+LCantDetermineMainClassInfo=Could not determine the main class for this application.
+LUnsignedJarWithSecurity=Cannot grant permissions to unsigned jars.
+LUnsignedJarWithSecurityInfo=Application requested security permissions, but jars are not signed.
+LSignedAppJarUsingUnsignedJar=Signed application using unsigned jars.
+LSignedAppJarUsingUnsignedJarInfo=The main application jar is signed, but some of the jars it is using aren't.
+
+JNotApplet=File is not an applet.
+JNotApplication=File is not an application.
+JNotComponent=File is not a component.
+JNotInstaller=File is not an installer.
+JInvalidExtensionDescriptor=Extension does not refer to a component or installer (name={1}, location={2}).
+
+LNotVerified=Jars not verified.
+LCancelOnUserRequest=Canceled on user request.
+LFatalVerification=A fatal error occurred while trying to verify jars.
+LFatalVerificationInfo=
+
+LNotVerifiedDialog=Not all jars could be verified.
+LAskToContinue=Would you still like to continue running this application?
+
+# Parser
+PInvalidRoot=Root node is not a jnlp node
+PSpecUnsupported=Spec version not supported (supports {0})
+PNoResources=No resources section defined
+PUntrustedNative=nativelib element cannot be specified unless a trusted environment is requested.
+PExtensionHasJ2SE=j2se element cannot be specified in a component extension file.
+PInnerJ2SE=j2se element cannot be specified within a j2se element.
+PTwoMains=Duplicate main JAR defined in a resources element (there can be only one)
+PNativeHasMain=Cannot specify main attribute on native JARs.
+PNoInfoElement=No information section defined
+PTwoDescriptions=Duplicate description of kind {0}
+PSharing=Element "sharing-allowed" is illegal in a standard JNLP file
+PTwoSecurity=Only one security element allowed per JNLPFile.
+PEmptySecurity=Security element specified but does not contain a permissions element.
+PTwoDescriptors=Only one application descriptor element allowed per JNLPFile.
+PTwoDesktops=Only one desktop element allowed
+PTwoMenus=Only one menu element allowed
+PTwoTitles=Only one title element allowed
+PTwoIcons=Only one icon element allowed
+PTwoUpdates=Only one update element is allowed
+PUnknownApplet=Unknown Applet
+PBadWidth=Invalid applet width.
+PBadHeight=Invalid applet height.
+PUrlNotInCodebase=Relative URL does not specify a subdirectory of the codebase. (node={0}, href={1}, base={2})
+PBadRelativeUrl=Invalid relative URL (node={0}, href={1}, base={2})
+PBadNonrelativeUrl=Invalid non-relative URL (node={0}, href={0}).
+PNeedsAttribute=The {0} element must specify a {1} attribute.
+PBadXML=Invalid XML document syntax.
+PBadHeapSize=Invalid value for heap size ({0})
+
+# Runtime
+BLaunchAbout=Launching about window...
+BNeedsFile=Must specify a .jnlp file
+BFileLoc=JNLP file location
+BArgNA=Arguments not used for this type of JNLP file (ignored).
+BParamNA=Parameters not used for this type of JNLP file (ignored).
+BBadProp=Incorrect property format {0} (should be key=value)
+BBadParam=Incorrect parameter format {0} (should be name=value)
+BNoDir=Directory {0} does not exist.
+BNoBase=No base directory (contains cache and other data)
+RNoResource=Missing Resource: {0}
+RShutdown=This exception to prevent shutdown of JVM, but the process has been terminated.
+RExitTaken=Exit class already set and caller is not exit class.
+RCantReplaceSM=Changing the SecurityManager is not allowed.
+RDenyStopped=Stopped applications have no permissions.
+RExitNoApp=Can not exit the JVM because the current application cannot be determined.
+RNoLockDir=Unable to create locks directory ({0})
+RNestedJarExtration=Unable to extract nested jar.
+RUnexpected=Unexpected {0} at {1}
+
+# Boot options, message should be shorter than this ---------------->
+BOUsage=javaws [-run-options] <jnlp file>
+BOUsage2=javaws [-control-options]
+BOBasedir = Directory where the cache is kept.
+BOJnlp = Location of JNLP file to launch (url or file).
+BOArg = Adds an application argument before launching.
+BOParam = Adds an applet parameter before launching.
+BOProperty = Sets a system property before launching.
+BOUpdate = Update check if seconds since last checked.
+BOLicense = Display the GPL license and exit.
+BOVerbose = Enable verbose output.
+BOAbout = Shows a sample application.
+BONosecurity= Disables the secure runtime environment.
+BONoupdate = Disables checking for updates.
+BOHeadless = Disables download window, other UIs.
+BOStrict = Enables strict checking of JNLP file format.
+BOViewer = Shows the trusted certificate viewer.
+BOUmask = Sets the umask for files created by an application.
+BXnofork = Do not create another JVM.
+BXclearcache= Clean the JNLP application cache.
+BOHelp = Print this message and exit.
+
+# Cache
+CAutoGen=automatically generated - do not edit
+CNotCacheable={0} is not a cacheable resource
+CDownloading=Downloading
+CComplete=Complete
+CChooseCache=Choose a cache directory...
+CChooseCacheInfo=Netx needs a location for storing cache files.
+CChooseCacheDir=Cache directory
+CCannotClearCache=Can not clear cache at this time
+
+# Security
+SFileReadAccess=The application has requested read access to {0}. Do you want to allow this action?
+SFileWriteAccess=The application has requested write access to {0}. Do you want to allow this action?
+SDesktopShortcut=The application has requested permission to create a desktop launcher. Do you want to allow this action?
+SSigUnverified=The application's digital signature cannot be verified. Do you want to run the application?
+SSigVerified=The application's digital signature has been verified. Do you want to run the application?
+SSignatureError=The application's digital signature has an error. Do you want to run the application?
+SUntrustedSource=The digital signature could not be verified by a trusted source. Only run if you trust the origin of the application.
+STrustedSource=The digital signature has been validated by a trusted source.
+SClipboardReadAccess=The application has requested read-only access to the system clipboard. Do you want to allow this action?
+SClipboardWriteAccess=The application has requested write-only access to the system clipboard. Do you want to allow this action?
+SPrinterAccess=The application has requested printer access. Do you want to allow this action?
+SNetworkAccess=The application has requested permission to establish connections to {0}. Do you want to allow this action?
+
+# Security - used for the More Information dialog
+SBadKeyUsage=Resources contain entries whose signer certificate's KeyUsage extension doesn't allow code signing.
+SBadExtendedKeyUsage=Resources contain entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.
+SBadNetscapeCertType=Resources contain entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.
+SHasUnsignedEntry=Resources contain unsigned entries which have not been integrity-checked.
+SHasExpiredCert=The digital signature has expired.
+SHasExpiringCert=Resources contain entries whose signer certificate will expire within six months.
+SNotYetValidCert=Resources contain entries whose signer certificate is not yet valid.
+SUntrustedCertificate=The digital signature was generated with an untrusted certificate.
+STrustedCertificate=The digital signature was generated with a trusted certificate.
+SCNMisMatch=The expected hostname for this certificate is: "{0}"<BR>The address being connected to is: "{1}"
+SRunWithoutRestrictions=This application will be run without the security restrictions normally provided by java.
+
+
diff --git a/netx/net/sourceforge/jnlp/resources/about.jnlp b/netx/net/sourceforge/jnlp/resources/about.jnlp
new file mode 100644
index 0000000..e5bf1e1
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/about.jnlp
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<jnlp spec="1.0" href="about.jnlp" codebase="http://icedtea.classpath.org/netx/">
+ <information>
+ <title>About window for NetX</title>
+ <vendor>NetX</vendor>
+ <homepage href="http://jnlp.sourceforge.net/netx/"/>
+ <description>Displays information about NetX</description>
+ <offline/>
+ </information>
+ <resources>
+ <j2se version="1.4+"/>
+ <jar href="about.jar"/>
+ </resources>
+ <security>
+ <all-permissions/>
+ </security>
+ <application-desc main-class="net.sourceforge.jnlp.about.Main">
+ </application-desc>
+</jnlp>
+
diff --git a/netx/net/sourceforge/jnlp/resources/default.jnlp b/netx/net/sourceforge/jnlp/resources/default.jnlp
new file mode 100644
index 0000000..b266101
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/default.jnlp
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<jnlp spec="1.0" href="default.jnlp" codebase="http://icedtea.classpath.org/netx/">
+ <information>
+ <title>About window for NetX</title>
+ <vendor>NetX</vendor>
+ <homepage href="http://jnlp.sourceforge.net/netx/"/>
+ <description>Displays information about NetX</description>
+ <offline/>
+ </information>
+ <resources>
+ <j2se version="1.4+"/>
+ <jar href="default.jar"/>
+ </resources>
+ <security>
+ <all-permissions/>
+ </security>
+ <application-desc main-class="net.sourceforge.jnlp.about.Main">
+ </application-desc>
+</jnlp>
+
diff --git a/netx/net/sourceforge/jnlp/resources/info-small.png b/netx/net/sourceforge/jnlp/resources/info-small.png
new file mode 100644
index 0000000..14e04a7
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/info-small.png
Binary files differ
diff --git a/netx/net/sourceforge/jnlp/resources/install.png b/netx/net/sourceforge/jnlp/resources/install.png
new file mode 100644
index 0000000..d5c56ea
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/install.png
Binary files differ
diff --git a/netx/net/sourceforge/jnlp/resources/netx-icon.png b/netx/net/sourceforge/jnlp/resources/netx-icon.png
new file mode 100644
index 0000000..bdbe264
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/netx-icon.png
Binary files differ
diff --git a/netx/net/sourceforge/jnlp/resources/warning-small.png b/netx/net/sourceforge/jnlp/resources/warning-small.png
new file mode 100644
index 0000000..7478363
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/warning-small.png
Binary files differ
diff --git a/netx/net/sourceforge/jnlp/resources/warning.png b/netx/net/sourceforge/jnlp/resources/warning.png
new file mode 100644
index 0000000..ecf86f8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/resources/warning.png
Binary files differ
diff --git a/netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java b/netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java
new file mode 100644
index 0000000..84ee28f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.awt.*;
+
+/**
+ * Thread group for a JNLP application.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public class AppThreadGroup extends ThreadGroup {
+
+ /** the app */
+ private ApplicationInstance app = null;
+
+
+ /**
+ * Creates new JavaAppThreadGroup
+ *
+ * @param name of the App
+ */
+ public AppThreadGroup(ThreadGroup parent, String name) {
+ super(parent, name);
+ }
+
+ /**
+ * Sets the JNLP app this group is for; can only be called once.
+ */
+ public void setApplication(ApplicationInstance app) {
+ if (this.app != null)
+ throw new IllegalStateException("Application can only be set once");
+
+ this.app = app;
+ }
+
+ /**
+ * Returns the JNLP app for this thread group.
+ */
+ public ApplicationInstance getApplication() {
+ return app;
+ }
+
+ /**
+ * Handle uncaught exceptions for the app.
+ */
+ public void uncaughtException(Thread t, Throwable e) {
+ super.uncaughtException(t, e);
+ }
+
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/AppletAudioClip.java b/netx/net/sourceforge/jnlp/runtime/AppletAudioClip.java
new file mode 100644
index 0000000..3b62e0d
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/AppletAudioClip.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.net.*;
+import java.io.*;
+import java.applet.*;
+import javax.sound.sampled.*;
+
+// based on Deane Richan's AppletAudioClip
+
+/**
+ * An applet audio clip using the javax.sound API.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.8 $
+ */
+public class AppletAudioClip implements AudioClip {
+
+ /** the clip */
+ private Clip clip;
+
+
+ /**
+ * Creates new AudioClip. If the clip cannot be opened no
+ * exception is thrown, instead the methods of the AudioClip
+ * return without performing any operations.
+ *
+ * @param location the clip location
+ */
+ public AppletAudioClip(URL location) {
+ try {
+ AudioInputStream stream = AudioSystem.getAudioInputStream(location);
+
+ clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
+ clip.open(stream);
+ }
+ catch (Exception ex) {
+ System.err.println("Error loading sound:"+location.toString());
+ clip = null;
+ }
+ }
+
+ /**
+ * Plays the clip in a continuous loop until the stop method is
+ * called.
+ */
+ public void loop() {
+ if (clip == null)
+ return;
+
+ clip.loop(Clip.LOOP_CONTINUOUSLY);
+ }
+
+ /**
+ * Plays the clip from the beginning.
+ */
+ public void play() {
+ if (clip == null)
+ return;
+
+ // applet audio clip resets to beginning when played again
+ clip.stop();
+ clip.setFramePosition(0);
+ clip.start();
+ }
+
+ /**
+ * Stops playing the clip.
+ */
+ public void stop() {
+ if (clip == null)
+ return;
+
+ clip.stop();
+ }
+
+ /**
+ * Stops playing the clip and disposes it; the clip cannot be
+ * played after being disposed.
+ */
+ void dispose() {
+ if (clip != null) {
+ clip.stop();
+ clip.flush();
+ clip.close();
+ }
+
+ clip = null;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java b/netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java
new file mode 100644
index 0000000..cd37c44
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java
@@ -0,0 +1,354 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+import java.lang.reflect.InvocationTargetException;
+import java.net.*;
+import java.io.*;
+import javax.swing.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.util.*;
+
+/**
+ * The applet environment including stub, context, and frame. The
+ * default environment puts the applet in a non-resiable frame;
+ * this can be changed by obtaining the frame and setting it
+ * resizable.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.12 $
+ */
+public class AppletEnvironment implements AppletContext, AppletStub {
+
+ /** the JNLP file */
+ private JNLPFile file;
+
+ /** the applet instance */
+ private AppletInstance appletInstance;
+
+ /** the applet */
+ private Applet applet;
+
+ /** the parameters */
+ private Map parameters;
+
+ /** the applet container */
+ private Container cont;
+
+ /** weak references to the audio clips */
+ private WeakList weakClips = new WeakList();
+
+ /** whether the applet has been started / displayed */
+ private boolean appletStarted = false;
+
+ /** whether the applet has been destroyed */
+ private boolean destroyed = false;
+
+
+ /**
+ * Create a new applet environment for the applet specified by
+ * the JNLP file.
+ */
+ public AppletEnvironment(JNLPFile file, final AppletInstance appletInstance, Container cont) {
+ this.file = file;
+ this.appletInstance = appletInstance;
+ this.applet = appletInstance.getApplet();
+
+ parameters = file.getApplet().getParameters();
+ this.cont = cont;
+ }
+
+ /**
+ * Create a new applet environment for the applet specified by
+ * the JNLP file, in a new frame.
+ */
+ public AppletEnvironment(JNLPFile file, final AppletInstance appletInstance) {
+ this(file, appletInstance, null);
+
+ Frame frame = new Frame(file.getApplet().getName() + " - Applet");
+ frame.setResizable(false);
+
+ appletInstance.addWindow(frame);
+ // may not need this once security manager can close windows
+ // that do not have app code on the stack
+ WindowListener closer = new WindowAdapter() {
+ public void windowClosing(WindowEvent event) {
+ appletInstance.destroy();
+ System.exit(0);
+ }
+ };
+ frame.addWindowListener(closer);
+ this.cont = frame;
+ }
+
+ /**
+ * Checks whether the applet has been destroyed, and throws an
+ * IllegalStateException if the applet has been destroyed of.
+ *
+ * @throws IllegalStateException
+ */
+ private void checkDestroyed() {
+ if (destroyed)
+ throw new IllegalStateException("Illegal applet stub/context access: applet destroyed.");
+ }
+
+ /**
+ * Disposes the applet's resources and disables the applet
+ * environment from further use; after calling this method the
+ * applet stub and context methods throw IllegalStateExceptions.
+ */
+ public void destroy() {
+ destroyed = true;
+
+ List clips = weakClips.hardList();
+ for (int i = 0; i < clips.size(); i++) {
+ ((AppletAudioClip)clips.get(i)).dispose();
+ }
+ }
+
+ /**
+ * Returns the frame that contains the applet. Disposing this
+ * frame will destroy the applet.
+ */
+ public Container getAppletFrame() {
+ // TODO: rename this method to getAppletContainer ?
+ return cont;
+ }
+
+ /**
+ * Initialize, start, and show the applet.
+ */
+ public void startApplet() {
+ checkDestroyed();
+
+ if (appletStarted)
+ return;
+
+ appletStarted = true;
+
+ try {
+ AppletDesc appletDesc = file.getApplet();
+
+ if (cont instanceof AppletStub)
+ applet.setStub((AppletStub)cont);
+ else
+ applet.setStub(this);
+
+ cont.setLayout(new BorderLayout());
+ cont.add("Center", applet);
+ cont.validate();
+
+ // This is only needed if the applet is in its own frame.
+ if (cont instanceof Frame) {
+ Frame frame = (Frame) cont;
+ frame.pack(); // cause insets to be calculated
+
+ Insets insets = frame.getInsets();
+ frame.setSize(appletDesc.getWidth() + insets.left + insets.right,
+ appletDesc.getHeight() + insets.top + insets.bottom);
+ }
+
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ // do first because some applets need to be displayed before
+ // starting (they use Component.getImage or something)
+ cont.setVisible(true);
+
+ applet.init();
+ applet.start();
+
+ cont.invalidate(); // this should force the applet to
+ cont.validate(); // the correct size and to repaint
+ cont.repaint();
+ }
+ });
+ } catch (InterruptedException ie) {
+
+ } catch (InvocationTargetException ite) {
+
+ }
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ // should also kill the applet?
+ }
+ }
+
+ // applet context methods
+
+ /**
+ * Returns the applet if the applet's name is specified,
+ * otherwise return null.
+ */
+ public Applet getApplet(String name) {
+ checkDestroyed();
+
+ if (name != null && name.equals(file.getApplet().getName()))
+ return applet;
+ else
+ return null;
+ }
+
+ /**
+ * Returns an enumeration that contains only the applet
+ * from the JNLP file.
+ */
+ public Enumeration getApplets() {
+ checkDestroyed();
+
+ return Collections.enumeration( Arrays.asList(new Applet[] { applet }) );
+ }
+
+ /**
+ * Returns an audio clip.
+ */
+ public AudioClip getAudioClip(URL location) {
+ checkDestroyed();
+
+ AppletAudioClip clip = new AppletAudioClip(location);
+
+ weakClips.add(clip);
+ weakClips.trimToSize();
+
+ return clip;
+ }
+
+ /**
+ * Return an image loaded from the specified location.
+ */
+ public Image getImage(URL location) {
+ checkDestroyed();
+
+ //return Toolkit.getDefaultToolkit().createImage(location);
+ Image image = (new ImageIcon(location)).getImage();
+
+ return image;
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ public void showDocument(java.net.URL uRL) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ public void showDocument(java.net.URL uRL, java.lang.String str) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Not implemented yet.
+ */
+ public void showStatus(java.lang.String str) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ public void setStream(String key, InputStream stream) {
+ checkDestroyed();
+
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ public InputStream getStream(String key) {
+ checkDestroyed();
+
+ return null;
+ }
+
+ /**
+ * Required for JRE1.4, but not implemented yet.
+ */
+ public Iterator getStreamKeys() {
+ checkDestroyed();
+
+ return null;
+ }
+
+ // stub methods
+
+ public void appletResize(int width, int height) {
+ checkDestroyed();
+
+ if (cont instanceof Frame) {
+ Frame frame = (Frame) cont;
+ Insets insets = frame.getInsets();
+
+ frame.setSize(width + insets.left + insets.right,
+ height + insets.top + insets.bottom);
+ }
+ }
+
+ public AppletContext getAppletContext() {
+ checkDestroyed();
+
+ return this;
+ }
+
+ public URL getCodeBase() {
+ checkDestroyed();
+
+ return file.getCodeBase();
+ }
+
+ public URL getDocumentBase() {
+ checkDestroyed();
+
+ return file.getApplet().getDocumentBase();
+ }
+
+ // FIXME: Sun's applet code forces all parameters to lower case.
+ // Does Netx's JNLP code do the same, so we can remove the first lookup?
+ public String getParameter(String name) {
+ checkDestroyed();
+
+ String s = (String) parameters.get(name);
+ if (s != null)
+ return s;
+
+ return (String) parameters.get(name.toLowerCase());
+ }
+
+ public boolean isActive() {
+ checkDestroyed();
+
+ // it won't be started or stopped, so if it can call it's running
+ return true;
+ }
+
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/AppletInstance.java b/netx/net/sourceforge/jnlp/runtime/AppletInstance.java
new file mode 100644
index 0000000..51bc801
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/AppletInstance.java
@@ -0,0 +1,138 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.applet.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.List;
+import java.security.*;
+import java.lang.reflect.*;
+import java.lang.ref.*;
+
+import net.sourceforge.jnlp.*;
+
+
+/**
+ * Represents a launched application instance created from a JNLP
+ * file. This class does not control the operation of the applet,
+ * use the AppletEnvironment class to start and stop the applet.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.9 $
+ */
+public class AppletInstance extends ApplicationInstance {
+
+ /** whether the applet's stop and destroy methods have been called */
+ private boolean appletStopped = false;
+
+ /** the applet */
+ private Applet applet;
+
+ /** the applet environment */
+ private AppletEnvironment environment;
+
+
+ /**
+ * Create a New Task based on the Specified URL
+ */
+ public AppletInstance(JNLPFile file, ThreadGroup group, ClassLoader loader, Applet applet) {
+ super(file, group, loader);
+
+ this.applet = applet;
+
+ this.environment = new AppletEnvironment(file, this);
+ }
+
+ /**
+ *
+ */
+ public AppletInstance(JNLPFile file, ThreadGroup group, ClassLoader loader, Applet applet, Container cont) {
+ super(file, group, loader);
+ this.applet = applet;
+ this.environment = new AppletEnvironment(file, this, cont);
+ }
+
+ /**
+ * Sets whether the applet is resizable or not. Applets default
+ * to being not resizable.
+ */
+ public void setResizable(boolean resizable) {
+ Container c = environment.getAppletFrame();
+ if (c instanceof Frame)
+ ((Frame) c).setResizable(resizable);
+ }
+
+ /**
+ * Returns whether the applet is resizable.
+ */
+ public boolean isResizable() {
+ Container c = environment.getAppletFrame();
+ if (c instanceof Frame)
+ return ((Frame) c).isResizable();
+
+ return false;
+ }
+
+ /**
+ * Returns the application title.
+ */
+ public String getTitle() {
+ return getJNLPFile().getApplet().getName();
+ }
+
+ /**
+ * Returns the applet environment.
+ */
+ public AppletEnvironment getAppletEnvironment() {
+ return environment;
+ }
+
+ /**
+ * Returns the applet.
+ */
+ public Applet getApplet() {
+ return applet;
+ }
+
+ /**
+ * Stop the application and destroy its resources.
+ */
+ public void destroy() {
+ if (appletStopped)
+ return;
+
+ appletStopped = true;
+
+ try {
+ applet.stop();
+ applet.destroy();
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+
+ environment.destroy();
+
+ super.destroy();
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
new file mode 100644
index 0000000..95ccb1e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
@@ -0,0 +1,319 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.awt.Window;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+
+import javax.swing.event.EventListenerList;
+
+import sun.awt.AppContext;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.PropertyDesc;
+import net.sourceforge.jnlp.SecurityDesc;
+import net.sourceforge.jnlp.ShortcutDesc;
+import net.sourceforge.jnlp.event.ApplicationEvent;
+import net.sourceforge.jnlp.event.ApplicationListener;
+import net.sourceforge.jnlp.security.SecurityWarningDialog.AccessType;
+import net.sourceforge.jnlp.services.ServiceUtil;
+import net.sourceforge.jnlp.util.WeakList;
+import net.sourceforge.jnlp.util.XDesktopEntry;
+
+/**
+ * Represents a running instance of an application described in a
+ * JNLPFile. This class provides a way to track the application's
+ * resources and destroy the application.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.15 $
+ */
+public class ApplicationInstance {
+
+ // todo: should attempt to unload the environment variables
+ // installed by the application.
+
+
+ /** the file */
+ private JNLPFile file;
+
+ /** the thread group */
+ private ThreadGroup group;
+
+ /** the classloader */
+ private ClassLoader loader;
+
+ /**
+ * Every application/applet gets its own AppContext. This allows us to do
+ * things like have two different look and feels for two different applets
+ * (running in the same VM), allows untrusted programs to manipulate the
+ * event queue (safely) and (possibly) more.<p>
+ *
+ * It is set to the AppContext which created this ApplicationInstance
+ */
+ private AppContext appContext;
+
+ /** whether the application has stopped running */
+ private boolean stopped = false;
+
+ /** weak list of windows opened by the application */
+ private WeakList weakWindows = new WeakList();
+
+ /** list of application listeners */
+ private EventListenerList listeners = new EventListenerList();
+
+ /** whether or not this application is signed */
+ private boolean isSigned = false;
+
+ /**
+ * Create an application instance for the file. This should be done in the
+ * appropriate {@link ThreadGroup} only.
+ */
+ public ApplicationInstance(JNLPFile file, ThreadGroup group, ClassLoader loader) {
+ this.file = file;
+ this.group = group;
+ this.loader = loader;
+ this.isSigned = ((JNLPClassLoader) loader).getSigning();
+ this.appContext = AppContext.getAppContext();
+ }
+
+ /**
+ * Add an Application listener
+ */
+ public void addApplicationListener(ApplicationListener listener) {
+ listeners.add(ApplicationListener.class, listener);
+ }
+
+ /**
+ * Remove an Application Listener
+ */
+ public void removeApplicationListener(ApplicationListener listener) {
+ listeners.remove(ApplicationListener.class, listener);
+ }
+
+ /**
+ * Notify listeners that the application has been terminated.
+ */
+ protected void fireDestroyed() {
+ Object list[] = listeners.getListenerList();
+ ApplicationEvent event = null;
+
+ for (int i=list.length-1; i>0; i-=2) { // last to first required
+ if (event == null)
+ event = new ApplicationEvent(this);
+
+ ((ApplicationListener)list[i]).applicationDestroyed(event);
+ }
+ }
+
+ /**
+ * Initialize the application's environment (installs
+ * environment variables, etc).
+ */
+ public void initialize() {
+ installEnvironment();
+
+ //Fixme: -Should check whether a desktop entry already exists for
+ // for this jnlp file, and do nothing if it exists.
+ // -If no href is specified in the jnlp tag, it should
+ // default to using the one passed in as an argument.
+ addMenuAndDesktopEntries();
+ }
+
+ /**
+ * Creates menu and desktop entries if required by the jnlp file
+ */
+
+ private void addMenuAndDesktopEntries() {
+ XDesktopEntry entry = new XDesktopEntry(file);
+ ShortcutDesc sd = file.getInformation().getShortcut();
+
+ if (sd != null && sd.onDesktop()) {
+ if (ServiceUtil.checkAccess(this, AccessType.CREATE_DESTKOP_SHORTCUT)) {
+ entry.createDesktopShortcut();
+ }
+ }
+
+ if (sd != null && sd.getMenu() != null) {
+ /*
+ * Sun's WebStart implementation doesnt seem to do anything under GNOME
+ */
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("ApplicationInstance.addMenuAndDesktopEntries():"
+ + " Adding menu entries NOT IMPLEMENTED");
+ }
+ }
+
+ }
+
+ /**
+ * Releases the application's resources before it is collected.
+ * Only collectable if classloader and thread group are
+ * also collectable so basically is almost never called (an
+ * application would have to close its windows and exit its
+ * threads but not call System.exit).
+ */
+ public void finalize() {
+ destroy();
+ }
+
+ /**
+ * Install the environment variables.
+ */
+ void installEnvironment() {
+ final PropertyDesc props[] = file.getResources().getProperties();
+
+ CodeSource cs = new CodeSource((URL) null, (java.security.cert.Certificate [])null);
+
+ JNLPClassLoader loader = (JNLPClassLoader) this.loader;
+ SecurityDesc s = loader.getSecurity();
+
+ ProtectionDomain pd = new ProtectionDomain(cs, s.getPermissions(), null, null);
+
+ // Add to hashmap
+ AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] {pd});
+
+ PrivilegedAction installProps = new PrivilegedAction() {
+ public Object run() {
+ for (int i=0; i < props.length; i++) {
+ System.setProperty(props[i].getKey(), props[i].getValue());
+ }
+
+ return null;
+ }
+ };
+ AccessController.doPrivileged(installProps, acc);
+ }
+
+ /**
+ * Returns the JNLP file for this task.
+ */
+ public JNLPFile getJNLPFile() {
+ return file;
+ }
+
+ /**
+ * Returns the application title.
+ */
+ public String getTitle() {
+ return file.getTitle();
+ }
+
+ /**
+ * Returns whether the application is running.
+ */
+ public boolean isRunning() {
+ return !stopped;
+ }
+
+ /**
+ * Stop the application and destroy its resources.
+ */
+ public void destroy() {
+ if (stopped)
+ return;
+
+ try {
+ // destroy resources
+ for (int i=0; i < weakWindows.size(); i++) {
+ Window w = (Window) weakWindows.get(i);
+ if (w != null)
+ w.dispose();
+ }
+
+ weakWindows.clear();
+
+ // interrupt threads
+ Thread threads[] = new Thread[ group.activeCount() * 2 ];
+ int nthreads = group.enumerate(threads);
+ for (int i=0; i < nthreads; i++) {
+ if (JNLPRuntime.isDebug())
+ System.out.println("Interrupt thread: "+threads[i]);
+
+ threads[i].interrupt();
+ }
+
+ // then stop
+ Thread.currentThread().yield();
+ nthreads = group.enumerate(threads);
+ for (int i=0; i < nthreads; i++) {
+ if (JNLPRuntime.isDebug())
+ System.out.println("Stop thread: "+threads[i]);
+
+ threads[i].stop();
+ }
+
+ // then destroy - except Thread.destroy() not implemented in jdk
+
+ }
+ finally {
+ stopped = true;
+ fireDestroyed();
+ }
+ }
+
+ /**
+ * Returns the thread group.
+ *
+ * @throws IllegalStateException if the app is not running
+ */
+ public ThreadGroup getThreadGroup() throws IllegalStateException {
+ if (stopped)
+ throw new IllegalStateException();
+
+ return group;
+ }
+
+ /**
+ * Returns the classloader.
+ *
+ * @throws IllegalStateException if the app is not running
+ */
+ public ClassLoader getClassLoader() throws IllegalStateException {
+ if (stopped)
+ throw new IllegalStateException();
+
+ return loader;
+ }
+
+ /**
+ * Adds a window that this application opened. When the
+ * application is disposed, these windows will also be disposed.
+ */
+ protected void addWindow(Window window) {
+ weakWindows.add(window);
+ weakWindows.trimToSize();
+ }
+
+ /**
+ * Returns whether or not this jar is signed.
+ */
+ public boolean isSigned() {
+ return isSigned;
+ }
+
+ public AppContext getAppContext() {
+ return appContext;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/Boot.java b/netx/net/sourceforge/jnlp/runtime/Boot.java
new file mode 100644
index 0000000..7442549
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/Boot.java
@@ -0,0 +1,463 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import net.sourceforge.jnlp.AppletDesc;
+import net.sourceforge.jnlp.ApplicationDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.Launcher;
+import net.sourceforge.jnlp.ParseException;
+import net.sourceforge.jnlp.PropertyDesc;
+import net.sourceforge.jnlp.ResourcesDesc;
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.security.VariableX509TrustManager;
+import net.sourceforge.jnlp.security.viewer.CertificateViewer;
+import net.sourceforge.jnlp.services.ServiceUtil;
+
+/**
+ * This is the main entry point for the JNLP client. The main
+ * method parses the command line parameters and loads a JNLP
+ * file into the secure runtime environment. This class is meant
+ * to be called from the command line or file association; to
+ * initialize the netx engine from other code invoke the
+ * <code>JNLPRuntime.initialize</code> method after configuring
+ * the runtime.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.21 $
+ */
+public final class Boot implements PrivilegedAction {
+
+ // todo: decide whether a spawned netx (external launch)
+ // should inherit the same options as this instance (store argv?)
+
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+ private static String R(String key, Object param) { return JNLPRuntime.getMessage(key, new Object[] {param}); }
+
+ private static final String version = "0.5";
+
+ /** the text to display before launching the about link */
+ private static final String aboutMessage = ""
+ + "netx v"+version+" - (C)2001-2003 Jon A. Maxwell ([email protected])\n"
+ + "\n"
+ + R("BLaunchAbout");
+
+ private static final String miniLicense = "\n"
+ + " netx - an open-source JNLP client.\n"
+ + " Copyright (C) 2001-2003 Jon A. Maxwell (JAM)\n"
+ + "\n"
+ + " // This library is free software; you can redistribute it and/or\n"
+ + " modify it under the terms of the GNU Lesser General Public\n"
+ + " License as published by the Free Software Foundation; either\n"
+ + " version 2.1 of the License, or (at your option) any later version.\n"
+ + "\n"
+ + " This library is distributed in the hope that it will be useful,\n"
+ + " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+ + " Lesser General Public License for more details.\n"
+ + "\n"
+ + " You should have received a copy of the GNU Lesser General Public\n"
+ + " License along with this library; if not, write to the Free Software\n"
+ + " Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"
+ + "\n";
+
+ private static final String helpMessage = "\n"
+ + "Usage: " + R("BOUsage")+"\n"
+ + " " + R("BOUsage2")+"\n"
+ + "\n"
+ + "control-options:"+"\n"
+ + " -about "+R("BOAbout")+"\n"
+ + " -viewer "+R("BOViewer")+"\n"
+ + "\n"
+ + "run-options:"+"\n"
+ + " -basedir dir "+R("BOBasedir")+"\n"
+ + " -arg arg "+R("BOArg")+"\n"
+ + " -param name=value "+R("BOParam")+"\n"
+ + " -property name=value "+R("BOProperty")+"\n"
+ + " -update seconds "+R("BOUpdate")+"\n"
+ + " -license "+R("BOLicense")+"\n"
+ + " -verbose "+R("BOVerbose")+"\n"
+ + " -nosecurity "+R("BONosecurity")+"\n"
+ + " -noupdate "+R("BONoupdate")+"\n"
+ + " -headless "+R("BOHeadless")+"\n"
+ + " -strict "+R("BOStrict")+"\n"
+ + " -umask=value "+R("BOUmask")+"\n"
+ + " -Xnofork "+R("BXnofork")+"\n"
+ + " -Xclearcache "+R("BXclearcache")+"\n"
+ + " -help "+R("BOHelp")+"\n";
+
+ private static final String doubleArgs = "-basedir -jnlp -arg -param -property -update";
+
+ private static String args[]; // avoid the hot potato
+
+
+ /**
+ * Launch the JNLP file specified by the command-line arguments.
+ */
+ public static void main(String[] argsIn) {
+ args = argsIn;
+
+ if (null != getOption("-viewer")) {
+
+ try {
+ CertificateViewer.main(null);
+ System.exit(0);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ if (null != getOption("-license")) {
+ System.out.println(miniLicense);
+ System.exit(0);
+ }
+
+ if (null != getOption("-help")) {
+ System.out.println(helpMessage);
+ System.exit(0);
+ }
+
+ if (null != getOption("-about"))
+ System.out.println(aboutMessage);
+
+ if (null != getOption("-verbose"))
+ JNLPRuntime.setDebug(true);
+
+ if (null != getOption("-update")) {
+ int value = Integer.parseInt(getOption("-update"));
+ JNLPRuntime.setDefaultUpdatePolicy(new UpdatePolicy(value*1000l));
+ }
+
+ if (null != getOption("-headless"))
+ JNLPRuntime.setHeadless(true);
+
+
+ if (null != getOption("-noupdate"))
+ JNLPRuntime.setDefaultUpdatePolicy(UpdatePolicy.NEVER);
+
+ if (null != getOption("-Xnofork")) {
+ JNLPRuntime.setForksAllowed(false);
+ }
+
+ // wire in custom authenticator
+ try {
+ SSLSocketFactory sslSocketFactory;
+ SSLContext context = SSLContext.getInstance("SSL");
+ TrustManager[] trust = new TrustManager[] { VariableX509TrustManager.getInstance() };
+ context.init(null, trust, null);
+ sslSocketFactory = context.getSocketFactory();
+
+ HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
+ } catch (Exception e) {
+ System.err.println("Unable to set SSLSocketfactory (may _prevent_ access to sites that should be trusted)! Continuing anyway...");
+ e.printStackTrace();
+ }
+
+ JNLPRuntime.setInitialArgments(Arrays.asList(argsIn));
+
+ // do in a privileged action to clear the security context of
+ // the Boot13 class, which doesn't have any privileges in
+ // JRE1.3; JRE1.4 works without Boot13 or this PrivilegedAction.
+ AccessController.doPrivileged(new Boot());
+
+ }
+
+ /**
+ * The privileged part (jdk1.3 compatibility).
+ */
+ public Object run() {
+ JNLPRuntime.setBaseDir(getBaseDir());
+ JNLPRuntime.setSecurityEnabled(null == getOption("-nosecurity"));
+ JNLPRuntime.initialize(true);
+
+ /*
+ * FIXME
+ * This should have been done with the rest of the argument parsing
+ * code. But we need to know what the cache and base directories are,
+ * and baseDir is initialized here
+ */
+ if (null != getOption("-Xclearcache")) {
+ CacheUtil.clearCache();
+ return null;
+ }
+
+ try {
+ new Launcher().launch(getFile());
+ }
+ catch (LaunchException ex) {
+ // default handler prints this
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+
+ fatalError(JNLPRuntime.getMessage("RUnexpected",
+ new Object[] {ex.toString(), ex.getStackTrace()[0]} ));
+ }
+
+ return null;
+ }
+
+ private static void fatalError(String message) {
+ System.err.println("netx: "+message);
+ System.exit(1);
+ }
+
+ /**
+ * Returns the about.jnlp file in {java.home}/lib or null if this file
+ * does not exist.
+ */
+ private static String getAboutFile() {
+
+ if (new File(JNLPRuntime.NETX_ABOUT_FILE).exists())
+ return JNLPRuntime.NETX_ABOUT_FILE;
+ else
+ return null;
+ }
+
+ /**
+ * Returns the file to open; does not return if no file was
+ * specified.
+ */
+ private static JNLPFile getFile() throws ParseException, MalformedURLException, IOException {
+
+ String location = getJNLPFile();
+
+ // override -jnlp with aboutFile
+ if (getOption("-about") != null) {
+ location = getAboutFile();
+ if (location == null)
+ fatalError("Unable to find about.jnlp in {java.home}/lib/");
+ } else {
+ location = getJNLPFile();
+ }
+
+ if (location == null) {
+ System.out.println(helpMessage);
+ System.exit(1);
+ }
+
+ if (JNLPRuntime.isDebug())
+ System.out.println(R("BFileLoc")+": "+location);
+
+ URL url = null;
+
+ try {
+ if (new File(location).exists())
+ url = new File(location).toURL(); // Why use file.getCanonicalFile?
+ else
+ url = new URL(ServiceUtil.getBasicService().getCodeBase(), location);
+ } catch (Exception e) {
+ fatalError("Invalid jnlp file " + location);
+ if (JNLPRuntime.isDebug())
+ e.printStackTrace();
+ }
+
+ boolean strict = (null != getOption("-strict"));
+
+ JNLPFile file = new JNLPFile(url, strict);
+
+ // Launches the jnlp file where this file originated.
+ if (file.getSourceLocation() != null) {
+ file = new JNLPFile(file.getSourceLocation(), strict);
+ }
+
+ // add in extra params from command line
+ addProperties(file);
+
+ if (file.isApplet())
+ addParameters(file);
+
+ if (file.isApplication())
+ addArguments(file);
+
+ if (JNLPRuntime.isDebug()) {
+ if (getOption("-arg") != null)
+ if (file.isInstaller() || file.isApplet())
+ System.out.println(R("BArgsNA"));
+
+ if (getOption("-param") != null)
+ if (file.isApplication())
+ System.out.println(R("BParamNA"));
+ }
+
+ return file;
+ }
+
+ /**
+ * Add the properties to the JNLP file.
+ */
+ private static void addProperties(JNLPFile file) {
+ String props[] = getOptions("-property");
+ ResourcesDesc resources = file.getResources();
+
+ for (int i=0; i < props.length; i++) {
+ // allows empty property, not sure about validity of that.
+ int equals = props[i].indexOf("=");
+ if (equals == -1)
+ fatalError(R("BBadProp", props[i]));
+
+ String key = props[i].substring(0, equals);
+ String value = props[i].substring(equals+1, props[i].length());
+
+ resources.addResource(new PropertyDesc(key, value));
+ }
+ }
+
+ /**
+ * Add the params to the JNLP file; only call if file is
+ * actually an applet file.
+ */
+ private static void addParameters(JNLPFile file) {
+ String params[] = getOptions("-param");
+ AppletDesc applet = file.getApplet();
+
+ for (int i=0; i < params.length; i++) {
+ // allows empty param, not sure about validity of that.
+ int equals = params[i].indexOf("=");
+ if (equals == -1)
+ fatalError(R("BBadParam", params[i]));
+
+ String name = params[i].substring(0, equals);
+ String value = params[i].substring(equals+1, params[i].length());
+
+ applet.addParameter(name, value);
+ }
+ }
+
+ /**
+ * Add the arguments to the JNLP file; only call if file is
+ * actually an application (not installer).
+ */
+ private static void addArguments(JNLPFile file) {
+ String args[] = getOptions("-arg"); // FYI args also global variable
+ ApplicationDesc app = file.getApplication();
+
+ for (int i=0; i < args.length; i++) {
+ app.addArgument(args[i]);
+ }
+ }
+
+ /**
+ * Gets the JNLP file from the command line arguments, or exits upon error.
+ */
+ private static String getJNLPFile() {
+
+ if (args.length == 0) {
+ System.out.println(helpMessage);
+ System.exit(0);
+ } else if (args.length == 1) {
+ return args[args.length - 1];
+ } else {
+ String lastArg = args[args.length - 1];
+ String secondLastArg = args[args.length - 2];
+
+ if (doubleArgs.indexOf(secondLastArg) == -1) {
+ return lastArg;
+ } else {
+ System.out.println(helpMessage);
+ System.exit(0);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return value of the first occurence of the specified
+ * option, or null if the option is not present. If the
+ * option is a flag (0-parameter) and is present then the
+ * option name is returned.
+ */
+ private static String getOption(String option) {
+ String result[] = getOptions(option);
+
+ if (result.length == 0)
+ return null;
+ else
+ return result[0];
+ }
+
+ /**
+ * Return all the values of the specified option, or an empty
+ * array if the option is not present. If the option is a
+ * flag (0-parameter) and is present then the option name is
+ * returned once for each occurrence.
+ */
+ private static String[] getOptions(String option) {
+ List result = new ArrayList();
+
+ for (int i=0; i < args.length; i++) {
+ if (option.equals(args[i])) {
+ if (-1 == doubleArgs.indexOf(option))
+ result.add(option);
+ else
+ if (i+1 < args.length)
+ result.add(args[i+1]);
+ }
+
+ if (args[i].startsWith("-") && -1 != doubleArgs.indexOf(args[i]))
+ i++;
+ }
+
+ return (String[]) result.toArray( new String[result.size()] );
+ }
+
+ /**
+ * Return the base dir. If the base dir parameter is not set
+ * the value is read from JNLPRuntime.NETX_ABOUT_FILE file.
+ * If that file does not exist, an install dialog is displayed
+ * to select the base directory.
+ */
+ private static File getBaseDir() {
+ if (getOption("-basedir") != null) {
+ File basedir = new File(getOption("-basedir"));
+
+ if (!basedir.exists() || !basedir.isDirectory())
+ fatalError(R("BNoDir", basedir));
+
+ return basedir;
+ }
+
+ // check .netxrc
+ File basedir = JNLPRuntime.getDefaultBaseDir();
+ if (basedir == null)
+ fatalError(R("BNoBase"));
+
+ return basedir;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/Boot13.java b/netx/net/sourceforge/jnlp/runtime/Boot13.java
new file mode 100644
index 0000000..4440c5f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/Boot13.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.lang.reflect.*;
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import javax.swing.*;
+
+/**
+ * Allows a Policy and SecurityManager to be set in JRE1.3 without
+ * running the code with only applet permissions; this class is
+ * for backward compatibility only and is totally unnecessary if
+ * running in jdk 1.4 or later (can call Boot directly).
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.5 $
+ */
+public class Boot13 extends URLClassLoader {
+
+ // The problem with setting a Policy in jdk1.3 is that the
+ // system and application classes seem to be loaded in such a
+ // way that only their protection domain determines the
+ // permissions; the policy object is never asked for permissions
+ // after the class is loaded. This hack creates a classloader
+ // that loads duplicate versions of the classes in such a
+ // manner where they ask with the policy object. The jdk1.4
+ // correctly honors the Policy object making this unneccessary
+ // post-1.3.
+
+ private Boot13(URL source[]) {
+ super(source);
+ }
+
+ protected PermissionCollection getPermissions(CodeSource source) {
+ Permissions result = new Permissions();
+ result.add( new AllPermission() );
+
+ return result;
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ Class c = findLoadedClass(name);
+ if (c != null)
+ return c;
+
+ // reverse the search order so that classes from this
+ // classloader, which sets the right permissions, are found
+ // before the parent classloader which has the same classes
+ // but the wrong permissions.
+ try {
+ return findClass(name);
+ }
+ catch (ClassNotFoundException ex) {
+ }
+
+ return getParent().loadClass(name);
+ }
+
+ public static void main(final String args[]) throws Exception {
+ URL cs = Boot13.class.getProtectionDomain().getCodeSource().getLocation();
+ // instead of using a custom loadClass search order, we could
+ // put the classes in a boot/ subdir of the JAR and load
+ // them from there. This would be an improvement by not
+ // allowing applications to get a duplicate jnlp engine (one
+ // with applet access permissions) by using the system
+ // classloader but a drawback by not allowing Boot to be
+ // called directly.
+ //cs = new URL("jar:"+cs+"!/boot/");
+
+ if (cs == null) {
+ System.err.println("fatal: cannot determine code source.");
+ System.exit(1);
+ }
+
+ Boot13 b = new Boot13(new URL[] {cs});
+
+ Thread.currentThread().setContextClassLoader(b); // try to prevent getting the non-policy version of classes
+
+ Class c = b.loadClass("net.sourceforge.jnlp.runtime.Boot");
+ Method main = c.getDeclaredMethod("main", new Class[] {String[].class} );
+
+ main.invoke(null, new Object[] { args } );
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
new file mode 100644
index 0000000..972eb25
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
@@ -0,0 +1,1198 @@
+
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeSet;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import net.sourceforge.jnlp.ExtensionDesc;
+import net.sourceforge.jnlp.JARDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.ParseException;
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.ResourcesDesc;
+import net.sourceforge.jnlp.SecurityDesc;
+import net.sourceforge.jnlp.Version;
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+import net.sourceforge.jnlp.tools.JarSigner;
+import sun.misc.JarIndex;
+
+/**
+ * Classloader that takes it's resources from a JNLP file. If the
+ * JNLP file defines extensions, separate classloaders for these
+ * will be created automatically. Classes are loaded with the
+ * security context when the classloader was created.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.20 $
+ */
+public class JNLPClassLoader extends URLClassLoader {
+
+ // todo: initializePermissions should get the permissions from
+ // extension classes too so that main file classes can load
+ // resources in an extension.
+
+ /** shortcut for resources */
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+
+ /** map from JNLPFile url to shared classloader */
+ private static Map urlToLoader = new HashMap(); // never garbage collected!
+
+ /** the directory for native code */
+ private File nativeDir = null; // if set, some native code exists
+
+ /** a list of directories that contain native libraries */
+ private List<File> nativeDirectories = Collections.synchronizedList(new LinkedList<File>());
+
+ /** security context */
+ private AccessControlContext acc = AccessController.getContext();
+
+ /** the permissions for the cached jar files */
+ private List resourcePermissions;
+
+ /** the app */
+ private ApplicationInstance app = null; // here for faster lookup in security manager
+
+ /** list of this, local and global loaders this loader uses */
+ private JNLPClassLoader loaders[] = null; // ..[0]==this
+
+ /** whether to strictly adhere to the spec or not */
+ private boolean strict = true;
+
+ /** loads the resources */
+ private ResourceTracker tracker = new ResourceTracker(true); // prefetch
+
+ /** the update policy for resources */
+ private UpdatePolicy updatePolicy;
+
+ /** the JNLP file */
+ private JNLPFile file;
+
+ /** the resources section */
+ private ResourcesDesc resources;
+
+ /** the security section */
+ private SecurityDesc security;
+
+ /** Permissions granted by the user during runtime. */
+ private ArrayList<Permission> runtimePermissions = new ArrayList<Permission>();
+
+ /** all jars not yet part of classloader or active */
+ private List available = new ArrayList();
+
+ /** all of the jar files that were verified */
+ private ArrayList<String> verifiedJars = null;
+
+ /** all of the jar files that were not verified */
+ private ArrayList<String> unverifiedJars = null;
+
+ /** the jarsigner tool to verify our jars */
+ private JarSigner js = null;
+
+ private boolean signing = false;
+
+ /** ArrayList containing jar indexes for various jars available to this classloader */
+ private ArrayList<JarIndex> jarIndexes = new ArrayList<JarIndex>();
+
+ /** File entries in the jar files available to this classloader */
+ private TreeSet jarEntries = new TreeSet();
+
+ /** Map of specific codesources to securitydesc */
+ private HashMap<URL, SecurityDesc> jarLocationSecurityMap = new HashMap<URL, SecurityDesc>();
+
+ /**
+ * Create a new JNLPClassLoader from the specified file.
+ *
+ * @param file the JNLP file
+ */
+ protected JNLPClassLoader(JNLPFile file, UpdatePolicy policy) throws LaunchException {
+ super(new URL[0], JNLPClassLoader.class.getClassLoader());
+
+ if (JNLPRuntime.isDebug())
+ System.out.println("New classloader: "+file.getFileLocation());
+
+ this.file = file;
+ this.updatePolicy = policy;
+ this.resources = file.getResources();
+
+ // initialize extensions
+ initializeExtensions();
+
+ // initialize permissions
+ initializePermissions();
+
+ initializeResources();
+
+ setSecurity();
+
+ }
+
+ private void setSecurity() throws LaunchException {
+
+ URL codebase = null;
+
+ if (file.getCodeBase() != null) {
+ codebase = file.getCodeBase();
+ } else {
+ //Fixme: codebase should be the codebase of the Main Jar not
+ //the location. Although, it still works in the current state.
+ codebase = file.getResources().getMainJAR().getLocation();
+ }
+
+ /**
+ * When we're trying to load an applet, file.getSecurity() will return
+ * null since there is no jnlp file to specify permissions. We
+ * determine security settings here, after trying to verify jars.
+ */
+ if (file instanceof PluginBridge) {
+ if (signing == true) {
+ this.security = new SecurityDesc(file,
+ SecurityDesc.ALL_PERMISSIONS,
+ codebase.getHost());
+ } else {
+ this.security = new SecurityDesc(file,
+ SecurityDesc.SANDBOX_PERMISSIONS,
+ codebase.getHost());
+ }
+ } else { //regular jnlp file
+
+ /*
+ * Various combinations of the jars being signed and <security> tags being
+ * present are possible. They are treated as follows
+ *
+ * Jars JNLP File Result
+ *
+ * Signed <security> Appropriate Permissions
+ * Signed no <security> Sandbox
+ * Unsigned <security> Error
+ * Unsigned no <security> Sandbox
+ *
+ */
+ if (!file.getSecurity().getSecurityType().equals(SecurityDesc.SANDBOX_PERMISSIONS) && !signing) {
+ throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LUnsignedJarWithSecurity"), R("LUnsignedJarWithSecurityInfo"));
+ }
+ else if (signing == true) {
+ this.security = file.getSecurity();
+ } else {
+ this.security = new SecurityDesc(file,
+ SecurityDesc.SANDBOX_PERMISSIONS,
+ codebase.getHost());
+ }
+ }
+ }
+
+ /**
+ * Returns a JNLP classloader for the specified JNLP file.
+ *
+ * @param file the file to load classes for
+ * @param policy the update policy to use when downloading resources
+ */
+ public static JNLPClassLoader getInstance(JNLPFile file, UpdatePolicy policy) throws LaunchException {
+ JNLPClassLoader baseLoader = null;
+ JNLPClassLoader loader = null;
+ String uniqueKey = file.getUniqueKey();
+
+ if (uniqueKey != null)
+ baseLoader = (JNLPClassLoader) urlToLoader.get(uniqueKey);
+
+ try {
+
+ // If base loader is null, or the baseloader's file and this
+ // file is different, initialize a new loader
+ if (baseLoader == null ||
+ !baseLoader.getJNLPFile().getFileLocation().equals(file.getFileLocation())) {
+
+ loader = new JNLPClassLoader(file, policy);
+
+ // New loader init may have caused extentions to create a
+ // loader for this unique key. Check.
+ JNLPClassLoader extLoader = (JNLPClassLoader) urlToLoader.get(uniqueKey);
+
+ if (extLoader != null && extLoader != loader) {
+ if (loader.signing && !extLoader.signing)
+ if (!SecurityWarningDialog.showNotAllSignedWarningDialog(file))
+ throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo"));
+
+ loader.merge(extLoader);
+ }
+
+ // loader is now current + ext. But we also need to think of
+ // the baseLoader
+ if (baseLoader != null && baseLoader != loader) {
+ loader.merge(baseLoader);
+ }
+
+ } else {
+ // if key is same and locations match, this is the loader we want
+ loader = baseLoader;
+ }
+
+ } catch (LaunchException e) {
+ throw e;
+ }
+
+ // loaders are mapped to a unique key. Only extensions and parent
+ // share a key, so it is safe to always share based on it
+ urlToLoader.put(uniqueKey, loader);
+
+ return loader;
+ }
+
+ /**
+ * Returns a JNLP classloader for the JNLP file at the specified
+ * location.
+ *
+ * @param location the file's location
+ * @param version the file's version
+ * @param policy the update policy to use when downloading resources
+ */
+ public static JNLPClassLoader getInstance(URL location, String uniqueKey, Version version, UpdatePolicy policy)
+ throws IOException, ParseException, LaunchException {
+ JNLPClassLoader loader = (JNLPClassLoader) urlToLoader.get(uniqueKey);
+
+ if (loader == null || !location.equals(loader.getJNLPFile().getFileLocation()))
+ loader = getInstance(new JNLPFile(location, uniqueKey, version, false, policy), policy);
+
+ return loader;
+ }
+
+ /**
+ * Load the extensions specified in the JNLP file.
+ */
+ void initializeExtensions() {
+ ExtensionDesc[] ext = resources.getExtensions();
+
+ List loaderList = new ArrayList();
+
+ loaderList.add(this);
+
+ //if (ext != null) {
+ for (int i=0; i < ext.length; i++) {
+ try {
+ String uniqueKey = this.getJNLPFile().getUniqueKey();
+ JNLPClassLoader loader = getInstance(ext[i].getLocation(), uniqueKey, ext[i].getVersion(), updatePolicy);
+ loaderList.add(loader);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ //}
+
+ loaders = (JNLPClassLoader[]) loaderList.toArray(new JNLPClassLoader[ loaderList.size()]);
+ }
+
+ /**
+ * Make permission objects for the classpath.
+ */
+ void initializePermissions() {
+ resourcePermissions = new ArrayList();
+
+ JARDesc jars[] = resources.getJARs();
+ for (int i=0; i < jars.length; i++) {
+ Permission p = CacheUtil.getReadPermission(jars[i].getLocation(),
+ jars[i].getVersion());
+
+ if (JNLPRuntime.isDebug()) {
+ if (p == null)
+ System.out.println("Unable to add permission for " + jars[i].getLocation());
+ else
+ System.out.println("Permission added: " + p.toString());
+ }
+ if (p != null)
+ resourcePermissions.add(p);
+ }
+ }
+
+ /**
+ * Load all of the JARs used in this JNLP file into the
+ * ResourceTracker for downloading.
+ */
+ void initializeResources() throws LaunchException {
+ JARDesc jars[] = resources.getJARs();
+ if (jars == null || jars.length == 0)
+ return;
+ /*
+ if (jars == null || jars.length == 0) {
+ throw new LaunchException(null, null, R("LSFatal"),
+ R("LCInit"), R("LFatalVerification"), "No jars!");
+ }
+ */
+ List initialJars = new ArrayList();
+
+ for (int i=0; i < jars.length; i++) {
+
+ available.add(jars[i]);
+
+ if (jars[i].isEager())
+ initialJars.add(jars[i]); // regardless of part
+
+ tracker.addResource(jars[i].getLocation(),
+ jars[i].getVersion(),
+ jars[i].isCacheable() ? JNLPRuntime.getDefaultUpdatePolicy() : UpdatePolicy.FORCE
+ );
+ }
+
+ if (strict)
+ fillInPartJars(initialJars); // add in each initial part's lazy jars
+
+ if (JNLPRuntime.isVerifying()) {
+
+ JarSigner js;
+ waitForJars(initialJars); //download the jars first.
+
+ try {
+ js = verifyJars(initialJars);
+ } catch (Exception e) {
+ //we caught an Exception from the JarSigner class.
+ //Note: one of these exceptions could be from not being able
+ //to read the cacerts or trusted.certs files.
+ e.printStackTrace();
+ throw new LaunchException(null, null, R("LSFatal"),
+ R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo"));
+ }
+
+ //Case when at least one jar has some signing
+ if (js.anyJarsSigned()){
+ signing = true;
+
+ if (!js.allJarsSigned() &&
+ !SecurityWarningDialog.showNotAllSignedWarningDialog(file))
+ throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo"));
+
+
+ //user does not trust this publisher
+ if (!js.getAlreadyTrustPublisher()) {
+ checkTrustWithUser(js);
+ } else {
+ /**
+ * If the user trusts this publisher (i.e. the publisher's certificate
+ * is in the user's trusted.certs file), we do not show any dialogs.
+ */
+ }
+ } else {
+
+ signing = false;
+ //otherwise this jar is simply unsigned -- make sure to ask
+ //for permission on certain actions
+ }
+ }
+
+ for (JARDesc jarDesc: file.getResources().getJARs()) {
+ try {
+ URL location = tracker.getCacheFile(jarDesc.getLocation()).toURI().toURL();
+ SecurityDesc jarSecurity = file.getSecurity();
+
+ if (file instanceof PluginBridge) {
+
+ URL codebase = null;
+
+ if (file.getCodeBase() != null) {
+ codebase = file.getCodeBase();
+ } else {
+ //Fixme: codebase should be the codebase of the Main Jar not
+ //the location. Although, it still works in the current state.
+ codebase = file.getResources().getMainJAR().getLocation();
+ }
+
+ jarSecurity = new SecurityDesc(file,
+ SecurityDesc.ALL_PERMISSIONS,
+ codebase.getHost());
+ }
+
+ jarLocationSecurityMap.put(location, jarSecurity);
+ } catch (MalformedURLException mfe) {
+ System.err.println(mfe.getMessage());
+ }
+ }
+
+ activateJars(initialJars);
+ }
+
+ private void checkTrustWithUser(JarSigner js) throws LaunchException {
+ if (!js.getRootInCacerts()) { //root cert is not in cacerts
+ boolean b = SecurityWarningDialog.showCertWarningDialog(
+ SecurityWarningDialog.AccessType.UNVERIFIED, file, js);
+ if (!b)
+ throw new LaunchException(null, null, R("LSFatal"),
+ R("LCLaunching"), R("LNotVerified"), "");
+ } else if (js.getRootInCacerts()) { //root cert is in cacerts
+ boolean b = false;
+ if (js.noSigningIssues())
+ b = SecurityWarningDialog.showCertWarningDialog(
+ SecurityWarningDialog.AccessType.VERIFIED, file, js);
+ else if (!js.noSigningIssues())
+ b = SecurityWarningDialog.showCertWarningDialog(
+ SecurityWarningDialog.AccessType.SIGNING_ERROR, file, js);
+ if (!b)
+ throw new LaunchException(null, null, R("LSFatal"),
+ R("LCLaunching"), R("LCancelOnUserRequest"), "");
+ }
+ }
+
+ /**
+ * Add applet's codebase URL. This allows compatibility with
+ * applets that load resources from their codebase instead of
+ * through JARs, but can slow down resource loading. Resources
+ * loaded from the codebase are not cached.
+ */
+ public void enableCodeBase() {
+ addURL( file.getCodeBase() ); // nothing happens if called more that once?
+ }
+
+ /**
+ * Sets the JNLP app this group is for; can only be called once.
+ */
+ public void setApplication(ApplicationInstance app) {
+ if (this.app != null) {
+ if (JNLPRuntime.isDebug()) {
+ Exception ex = new IllegalStateException("Application can only be set once");
+ ex.printStackTrace();
+ }
+ return;
+ }
+
+ this.app = app;
+ }
+
+ /**
+ * Returns the JNLP app for this classloader
+ */
+ public ApplicationInstance getApplication() {
+ return app;
+ }
+
+ /**
+ * Returns the JNLP file the classloader was created from.
+ */
+ public JNLPFile getJNLPFile() {
+ return file;
+ }
+
+ /**
+ * Returns the permissions for the CodeSource.
+ */
+ protected PermissionCollection getPermissions(CodeSource cs) {
+ Permissions result = new Permissions();
+
+ // should check for extensions or boot, automatically give all
+ // access w/o security dialog once we actually check certificates.
+
+ // copy security permissions from SecurityDesc element
+ if (security != null) {
+ // Security desc. is used only to track security settings for the
+ // application. However, an application may comprise of multiple
+ // jars, and as such, security must be evaluated on a per jar basis.
+
+ // set default perms
+ PermissionCollection permissions = security.getSandBoxPermissions();
+
+ // If more than default is needed:
+ // 1. Code must be signed
+ // 2. ALL or J2EE permissions must be requested (note: plugin requests ALL automatically)
+ if (cs.getCodeSigners() != null &&
+ (getCodeSourceSecurity(cs.getLocation()).getSecurityType().equals(SecurityDesc.ALL_PERMISSIONS) ||
+ getCodeSourceSecurity(cs.getLocation()).getSecurityType().equals(SecurityDesc.J2EE_PERMISSIONS))
+ ) {
+
+ permissions = getCodeSourceSecurity(cs.getLocation()).getPermissions();
+ }
+
+ Enumeration<Permission> e = permissions.elements();
+ while (e.hasMoreElements())
+ result.add(e.nextElement());
+ }
+
+ // add in permission to read the cached JAR files
+ for (int i=0; i < resourcePermissions.size(); i++)
+ result.add((Permission) resourcePermissions.get(i));
+
+ // add in the permissions that the user granted.
+ for (int i=0; i < runtimePermissions.size(); i++)
+ result.add(runtimePermissions.get(i));
+
+ return result;
+ }
+
+ protected void addPermission(Permission p) {
+ runtimePermissions.add(p);
+ }
+
+ /**
+ * Adds to the specified list of JARS any other JARs that need
+ * to be loaded at the same time as the JARs specified (ie, are
+ * in the same part).
+ */
+ protected void fillInPartJars(List jars) {
+ for (int i=0; i < jars.size(); i++) {
+ String part = ((JARDesc) jars.get(i)).getPart();
+
+ for (int a=0; a < available.size(); a++) {
+ JARDesc jar = (JARDesc) available.get(a);
+
+ if (part != null && part.equals(jar.getPart()))
+ if (!jars.contains(jar))
+ jars.add(jar);
+ }
+ }
+ }
+
+ /**
+ * Ensures that the list of jars have all been transferred, and
+ * makes them available to the classloader. If a jar contains
+ * native code, the libraries will be extracted and placed in
+ * the path.
+ *
+ * @param jars the list of jars to load
+ */
+ protected void activateJars(final List jars) {
+ PrivilegedAction activate = new PrivilegedAction() {
+
+ public Object run() {
+ // transfer the Jars
+ waitForJars(jars);
+
+ for (int i=0; i < jars.size(); i++) {
+ JARDesc jar = (JARDesc) jars.get(i);
+
+ available.remove(jar);
+
+ // add jar
+ File localFile = tracker.getCacheFile(jar.getLocation());
+ try {
+ URL location = jar.getLocation(); // non-cacheable, use source location
+ if (localFile != null) {
+ location = localFile.toURL(); // cached file
+
+ // This is really not the best way.. but we need some way for
+ // PluginAppletViewer::getCachedImageRef() to check if the image
+ // is available locally, and it cannot use getResources() because
+ // that prefetches the resource, which confuses MediaTracker.waitForAll()
+ // which does a wait(), waiting for notification (presumably
+ // thrown after a resource is fetched). This bug manifests itself
+ // particularly when using The FileManager applet from Webmin.
+
+ JarFile jarFile = new JarFile(localFile);
+ Enumeration e = jarFile.entries();
+ while (e.hasMoreElements()) {
+
+ JarEntry je = (JarEntry) e.nextElement();
+
+ // another jar in my jar? it is more likely than you think
+ if (je.getName().endsWith(".jar")) {
+ // We need to extract that jar so that it can be loaded
+ // (inline loading with "jar:..!/..." path will not work
+ // with standard classloader methods)
+
+ String extractedJarLocation = localFile.getParent() + "/" + je.getName();
+ File parentDir = new File(extractedJarLocation).getParentFile();
+ if (!parentDir.isDirectory() && !parentDir.mkdirs()) {
+ throw new RuntimeException(R("RNestedJarExtration"));
+ }
+ FileOutputStream extractedJar = new FileOutputStream(extractedJarLocation);
+ InputStream is = jarFile.getInputStream(je);
+
+ byte[] bytes = new byte[1024];
+ int read = is.read(bytes);
+ int fileSize = read;
+ while (read > 0) {
+ extractedJar.write(bytes, 0, read);
+ read = is.read(bytes);
+ fileSize += read;
+ }
+
+ is.close();
+ extractedJar.close();
+
+ // 0 byte file? skip
+ if (fileSize <= 0) {
+ continue;
+ }
+
+ JarSigner signer = new JarSigner();
+ signer.verifyJar(extractedJarLocation);
+
+ if (signer.anyJarsSigned() && !signer.getAlreadyTrustPublisher()) {
+ checkTrustWithUser(signer);
+ }
+
+ try {
+ URL fileURL = new URL("file://" + extractedJarLocation);
+ addURL(fileURL);
+
+ SecurityDesc jarSecurity = file.getSecurity();
+
+ if (file instanceof PluginBridge) {
+
+ URL codebase = null;
+
+ if (file.getCodeBase() != null) {
+ codebase = file.getCodeBase();
+ } else {
+ //Fixme: codebase should be the codebase of the Main Jar not
+ //the location. Although, it still works in the current state.
+ codebase = file.getResources().getMainJAR().getLocation();
+ }
+
+ jarSecurity = new SecurityDesc(file,
+ SecurityDesc.ALL_PERMISSIONS,
+ codebase.getHost());
+ }
+
+ jarLocationSecurityMap.put(fileURL, jarSecurity);
+
+ } catch (MalformedURLException mfue) {
+ if (JNLPRuntime.isDebug())
+ System.err.println("Unable to add extracted nested jar to classpath");
+
+ mfue.printStackTrace();
+ }
+ }
+
+ jarEntries.add(je.getName());
+ }
+
+ }
+
+ addURL(location);
+
+ // there is currently no mechanism to cache files per
+ // instance.. so only index cached files
+ if (localFile != null) {
+ JarIndex index = JarIndex.getJarIndex(new JarFile(localFile.getAbsolutePath()), null);
+
+ if (index != null)
+ jarIndexes.add(index);
+ }
+
+ if (JNLPRuntime.isDebug())
+ System.err.println("Activate jar: "+location);
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+
+ // some programs place a native library in any jar
+ activateNative(jar);
+ }
+
+ return null;
+ }
+ };
+
+ AccessController.doPrivileged(activate, acc);
+ }
+
+ /**
+ * Search for and enable any native code contained in a JAR by copying the
+ * native files into the filesystem. Called in the security context of the
+ * classloader.
+ */
+ protected void activateNative(JARDesc jar) {
+ if (JNLPRuntime.isDebug())
+ System.out.println("Activate native: "+jar.getLocation());
+
+ File localFile = tracker.getCacheFile(jar.getLocation());
+ if (localFile == null)
+ return;
+
+ if (nativeDir == null)
+ nativeDir = getNativeDir();
+
+ String[] librarySuffixes = { ".so", ".dylib", ".jnilib", ".framework", ".dll" };
+
+ try {
+ JarFile jarFile = new JarFile(localFile, false);
+ Enumeration<JarEntry> entries = jarFile.entries();
+
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+
+ if (e.isDirectory()) {
+ continue;
+ }
+
+ String name = new File(e.getName()).getName();
+ boolean isLibrary = false;
+
+ for (String suffix: librarySuffixes) {
+ if (name.endsWith(suffix)) {
+ isLibrary = true;
+ break;
+ }
+ }
+ if (!isLibrary) {
+ continue;
+ }
+
+ File outFile = new File(nativeDir, name);
+
+ CacheUtil.streamCopy(jarFile.getInputStream(e),
+ new FileOutputStream(outFile));
+ }
+ }
+ catch (IOException ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Return the base directory to store native code files in.
+ * This method does not need to return the same directory across
+ * calls.
+ */
+ protected File getNativeDir() {
+ nativeDir = new File(System.getProperty("java.io.tmpdir")
+ + File.separator + "netx-native-"
+ + (new Random().nextInt() & 0xFFFF));
+
+ if (!nativeDir.mkdirs())
+ return null;
+ else {
+ // add this new native directory to the search path
+ addNativeDirectory(nativeDir);
+ return nativeDir;
+ }
+ }
+
+ /**
+ * Adds the {@link File} to the search path of this {@link JNLPClassLoader}
+ * when trying to find a native library
+ */
+ protected void addNativeDirectory(File nativeDirectory) {
+ nativeDirectories.add(nativeDirectory);
+ }
+
+ /**
+ * Returns a list of all directories in the search path of the current classloader
+ * when it tires to find a native library.
+ * @return a list of directories in the search path for native libraries
+ */
+ protected List<File> getNativeDirectories() {
+ return nativeDirectories;
+ }
+
+ /**
+ * Return the absolute path to the native library.
+ */
+ protected String findLibrary(String lib) {
+ String syslib = System.mapLibraryName(lib);
+
+ for (File dir: getNativeDirectories()) {
+ File target = new File(dir, syslib);
+ if (target.exists())
+ return target.toString();
+ }
+
+ String result = super.findLibrary(lib);
+ if (result != null)
+ return result;
+
+ return findLibraryExt(lib);
+ }
+
+ /**
+ * Try to find the library path from another peer classloader.
+ */
+ protected String findLibraryExt(String lib) {
+ for (int i=0; i < loaders.length; i++) {
+ String result = null;
+
+ if (loaders[i] != this)
+ result = loaders[i].findLibrary(lib);
+
+ if (result != null)
+ return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * Wait for a group of JARs, and send download events if there
+ * is a download listener or display a progress window otherwise.
+ *
+ * @param jars the jars
+ */
+ private void waitForJars(List jars) {
+ URL urls[] = new URL[jars.size()];
+
+ for (int i=0; i < jars.size(); i++) {
+ JARDesc jar = (JARDesc) jars.get(i);
+
+ urls[i] = jar.getLocation();
+ }
+
+ CacheUtil.waitForResources(app, tracker, urls, file.getTitle());
+ }
+
+ /**
+ * Verifies code signing of jars to be used.
+ *
+ * @param jars the jars to be verified.
+ */
+ private JarSigner verifyJars(List<JARDesc> jars) throws Exception {
+
+ js = new JarSigner();
+ js.verifyJars(jars, tracker);
+ return js;
+ }
+
+ /**
+ * Find the loaded class in this loader or any of its extension loaders.
+ */
+ protected Class findLoadedClassAll(String name) {
+ for (int i=0; i < loaders.length; i++) {
+ Class result = null;
+
+ if (loaders[i] == this)
+ result = super.findLoadedClass(name);
+ else
+ result = loaders[i].findLoadedClassAll(name);
+
+ if (result != null)
+ return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * Find a JAR in the shared 'extension' classloaders, this
+ * classloader, or one of the classloaders for the JNLP file's
+ * extensions.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+
+ Class result = findLoadedClassAll(name);
+
+ // try parent classloader
+ if (result == null) {
+ try {
+ ClassLoader parent = getParent();
+ if (parent == null)
+ parent = ClassLoader.getSystemClassLoader();
+
+ return parent.loadClass(name);
+ }
+ catch (ClassNotFoundException ex) { }
+ }
+
+ // filter out 'bad' package names like java, javax
+ // validPackage(name);
+
+ // search this and the extension loaders
+ if (result == null)
+ try {
+ result = loadClassExt(name);
+ } catch (ClassNotFoundException cnfe) {
+
+ // Not found in external loader either. As a last resort, look in any available indexes
+
+ // Currently this loads jars directly from the site. We cannot cache it because this
+ // call is initiated from within the applet, which does not have disk read/write permissions
+ for (JarIndex index: jarIndexes) {
+ LinkedList<String> jarList = index.get(name.replace('.', '/'));
+
+ if (jarList != null) {
+ for (String jarName: jarList) {
+ JARDesc desc;
+ try {
+ desc = new JARDesc(new URL(file.getCodeBase(), jarName),
+ null, null, false, true, false, true);
+ } catch (MalformedURLException mfe) {
+ throw new ClassNotFoundException(name);
+ }
+
+ available.add(desc);
+
+ tracker.addResource(desc.getLocation(),
+ desc.getVersion(),
+ JNLPRuntime.getDefaultUpdatePolicy()
+ );
+
+ URL remoteURL;
+ try {
+ remoteURL = new URL(file.getCodeBase() + jarName);
+ } catch (MalformedURLException mfe) {
+ throw new ClassNotFoundException(name);
+ }
+
+ URL u;
+
+ try {
+ u = tracker.getCacheURL(remoteURL);
+ } catch (Exception e) {
+ throw new ClassNotFoundException(name);
+ }
+
+ if (u != null)
+ addURL(u);
+
+ }
+
+ // If it still fails, let it error out
+ result = loadClassExt(name);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Find the class in this loader or any of its extension loaders.
+ */
+ protected Class findClass(String name) throws ClassNotFoundException {
+ for (int i=0; i < loaders.length; i++) {
+ try {
+ if (loaders[i] == this)
+ return super.findClass(name);
+ else
+ return loaders[i].findClass(name);
+ }
+ catch(ClassNotFoundException ex) { }
+ catch(ClassFormatError cfe) {}
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ /**
+ * Search for the class by incrementally adding resources to the
+ * classloader and its extension classloaders until the resource
+ * is found.
+ */
+ private Class loadClassExt(String name) throws ClassNotFoundException {
+ // make recursive
+ addAvailable();
+
+ // find it
+ try {
+ return findClass(name);
+ }
+ catch(ClassNotFoundException ex) {
+ }
+
+ // add resources until found
+ while (true) {
+ JNLPClassLoader addedTo = addNextResource();
+
+ if (addedTo == null)
+ throw new ClassNotFoundException(name);
+
+ try {
+ return addedTo.findClass(name);
+ }
+ catch(ClassNotFoundException ex) {
+ }
+ }
+ }
+
+ /**
+ * Finds the resource in this, the parent, or the extension
+ * class loaders.
+ */
+ public URL getResource(String name) {
+ URL result = super.getResource(name);
+
+ for (int i=1; i < loaders.length; i++)
+ if (result == null)
+ result = loaders[i].getResource(name);
+
+ return result;
+ }
+
+ /**
+ * Finds the resource in this, the parent, or the extension
+ * class loaders.
+ */
+ public Enumeration findResources(String name) throws IOException {
+ Vector resources = new Vector();
+
+ for (int i=0; i < loaders.length; i++) {
+ Enumeration e;
+
+ if (loaders[i] == this)
+ e = super.findResources(name);
+ else
+ e = loaders[i].findResources(name);
+
+ while (e.hasMoreElements())
+ resources.add(e.nextElement());
+ }
+
+ return resources.elements();
+ }
+
+ /**
+ * Returns if the specified resource is available locally from a cached jar
+ *
+ * @param s The name of the resource
+ * @return Whether or not the resource is available locally
+ */
+ public boolean resourceAvailableLocally(String s) {
+ return jarEntries.contains(s);
+ }
+
+ /**
+ * Adds whatever resources have already been downloaded in the
+ * background.
+ */
+ protected void addAvailable() {
+ // go through available, check tracker for it and all of its
+ // part brothers being available immediately, add them.
+
+ for (int i=1; i < loaders.length; i++) {
+ loaders[i].addAvailable();
+ }
+ }
+
+ /**
+ * Adds the next unused resource to the classloader. That
+ * resource and all those in the same part will be downloaded
+ * and added to the classloader before returning. If there are
+ * no more resources to add, the method returns immediately.
+ *
+ * @return the classloader that resources were added to, or null
+ */
+ protected JNLPClassLoader addNextResource() {
+ if (available.size() == 0) {
+ for (int i=1; i < loaders.length; i++) {
+ JNLPClassLoader result = loaders[i].addNextResource();
+
+ if (result != null)
+ return result;
+ }
+ return null;
+ }
+
+ // add jar
+ List jars = new ArrayList();
+ jars.add(available.get(0));
+
+ fillInPartJars(jars);
+
+
+ activateJars(jars);
+
+ return this;
+ }
+
+ // this part compatibility with previous classloader
+ /**
+ * @deprecated
+ */
+ public String getExtensionName() {
+ String result = file.getInformation().getTitle();
+
+ if (result == null)
+ result = file.getInformation().getDescription();
+ if (result == null && file.getFileLocation() != null)
+ result = file.getFileLocation().toString();
+ if (result == null && file.getCodeBase() != null)
+ result = file.getCodeBase().toString();
+
+ return result;
+ }
+
+ /**
+ * @deprecated
+ */
+ public String getExtensionHREF() {
+ return file.getFileLocation().toString();
+ }
+
+ public boolean getSigning() {
+ return signing;
+ }
+
+ protected SecurityDesc getSecurity() {
+ return security;
+ }
+
+ /**
+ * Returns the security descriptor for given code source URL
+ *
+ * @param source The code source
+ * @return The SecurityDescriptor for that source
+ */
+
+ protected SecurityDesc getCodeSourceSecurity(URL source) {
+ return jarLocationSecurityMap.get(source);
+ }
+
+ /**
+ * Merges the code source/security descriptor mapping from another loader
+ *
+ * @param extLoader The loader form which to merge
+ * @throws SecurityException if the code is called from an untrusted source
+ */
+ private void merge(JNLPClassLoader extLoader) {
+
+ try {
+ System.getSecurityManager().checkPermission(new AllPermission());
+ } catch (SecurityException se) {
+ throw new SecurityException("JNLPClassLoader() may only be called from trusted sources!");
+ }
+
+ // jars
+ for (URL u : extLoader.getURLs())
+ addURL(u);
+
+ // native search paths
+ for (File nativeDirectory: extLoader.getNativeDirectories())
+ addNativeDirectory(nativeDirectory);
+
+ // security descriptors
+ for (URL key: extLoader.jarLocationSecurityMap.keySet()) {
+ jarLocationSecurityMap.put(key, extLoader.jarLocationSecurityMap.get(key));
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPPolicy.java b/netx/net/sourceforge/jnlp/runtime/JNLPPolicy.java
new file mode 100644
index 0000000..d839fbd
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPPolicy.java
@@ -0,0 +1,103 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.security.*;
+import java.util.Enumeration;
+
+/**
+ * Policy for JNLP environment. This class delegates to the
+ * system policy but always grants permissions to the JNLP code
+ * and system CodeSources (no separate policy file needed). This
+ * class may also grant permissions to applications at runtime if
+ * approved by the user.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+public class JNLPPolicy extends Policy {
+
+ /** classes from this source have all permissions */
+ private static CodeSource shellSource;
+
+ /** classes from this source have all permissions */
+ private static CodeSource systemSource;
+
+ /** the previous policy */
+ private static Policy systemPolicy;
+
+
+ protected JNLPPolicy() {
+ shellSource = JNLPPolicy.class.getProtectionDomain().getCodeSource();
+ systemSource = Policy.class.getProtectionDomain().getCodeSource();
+ systemPolicy = Policy.getPolicy();
+ }
+
+ /**
+ * Return a mutable, heterogeneous-capable permission collection
+ * for the source.
+ */
+ public PermissionCollection getPermissions(CodeSource source) {
+ if (source.equals(systemSource) || source.equals(shellSource))
+ return getAllPermissions();
+
+ // if we check the SecurityDesc here then keep in mind that
+ // code can add properties at runtime to the ResourcesDesc!
+ if (JNLPRuntime.getApplication() != null) {
+ if (JNLPRuntime.getApplication().getClassLoader() instanceof JNLPClassLoader) {
+ JNLPClassLoader cl = (JNLPClassLoader) JNLPRuntime.getApplication().getClassLoader();
+
+ PermissionCollection clPermissions = cl.getPermissions(source);
+
+ // systempolicy permissions need to be accounted for as well
+ CodeSource appletCS = new CodeSource(JNLPRuntime.getApplication().getJNLPFile().getSourceLocation(), (java.security.cert.Certificate[]) null);
+ Enumeration e = systemPolicy.getPermissions(appletCS).elements();
+ while (e.hasMoreElements())
+ clPermissions.add((Permission) e.nextElement());
+
+ return clPermissions;
+ }
+ }
+
+ // delegate to original Policy object; required to run under WebStart
+ return systemPolicy.getPermissions(source);
+ }
+
+ /**
+ * Refresh.
+ */
+ public void refresh() {
+ // no op
+ }
+
+ /**
+ * Return an all-permissions collection.
+ */
+ private Permissions getAllPermissions() {
+ Permissions result = new Permissions();
+
+ result.add( new AllPermission() );
+ return result;
+ }
+
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ //Include the permissions that may be added during runtime.
+ PermissionCollection pc = getPermissions(domain.getCodeSource());
+ return super.implies(domain, permission) || pc.implies(permission);
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
new file mode 100644
index 0000000..44cf24e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java
@@ -0,0 +1,572 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.io.*;
+import java.nio.channels.FileLock;
+import java.awt.*;
+import java.text.*;
+import java.util.*;
+import java.util.List;
+import java.security.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.cache.*;
+import net.sourceforge.jnlp.services.*;
+import net.sourceforge.jnlp.util.*;
+
+
+/**
+ * Configure and access the runtime environment. This class
+ * stores global jnlp properties such as default download
+ * indicators, the install/base directory, the default resource
+ * update policy, etc. Some settings, such as the base directory,
+ * cannot be changed once the runtime has been initialized.<p>
+ *
+ * The JNLP runtime can be locked to prevent further changes to
+ * the runtime environment except by a specified class. If set,
+ * only instances of the <i>exit class</i> can exit the JVM or
+ * change the JNLP runtime settings once the runtime has been
+ * initialized.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.19 $
+ */
+public class JNLPRuntime {
+
+ static {
+ loadResources();
+ }
+
+ /** the localized resource strings */
+ private static ResourceBundle resources;
+
+ /** the security manager */
+ private static JNLPSecurityManager security;
+
+ /** the security policy */
+ private static JNLPPolicy policy;
+
+ /** the base dir for cache, etc */
+ private static File baseDir;
+
+ /** a default launch handler */
+ private static LaunchHandler handler = null;
+
+ /** default download indicator */
+ private static DownloadIndicator indicator = null;
+
+ /** update policy that controls when to check for updates */
+ private static UpdatePolicy updatePolicy = UpdatePolicy.ALWAYS;
+
+ /** netx window icon */
+ private static Image windowIcon = null;
+
+ /** whether initialized */
+ private static boolean initialized = false;
+
+ /** whether netx is in command-line mode (headless) */
+ private static boolean headless = false;
+
+ /** whether we'll be checking for jar signing */
+ private static boolean verify = true;
+
+ /** whether the runtime uses security */
+ private static boolean securityEnabled = true;
+
+ /** whether debug mode is on */
+ private static boolean debug = false; // package access by Boot
+
+ /** mutex to wait on, for initialization */
+ public static Object initMutex = new Object();
+
+ /** set to true if this is a webstart application. */
+ private static boolean isWebstartApplication;
+
+ /** set to false to indicate another JVM should not be spawned, even if necessary */
+ private static boolean forksAllowed = true;
+
+ /** contains the arguments passed to the jnlp runtime */
+ private static List<String> initialArguments;
+
+ /** Username */
+ public static final String USER = System.getProperty("user.name");
+
+ /** User's home directory */
+ public static final String HOME_DIR = System.getProperty("user.home");
+
+ /** the ~/.netxrc file containing netx settings */
+ public static final String NETXRC_FILE = HOME_DIR + File.separator + ".netxrc";
+
+ /** the ~/.netx directory containing user-specific data */
+ public static final String NETX_DIR = HOME_DIR + File.separator + ".netx";
+
+ /** the ~/.netx/security directory containing security related information */
+ public static final String SECURITY_DIR = NETX_DIR + File.separator + "security";
+
+ /** the ~/.netx/security/trusted.certs file containing trusted certificates */
+ public static final String CERTIFICATES_FILE = SECURITY_DIR + File.separator + "trusted.certs";
+
+ /** the /tmp/ directory used for temporary files */
+ public static final String TMP_DIR = System.getProperty("java.io.tmpdir");
+
+ /**
+ * the /tmp/$USER/netx/locks/ directory containing locks for single instance
+ * applications
+ */
+ public static final String LOCKS_DIR = TMP_DIR + File.separator + USER + File.separator
+ + "netx" + File.separator + "locks";
+
+ /**
+ * The /tmp/$USER/netx/locks/netx_running file is used to indicate if any
+ * instances of netx are running (this file may exist even if no instances
+ * are running). All netx instances acquire a shared lock on this file. If
+ * this file can be locked (using a {@link FileLock}) in exclusive mode, then
+ * other netx instances are not running
+ */
+ public static final String NETX_RUNNING_FILE = LOCKS_DIR + File.separator + "netx_running";
+
+ /** the java.home directory */
+ public static final String JAVA_HOME_DIR = System.getProperty("java.home");
+
+ /** the JNLP file to open to display the network-based about window */
+ public static final String NETX_ABOUT_FILE = JAVA_HOME_DIR + File.separator + "lib"
+ + File.separator + "about.jnlp";
+
+
+
+ /**
+ * Returns whether the JNLP runtime environment has been
+ * initialized. Once initialized, some properties such as the
+ * base directory cannot be changed. Before
+ */
+ public static boolean isInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Initialize the JNLP runtime environment by installing the
+ * security manager and security policy, initializing the JNLP
+ * standard services, etc.<p>
+ *
+ * This method cannot be called more than once. Once
+ * initialized, methods that alter the runtime can only be
+ * called by the exit class.<p>
+ *
+ * @param isApplication is true if a webstart application is being initialized
+ *
+ * @throws IllegalStateException if the runtime was previously initialized
+ */
+ public static void initialize(boolean isApplication) throws IllegalStateException {
+ checkInitialized();
+
+ isWebstartApplication = isApplication;
+
+ //Setting the system property for javawebstart's version.
+ //The version stored will be the same as java's version.
+ System.setProperty("javawebstart.version", "javaws-" +
+ System.getProperty("java.version"));
+
+ if (headless == false)
+ checkHeadless();
+
+ if (!headless && windowIcon == null)
+ loadWindowIcon();
+
+ if (!headless && indicator == null)
+ indicator = new DefaultDownloadIndicator();
+
+ if (handler == null)
+ handler = new DefaultLaunchHandler();
+
+ if (baseDir == null)
+ baseDir = getDefaultBaseDir();
+
+ if (baseDir == null)
+ throw new IllegalStateException(JNLPRuntime.getMessage("BNoBase"));
+
+ ServiceManager.setServiceManagerStub(new XServiceManagerStub()); // ignored if we're running under Web Start
+
+ policy = new JNLPPolicy();
+ security = new JNLPSecurityManager(); // side effect: create JWindow
+
+ if (securityEnabled) {
+ Policy.setPolicy(policy); // do first b/c our SM blocks setPolicy
+ System.setSecurityManager(security);
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Returns true if a webstart application has been initialized, and false
+ * for a plugin applet.
+ */
+ public static boolean isWebstartApplication() {
+ return isWebstartApplication;
+ }
+
+ /**
+ * Returns the window icon.
+ */
+ public static Image getWindowIcon() {
+ return windowIcon;
+ }
+
+ /**
+ * Sets the window icon that is displayed in Java applications
+ * and applets instead of the default Java icon.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setWindowIcon(Image image) {
+ checkExitClass();
+ windowIcon = image;
+ }
+
+ /**
+ * Returns whether the JNLP client will use any AWT/Swing
+ * components.
+ */
+ public static boolean isHeadless() {
+ return headless;
+ }
+
+ /**
+ * Returns whether we are verifying code signing.
+ */
+ public static boolean isVerifying() {
+ return verify;
+ }
+ /**
+ * Sets whether the JNLP client will use any AWT/Swing
+ * components. In headless mode, client features that use the
+ * AWT are disabled such that the client can be used in
+ * headless mode (<code>java.awt.headless=true</code>).
+ *
+ * @throws IllegalStateException if the runtime was previously initialized
+ */
+ public static void setHeadless(boolean enabled) {
+ checkInitialized();
+ headless = enabled;
+ }
+
+ /**
+ * Sets whether we will verify code signing.
+ * @throws IllegalStateException if the runtime was previously initialized
+ */
+ public static void setVerify(boolean enabled) {
+ checkInitialized();
+ verify = enabled;
+ }
+
+ /**
+ * Return the base directory containing the cache, persistence
+ * store, etc.
+ */
+ public static File getBaseDir() {
+ return baseDir;
+ }
+
+ /**
+ * Sets the base directory containing the cache, persistence
+ * store, etc.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setBaseDir(File baseDirectory) {
+ checkInitialized();
+ baseDir = baseDirectory;
+ }
+
+ /**
+ * Returns whether the secure runtime environment is enabled.
+ */
+ public static boolean isSecurityEnabled() {
+ return securityEnabled;
+ }
+
+ /**
+ * Sets whether to enable the secure runtime environment.
+ * Disabling security can increase performance for some
+ * applications, and can be used to use netx with other code
+ * that uses its own security manager or policy.
+ *
+ * Disabling security is not recommended and should only be
+ * used if the JNLP files opened are trusted. This method can
+ * only be called before initalizing the runtime.<p>
+ *
+ * @param enabled whether security should be enabled
+ * @throws IllegalStateException if the runtime is already initialized
+ */
+ public static void setSecurityEnabled(boolean enabled) {
+ checkInitialized();
+ securityEnabled = enabled;
+ }
+
+ /**
+ * Returns the system default base dir for or if not set,
+ * prompts the user for the location.
+ *
+ * @return the base dir, or null if the user canceled the dialog
+ * @throws IOException if there was an io exception
+ */
+ public static File getDefaultBaseDir() {
+ PropertiesFile props = JNLPRuntime.getProperties();
+
+ String baseStr = props.getProperty("basedir");
+ if (baseStr != null)
+ return new File(baseStr);
+
+ String homeDir = HOME_DIR;
+ File baseDir = new File(NETX_DIR);
+ if (homeDir == null || (!baseDir.isDirectory() && !baseDir.mkdir()))
+ return null;
+
+ props.setProperty("basedir", baseDir.toString());
+ props.store();
+
+ return baseDir;
+ }
+
+ /**
+ * Set a class that can exit the JVM; if not set then any class
+ * can exit the JVM.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setExitClass(Class exitClass) {
+ checkExitClass();
+ security.setExitClass(exitClass);
+ }
+
+ /**
+ * Disables applets from calling exit.
+ *
+ * Once disabled, exit cannot be re-enabled for the duration of the JVM instance
+ */
+ public static void disableExit() {
+ security.disableExit();
+ }
+
+ /**
+ * Return the current Application, or null if none can be
+ * determined.
+ */
+ public static ApplicationInstance getApplication() {
+ return security.getApplication();
+ }
+
+ /**
+ * Return a PropertiesFile object backed by the runtime's
+ * properties file.
+ */
+ public static PropertiesFile getProperties() {
+ File netxrc = new File(NETXRC_FILE);
+ return new PropertiesFile(netxrc);
+ }
+
+ /**
+ * Return whether debug statements for the JNLP client code
+ * should be printed.
+ */
+ public static boolean isDebug() {
+ return debug;
+ }
+
+ /**
+ * Sets whether debug statements for the JNLP client code
+ * should be printed to the standard output.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setDebug(boolean enabled) {
+ checkExitClass();
+ debug = enabled;
+ }
+
+ /**
+ * Sets the default update policy.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setDefaultUpdatePolicy(UpdatePolicy policy) {
+ checkExitClass();
+ updatePolicy = policy;
+ }
+
+ /**
+ * Returns the default update policy.
+ */
+ public static UpdatePolicy getDefaultUpdatePolicy() {
+ return updatePolicy;
+ }
+
+ /**
+ * Sets the default launch handler.
+ */
+ public static void setDefaultLaunchHandler(LaunchHandler handler) {
+ checkExitClass();
+ JNLPRuntime.handler = handler;
+ }
+
+ /**
+ * Returns the default launch handler.
+ */
+ public static LaunchHandler getDefaultLaunchHandler() {
+ return handler;
+ }
+
+ /**
+ * Sets the default download indicator.
+ *
+ * @throws IllegalStateException if caller is not the exit class
+ */
+ public static void setDefaultDownloadIndicator(DownloadIndicator indicator) {
+ checkExitClass();
+ JNLPRuntime.indicator = indicator;
+ }
+
+ /**
+ * Returns the default download indicator.
+ */
+ public static DownloadIndicator getDefaultDownloadIndicator() {
+ return indicator;
+ }
+
+ /**
+ * Returns the localized resource string identified by the
+ * specified key. If the message is empty, a null is
+ * returned.
+ */
+ public static String getMessage(String key) {
+ try {
+ String result = resources.getString(key);
+ if (result.length() == 0)
+ return null;
+ else
+ return result;
+ }
+ catch (Exception ex) {
+ if (!key.equals("RNoResource"))
+ return getMessage("RNoResource", new Object[] {key});
+ else
+ return "Missing resource: "+key;
+ }
+ }
+
+ /**
+ * Returns the localized resource string using the specified
+ * arguments.
+ *
+ * @param args the formatting arguments to the resource string
+ */
+ public static String getMessage(String key, Object args[]) {
+ return MessageFormat.format(getMessage(key), args);
+ }
+
+ /**
+ * Returns true if the current runtime will fork
+ */
+ public static boolean getForksAllowed() {
+ return forksAllowed;
+ }
+
+ public static void setForksAllowed(boolean value) {
+ checkInitialized();
+ forksAllowed = value;
+ }
+
+ /**
+ * Throws an exception if called when the runtime is
+ * already initialized.
+ */
+ private static void checkInitialized() {
+ if (initialized)
+ throw new IllegalStateException("JNLPRuntime already initialized.");
+ }
+
+ /**
+ * Throws an exception if called with security enabled but
+ * a caller is not the exit class and the runtime has been
+ * initialized.
+ */
+ private static void checkExitClass() {
+ if (securityEnabled && initialized)
+ if (!security.isExitClass())
+ throw new IllegalStateException("Caller is not the exit class");
+ }
+
+ /**
+ * Check whether the VM is in headless mode.
+ */
+ private static void checkHeadless() {
+ //if (GraphicsEnvironment.isHeadless()) // jdk1.4+ only
+ // headless = true;
+ try {
+ if ("true".equalsIgnoreCase(System.getProperty("java.awt.headless")))
+ headless = true;
+ }
+ catch (SecurityException ex) {
+ }
+ }
+
+ /**
+ * Load the resources.
+ */
+ private static void loadResources() {
+ try {
+ resources = ResourceBundle.getBundle("net.sourceforge.jnlp.resources.Messages");
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException("Missing resource bundle in netx.jar:net/sourceforge/jnlp/resource/Messages.properties");
+ }
+ }
+
+ /**
+ * Load the window icon.
+ */
+ private static void loadWindowIcon() {
+ if (windowIcon != null)
+ return;
+
+ try {
+ windowIcon = new javax.swing.ImageIcon((new sun.misc.Launcher())
+ .getClassLoader().getResource("net/sourceforge/jnlp/resources/netx-icon.png")).getImage();
+ }
+ catch (Exception ex) {
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+ }
+
+
+ public static void setInitialArgments(List<String> args) {
+ checkInitialized();
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null)
+ securityManager.checkPermission(new AllPermission());
+ initialArguments = args;
+ }
+
+ public static List<String> getInitialArguments() {
+ return initialArguments;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java b/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java
new file mode 100644
index 0000000..3934607
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java
@@ -0,0 +1,541 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.runtime;
+
+import java.awt.Frame;
+import java.awt.Window;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.lang.ref.WeakReference;
+import java.net.SocketPermission;
+import java.security.AllPermission;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.SecurityPermission;
+import java.util.PropertyPermission;
+
+import javax.swing.JWindow;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+import net.sourceforge.jnlp.services.ServiceUtil;
+import net.sourceforge.jnlp.util.WeakList;
+import sun.awt.AWTSecurityManager;
+import sun.awt.AppContext;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Security manager for JNLP environment. This security manager
+ * cannot be replaced as it always denies attempts to replace the
+ * security manager or policy.<p>
+ *
+ * The JNLP security manager tracks windows created by an
+ * application, allowing those windows to be disposed when the
+ * application exits but the JVM does not. If security is not
+ * enabled then the first application to call System.exit will
+ * halt the JVM.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.17 $
+ */
+class JNLPSecurityManager extends AWTSecurityManager {
+
+ // todo: some apps like JDiskReport can close the VM even when
+ // an exit class is set - fix!
+
+ // todo: create an event dispatch thread for each application,
+ // so that the context classloader doesn't have to be switched
+ // to the foreground application (the currently the approach
+ // since some apps need their classloader as event dispatch
+ // thread's context classloader).
+
+ // todo: use a custom Permission object to identify the current
+ // application in an AccessControlContext by setting a side
+ // effect in its implies method. Use a custom
+ // AllPermissions-like permission to do this for apps granted
+ // all permissions (but investigate whether this will nuke
+ // the all-permission optimizations in the JRE).
+
+ // todo: does not exit app if close button pressed on JFrame
+ // with CLOSE_ON_EXIT (or whatever) set; if doesn't exit, use an
+ // WindowListener to catch WindowClosing event, then if exit is
+ // called immediately afterwards from AWT thread.
+
+ // todo: deny all permissions to applications that should have
+ // already been 'shut down' by closing their resources and
+ // interrupt the threads if operating in a shared-VM (exit class
+ // set). Deny will probably will slow checks down a lot though.
+
+ // todo: weak remember last getProperty application and
+ // re-install properties if another application calls, or find
+ // another way for different apps to have different properties
+ // in java.lang.Sytem with the same names.
+
+ private static String R(String key) { return JNLPRuntime.getMessage(key); }
+
+ /** only class that can exit the JVM, if set */
+ private Object exitClass = null;
+
+ /** this exception prevents exiting the JVM */
+ private SecurityException closeAppEx = // making here prevents huge stack traces
+ new SecurityException(JNLPRuntime.getMessage("RShutdown"));
+
+ /** weak list of windows created */
+ private WeakList weakWindows = new WeakList();
+
+ /** weak list of applications corresponding to window list */
+ private WeakList weakApplications = new WeakList();
+
+ /** weak reference to most app who's windows was most recently activated */
+ private WeakReference activeApplication = null;
+
+ /** Sets whether or not exit is allowed (in the context of the plugin, this is always false) */
+ private boolean exitAllowed = true;
+
+ /**
+ * The AppContext of the main application (netx). We need to store this here
+ * so we can return this when no code from an external application is
+ * running on the thread
+ */
+ private AppContext mainAppContext;
+
+ /**
+ * Creates a JNLP SecurityManager.
+ */
+ JNLPSecurityManager() {
+ // this has the side-effect of creating the Swing shared Frame
+ // owner. Since no application is running at this time, it is
+ // not added to any window list when checkTopLevelWindow is
+ // called for it (and not disposed).
+
+ if (!JNLPRuntime.isHeadless())
+ new JWindow().getOwner();
+
+ mainAppContext = AppContext.getAppContext();
+ }
+
+ /**
+ * Returns whether the exit class is present on the stack, or
+ * true if no exit class is set.
+ */
+ public boolean isExitClass() {
+ return isExitClass(getClassContext());
+ }
+
+ /**
+ * Returns whether the exit class is present on the stack, or
+ * true if no exit class is set.
+ */
+ private boolean isExitClass(Class stack[]) {
+ if (exitClass == null)
+ return true;
+
+ for (int i=0; i < stack.length; i++)
+ if (stack[i] == exitClass)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Set the exit class, which is the only class that can exit the
+ * JVM; if not set then any class can exit the JVM.
+ *
+ * @param exitClass the exit class
+ * @throws IllegalStateException if the exit class is already set
+ */
+ public void setExitClass(Class exitClass) throws IllegalStateException {
+ if (this.exitClass != null)
+ throw new IllegalStateException(R("RExitTaken"));
+
+ this.exitClass = exitClass;
+ }
+
+ /**
+ * Return the current Application, or null if none can be
+ * determined.
+ */
+ protected ApplicationInstance getApplication() {
+ return getApplication(getClassContext(), 0);
+ }
+
+ /**
+ * Return the application the opened the specified window (only
+ * call from event dispatch thread).
+ */
+ protected ApplicationInstance getApplication(Window window) {
+ for (int i = weakWindows.size(); i-->0;) {
+ Window w = (Window) weakWindows.get(i);
+ if (w == null) {
+ weakWindows.remove(i);
+ weakApplications.remove(i);
+ }
+
+ if (w == window)
+ return (ApplicationInstance) weakApplications.get(i);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the current Application, or null.
+ */
+ protected ApplicationInstance getApplication(Class stack[], int maxDepth) {
+ if (maxDepth <= 0)
+ maxDepth = stack.length;
+
+ // this needs to be tightened up
+ for (int i=0; i < stack.length && i < maxDepth; i++) {
+ if (stack[i].getClassLoader() instanceof JNLPClassLoader) {
+ JNLPClassLoader loader = (JNLPClassLoader) stack[i].getClassLoader();
+
+ if (loader != null && loader.getApplication() != null) {
+ return loader.getApplication();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the application's thread group if the application can
+ * be determined; otherwise returns super.getThreadGroup()
+ */
+ public ThreadGroup getThreadGroup() {
+ ApplicationInstance app = getApplication();
+ if (app == null)
+ return super.getThreadGroup();
+
+ return app.getThreadGroup();
+ }
+
+ /**
+ * Throws a SecurityException if the permission is denied,
+ * otherwise return normally. This method always denies
+ * permission to change the security manager or policy.
+ */
+ public void checkPermission(Permission perm) {
+ String name = perm.getName();
+
+ // Enable this manually -- it'll produce too much output for -verbose
+ // otherwise.
+ // if (true)
+ // System.out.println("Checking permission: " + perm.toString());
+
+ if (!JNLPRuntime.isWebstartApplication() &&
+ ("setPolicy".equals(name) || "setSecurityManager".equals(name)))
+ throw new SecurityException(R("RCantReplaceSM"));
+
+ try {
+ // deny all permissions to stopped applications
+ // The call to getApplication() below might not work if an
+ // application hasn't been fully initialized yet.
+// if (JNLPRuntime.isDebug()) {
+// if (!"getClassLoader".equals(name)) {
+// ApplicationInstance app = getApplication();
+// if (app != null && !app.isRunning())
+// throw new SecurityException(R("RDenyStopped"));
+// }
+// }
+
+ try {
+ super.checkPermission(perm);
+ } catch (SecurityException se) {
+
+ //This section is a special case for dealing with SocketPermissions.
+ if (JNLPRuntime.isDebug())
+ System.err.println("Requesting permission: " + perm.toString());
+
+ //Change this SocketPermission's action to connect and accept
+ //(and resolve). This is to avoid asking for connect permission
+ //on every address resolve.
+ Permission tmpPerm = null;
+ if (perm instanceof SocketPermission) {
+ tmpPerm = new SocketPermission(perm.getName(),
+ SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
+
+ // before proceeding, check if we are trying to connect to same origin
+ ApplicationInstance app = getApplication();
+ JNLPFile file = app.getJNLPFile();
+
+ String srcHost = file.getSourceLocation().getAuthority();
+ String destHost = name;
+
+ // host = abc.xyz.com or abc.xyz.com:<port>
+ if (destHost.indexOf(':') >= 0)
+ destHost = destHost.substring(0, destHost.indexOf(':'));
+
+ // host = abc.xyz.com
+ String[] hostComponents = destHost.split("\\.");
+
+ int length = hostComponents.length;
+ if (length >= 2) {
+
+ // address is in xxx.xxx.xxx format
+ destHost = hostComponents[length -2] + "." + hostComponents[length -1];
+
+ // host = xyz.com i.e. origin
+ boolean isDestHostName = false;
+
+ // make sure that it is not an ip address
+ try {
+ Integer.parseInt(hostComponents[length -1]);
+ } catch (NumberFormatException e) {
+ isDestHostName = true;
+ }
+
+ if (isDestHostName) {
+ // okay, destination is hostname. Now figure out if it is a subset of origin
+ if (srcHost.endsWith(destHost)) {
+ addPermission(tmpPerm);
+ return;
+ }
+ }
+ }
+
+ } else if (perm instanceof SecurityPermission) {
+
+ // JCE's initialization requires putProviderProperty permission
+ if (perm.equals(new SecurityPermission("putProviderProperty.SunJCE"))) {
+ if (inTrustedCallChain("com.sun.crypto.provider.SunJCE", "run")) {
+ return;
+ }
+ }
+
+ } else if (perm instanceof RuntimePermission) {
+
+ // KeyGenerator's init method requires internal spec access
+ if (perm.equals(new SecurityPermission("accessClassInPackage.sun.security.internal.spec"))) {
+ if (inTrustedCallChain("javax.crypto.KeyGenerator", "init")) {
+ return;
+ }
+ }
+
+ } else {
+ tmpPerm = perm;
+ }
+
+ if (tmpPerm != null) {
+ //askPermission will only prompt the user on SocketPermission
+ //meaning we're denying all other SecurityExceptions that may arise.
+ if (askPermission(tmpPerm)) {
+ addPermission(tmpPerm);
+ //return quietly.
+ } else {
+ throw se;
+ }
+ }
+ }
+ }
+ catch (SecurityException ex) {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Denying permission: "+perm);
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Returns weather the given class and method are in the current stack,
+ * and whether or not everything upto then is trusted
+ *
+ * @param className The name of the class to look for in the stack
+ * @param methodName The name of the method for the given class to look for in the stack
+ * @return Weather or not class::method() are in the chain, and everything upto there is trusted
+ */
+ private boolean inTrustedCallChain(String className, String methodName) {
+
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+
+ for (int i=0; i < stack.length; i++) {
+
+ // Everything up to the desired class/method must be trusted
+ if (!stack[i].getClass().getProtectionDomain().implies(new AllPermission())) {
+ return false;
+ }
+
+ if (stack[i].getClassName().equals(className) &&
+ stack[i].getMethodName().equals(methodName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Asks the user whether or not to grant permission.
+ * @param perm the permission to be granted
+ * @return true if the permission was granted, false otherwise.
+ */
+ private boolean askPermission(Permission perm) {
+
+ ApplicationInstance app = getApplication();
+ if (app != null && !app.isSigned()) {
+ if (perm instanceof SocketPermission
+ && ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.NETWORK, perm.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds a permission to the JNLPClassLoader.
+ * @param perm the permission to add to the JNLPClassLoader
+ */
+ private void addPermission(Permission perm) {
+ if (JNLPRuntime.getApplication().getClassLoader() instanceof JNLPClassLoader) {
+
+ JNLPClassLoader cl = (JNLPClassLoader) JNLPRuntime.getApplication().getClassLoader();
+ cl.addPermission(perm);
+ if (JNLPRuntime.isDebug()) {
+ if (cl.getPermissions(null).implies(perm))
+ System.err.println("Added permission: " + perm.toString());
+ else
+ System.err.println("Unable to add permission: " + perm.toString());
+ }
+ } else {
+ if (JNLPRuntime.isDebug())
+ System.err.println("Unable to add permission: " + perm + ", classloader not JNLP.");
+ }
+ }
+
+ /**
+ * Checks whether the window can be displayed without an applet
+ * warning banner, and adds the window to the list of windows to
+ * be disposed when the calling application exits.
+ */
+ public boolean checkTopLevelWindow(Object window) {
+ ApplicationInstance app = getApplication();
+
+ // remember window -> application mapping for focus, close on exit
+ if (app != null && window instanceof Window) {
+ Window w = (Window) window;
+
+ if (JNLPRuntime.isDebug())
+ System.err.println("SM: app: "+app.getTitle()+" is adding a window: "+window);
+
+ weakWindows.add(window); // for mapping window -> app
+ weakApplications.add(app);
+
+ app.addWindow(w);
+ }
+
+ // change coffee cup to netx for default icon
+ if (window instanceof Window)
+ for (Window w = (Window)window; w != null; w = w.getOwner())
+ if (window instanceof Frame)
+ ((Frame)window).setIconImage(JNLPRuntime.getWindowIcon());
+
+ // todo: set awt.appletWarning to custom message
+ // todo: logo on with glass pane on JFrame/JWindow?
+
+ return super.checkTopLevelWindow(window);
+ }
+
+ /**
+ * Checks whether the caller can exit the system. This method
+ * identifies whether the caller is a real call to Runtime.exec
+ * and has special behavior when returning from this method
+ * would exit the JVM and an exit class is set: if the caller is
+ * not the exit class then the calling application will be
+ * stopped and its resources destroyed (when possible), and an
+ * exception will be thrown to prevent the JVM from shutting
+ * down.<p>
+ *
+ * Calls not from Runtime.exit or with no exit class set will
+ * behave normally, and the exit class can always exit the JVM.
+ */
+ public void checkExit(int status) {
+
+ // applets are not allowed to exit, but the plugin main class (primordial loader) is
+ Class stack[] = getClassContext();
+ if (!exitAllowed) {
+ for (int i=0; i < stack.length; i++)
+ if (stack[i].getClassLoader() != null)
+ throw new AccessControlException("Applets may not call System.exit()");
+ }
+
+ super.checkExit(status);
+
+ boolean realCall = (stack[1] == Runtime.class);
+
+ if (isExitClass(stack)) // either exitClass called or no exitClass set
+ return; // to Runtime.exit or fake call to see if app has permission
+
+ // not called from Runtime.exit()
+ if (!realCall) {
+ // apps that can't exit should think they can exit normally
+ super.checkExit(status);
+ return;
+ }
+
+ // but when they really call, stop only the app instead of the JVM
+ ApplicationInstance app = getApplication(stack, 0);
+ if (app == null) {
+ // should check caller to make sure it is JFrame.close or
+ // other known System.exit call
+ if (activeApplication != null)
+ app = (ApplicationInstance) activeApplication.get();
+
+ if (app == null)
+ throw new SecurityException(R("RExitNoApp"));
+ }
+
+ app.destroy();
+
+ throw closeAppEx;
+ }
+
+ protected void disableExit() {
+ exitAllowed = false;
+ }
+
+ /**
+ * This returns the appropriate {@link AppContext}. Hooks in AppContext
+ * check if the current {@link SecurityManager} is an instance of
+ * AWTSecurityManager and if so, call this method to give it a chance to
+ * return the appropriate appContext based on the application that is
+ * running.<p>
+ *
+ * This can be called from any thread (possibly a swing thread) to find out
+ * the AppContext for the thread (which may correspond to a particular
+ * applet).
+ */
+ @Override
+ public AppContext getAppContext() {
+ ApplicationInstance app = getApplication();
+ if (app == null) {
+ /*
+ * if we cannot find an application based on the code on the stack,
+ * then assume it is the main application
+ */
+ return mainAppContext;
+ } else {
+ return app.getAppContext();
+ }
+
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/runtime/package.html b/netx/net/sourceforge/jnlp/runtime/package.html
new file mode 100644
index 0000000..cade6a2
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/runtime/package.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+
+This package contains the classes that manage the secure runtime
+environment for JNLP apps.
+
+<h2>Package Specification</h2>
+
+<ul>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/download-spec.html">JNLP specification</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+<li><a target="_top" href="http://jnlp.sourceforge.net/netx/">Netx JNLP Client</a>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/">Java Web Start JNLP Client</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
+
+
diff --git a/netx/net/sourceforge/jnlp/security/AccessWarningPane.java b/netx/net/sourceforge/jnlp/security/AccessWarningPane.java
new file mode 100644
index 0000000..5afbfff
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/AccessWarningPane.java
@@ -0,0 +1,209 @@
+/* AccessWarningPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.util.FileUtils;
+
+/**
+ * Provides a panel to show inside a SecurityWarningDialog. These dialogs are
+ * used to warn the user when either signed code (with or without signing
+ * issues) is going to be run, or when service permission (file, clipboard,
+ * printer, etc) is needed with unsigned code.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+public class AccessWarningPane extends SecurityDialogPanel {
+
+ JCheckBox alwaysAllow;
+ Object[] extras;
+
+ public AccessWarningPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ addComponents();
+ }
+
+ public AccessWarningPane(SecurityWarningDialog x, Object[] extras, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ this.extras = extras;
+ addComponents();
+ }
+
+ /**
+ * Creates the actual GUI components, and adds it to this panel
+ */
+ private void addComponents() {
+ SecurityWarningDialog.AccessType type = parent.getAccessType();
+ JNLPFile file = parent.getFile();
+
+ String name = "";
+ String publisher = "";
+ String from = "";
+
+ //We don't worry about exceptions when trying to fill in
+ //these strings -- we just want to fill in as many as possible.
+ try {
+ name = file.getInformation().getTitle() != null ? file.getInformation().getTitle() : "<no associated certificate>";
+ } catch (Exception e) {
+ }
+
+ try {
+ publisher = file.getInformation().getVendor() != null ? file.getInformation().getVendor() : "<no associated certificate>";
+ } catch (Exception e) {
+ }
+
+ try {
+ from = !file.getInformation().getHomepage().toString().equals("") ? file.getInformation().getHomepage().toString() : file.getSourceLocation().getAuthority();
+ } catch (Exception e) {
+ from = file.getSourceLocation().getAuthority();
+ }
+
+ //Top label
+ String topLabelText = "";
+ switch (type) {
+ case READ_FILE:
+ if (extras != null && extras.length > 0 && extras[0] instanceof String) {
+ topLabelText = R("SFileReadAccess", FileUtils.displayablePath((String)extras[0]));
+ } else {
+ topLabelText = R("SFileReadAccess", R("AFileOnTheMachine"));
+ }
+ break;
+ case WRITE_FILE:
+ if (extras != null && extras.length > 0 && extras[0] instanceof String) {
+ topLabelText = R("SFileWriteAccess", FileUtils.displayablePath((String)extras[0]));
+ } else {
+ topLabelText = R("SFileWriteAccess", R("AFileOnTheMachine"));
+ }
+ break;
+ case CREATE_DESTKOP_SHORTCUT:
+ topLabelText = R("SDesktopShortcut");
+ break;
+ case CLIPBOARD_READ:
+ topLabelText = R("SClipboardReadAccess");
+ break;
+ case CLIPBOARD_WRITE:
+ topLabelText = R("SClipboardWriteAccess");
+ break;
+ case PRINTER:
+ topLabelText = R("SPrinterAccess");
+ break;
+ case NETWORK:
+ if (extras != null && extras.length >= 0)
+ topLabelText = R("SNetworkAccess", extras[0]);
+ else
+ topLabelText = R("SNetworkAccess", "(address here)");
+ }
+
+ ImageIcon icon = new ImageIcon((new sun.misc.Launcher()).getClassLoader().getResource("net/sourceforge/jnlp/resources/warning.png"));
+ JLabel topLabel = new JLabel(htmlWrap(topLabelText), icon, SwingConstants.LEFT);
+ topLabel.setFont(new Font(topLabel.getFont().toString(),
+ Font.BOLD, 12));
+ JPanel topPanel = new JPanel(new BorderLayout());
+ topPanel.setBackground(Color.WHITE);
+ topPanel.add(topLabel, BorderLayout.CENTER);
+ topPanel.setPreferredSize(new Dimension(450,100));
+ topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //application info
+ JLabel nameLabel = new JLabel("Name: " + name);
+ nameLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ JLabel publisherLabel = new JLabel("Publisher: " + publisher);
+ publisherLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ JLabel fromLabel = new JLabel("From: " + from);
+ fromLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ alwaysAllow = new JCheckBox("Always allow this action");
+ alwaysAllow.setEnabled(false);
+
+ JPanel infoPanel = new JPanel(new GridLayout(4,1));
+ infoPanel.add(nameLabel);
+ infoPanel.add(publisherLabel);
+ infoPanel.add(fromLabel);
+ infoPanel.add(alwaysAllow);
+ infoPanel.setBorder(BorderFactory.createEmptyBorder(25,25,25,25));
+
+ //run and cancel buttons
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ JButton run = new JButton("Allow");
+ JButton cancel = new JButton("Cancel");
+ run.addActionListener(createSetValueListener(parent,0));
+ run.addActionListener(new CheckBoxListener());
+ cancel.addActionListener(createSetValueListener(parent, 1));
+ initialFocusComponent = cancel;
+ buttonPanel.add(run);
+ buttonPanel.add(cancel);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //all of the above
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ add(topPanel);
+ add(infoPanel);
+ add(buttonPanel);
+
+ }
+
+
+ private class CheckBoxListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ if (alwaysAllow != null && alwaysAllow.isSelected()) {
+ // TODO: somehow tell the ApplicationInstance
+ // to stop asking for permission
+ }
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/AppletWarningPane.java b/netx/net/sourceforge/jnlp/security/AppletWarningPane.java
new file mode 100644
index 0000000..8bd14ef
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/AppletWarningPane.java
@@ -0,0 +1,115 @@
+/* AppletWarningPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+
+public class AppletWarningPane extends SecurityDialogPanel {
+
+ public AppletWarningPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ addComponents();
+ }
+
+ protected void addComponents() {
+
+ //Top label
+ String topLabelText = "While support for verifying signed code" +
+ " has not been implemented yet, some applets will not run " +
+ "properly under the default restricted security level.";
+ String bottomLabelText = "Do you want to run this applet under the " +
+ "restricted security level? (clicking No will run this applet " +
+ "without any security checking, and should only be done if you " +
+ "trust the applet!)";
+
+ JLabel topLabel = new JLabel(htmlWrap(topLabelText));
+ topLabel.setFont(new Font(topLabel.getFont().toString(),
+ Font.BOLD, 12));
+ JPanel topPanel = new JPanel(new BorderLayout());
+ topPanel.setBackground(Color.WHITE);
+ topPanel.add(topLabel, BorderLayout.CENTER);
+ topPanel.setPreferredSize(new Dimension(400,80));
+ topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ JLabel bottomLabel = new JLabel(htmlWrap(bottomLabelText));
+ JPanel infoPanel = new JPanel(new BorderLayout());
+ infoPanel.add(bottomLabel, BorderLayout.CENTER);
+ infoPanel.setPreferredSize(new Dimension(400,80));
+ infoPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //run and cancel buttons
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ JButton yes = new JButton("Yes");
+ JButton no = new JButton("No");
+ JButton cancel = new JButton("Cancel");
+ int buttonWidth = cancel.getMinimumSize().width;
+ int buttonHeight = cancel.getMinimumSize().height;
+ Dimension d = new Dimension(buttonWidth, buttonHeight);
+ yes.setPreferredSize(d);
+ no.setPreferredSize(d);
+ cancel.setPreferredSize(d);
+ yes.addActionListener(createSetValueListener(parent, 0));
+ no.addActionListener(createSetValueListener(parent, 1));
+ cancel.addActionListener(createSetValueListener(parent, 2));
+ initialFocusComponent = cancel;
+ buttonPanel.add(yes);
+ buttonPanel.add(no);
+ buttonPanel.add(cancel);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //all of the above
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ add(topPanel);
+ add(infoPanel);
+ add(buttonPanel);
+
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/CertVerifier.java b/netx/net/sourceforge/jnlp/security/CertVerifier.java
new file mode 100644
index 0000000..cef69c3
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/CertVerifier.java
@@ -0,0 +1,92 @@
+/* CertVerifier.java
+ Copyright (C) 2009 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+
+/**
+ * An interface that provides various details about a certificate
+ */
+
+public interface CertVerifier {
+
+ /**
+ * Return if the publisher is already trusted
+ */
+ public boolean getAlreadyTrustPublisher();
+
+ /**
+ * Return if the root is in CA certs
+ */
+ public boolean getRootInCacerts();
+
+ /**
+ * Return if there are signing issues with the certificate(s) being veried
+ */
+ public boolean hasSigningIssues();
+
+ /**
+ * Return if there are no signing issues with this cert (!hasSigningIssues())
+ */
+ public boolean noSigningIssues();
+
+ /**
+ * Get the details regarding issue(s) with this certificate
+ */
+ public ArrayList<String> getDetails();
+
+ /**
+ * Return a valid certificate path to this certificate(s) being verified
+ * @return The CertPath
+ */
+ public ArrayList<CertPath> getCerts();
+
+ /**
+ * Returns the application's publisher's certificate.
+ */
+ public abstract Certificate getPublisher();
+
+ /**
+ * Returns the application's root's certificate. This
+ * may return the same certificate as getPublisher() in
+ * the event that the application is self signed.
+ */
+ public abstract Certificate getRoot();
+}
diff --git a/netx/net/sourceforge/jnlp/security/CertWarningPane.java b/netx/net/sourceforge/jnlp/security/CertWarningPane.java
new file mode 100644
index 0000000..59559c4
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/CertWarningPane.java
@@ -0,0 +1,254 @@
+/* CertWarningPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.PluginBridge;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.tools.KeyTool;
+
+/**
+ * Provides the panel for using inside a SecurityWarningDialog. These dialogs are
+ * used to warn the user when either signed code (with or without signing
+ * issues) is going to be run, or when service permission (file, clipboard,
+ * printer, etc) is needed with unsigned code.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+public class CertWarningPane extends SecurityDialogPanel {
+
+ JCheckBox alwaysTrust;
+ CertVerifier certVerifier;
+
+ public CertWarningPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ this.certVerifier = certVerifier;
+ addComponents();
+ }
+
+ /**
+ * Creates the actual GUI components, and adds it to this panel
+ */
+ private void addComponents() {
+ SecurityWarningDialog.AccessType type = parent.getAccessType();
+ JNLPFile file = parent.getFile();
+ Certificate c = parent.getJarSigner().getPublisher();
+
+ String name = "";
+ String publisher = "";
+ String from = "";
+
+ //We don't worry about exceptions when trying to fill in
+ //these strings -- we just want to fill in as many as possible.
+ try {
+ if ((certVerifier instanceof HttpsCertVerifier) &&
+ (c instanceof X509Certificate))
+ name = SecurityUtil.getCN(((X509Certificate)c)
+ .getSubjectX500Principal().getName());
+ else if (file instanceof PluginBridge)
+ name = file.getTitle();
+ else
+ name = file.getInformation().getTitle();
+ } catch (Exception e) {
+ }
+
+ try {
+ if (c instanceof X509Certificate) {
+ publisher = SecurityUtil.getCN(((X509Certificate)c)
+ .getSubjectX500Principal().getName());
+ }
+ } catch (Exception e) {
+ }
+
+ try {
+ if (file instanceof PluginBridge)
+ from = file.getCodeBase().getHost();
+ else
+ from = file.getInformation().getHomepage().toString();
+ } catch (Exception e) {
+ }
+
+ //Top label
+ String topLabelText = "";
+ String propertyName = "";
+ if (certVerifier instanceof HttpsCertVerifier)
+ {
+ topLabelText = "The website's certificate cannot be verified. " +
+ "Do you want to continue?";
+ propertyName = "OptionPane.warningIcon";
+ }
+ else
+ switch (type) {
+ case VERIFIED:
+ topLabelText = R("SSigVerified");
+ propertyName = "OptionPane.informationIcon";
+ break;
+ case UNVERIFIED:
+ topLabelText = R("SSigUnverified");
+ propertyName = "OptionPane.warningIcon";
+ break;
+ case SIGNING_ERROR:
+ topLabelText = R("SSignatureError");
+ propertyName = "OptionPane.warningIcon";
+ break;
+ }
+ ImageIcon icon = new ImageIcon((new sun.misc.Launcher())
+ .getClassLoader().getResource("net/sourceforge/jnlp/resources/warning.png"));
+ JLabel topLabel = new JLabel(htmlWrap(topLabelText), icon, SwingConstants.LEFT);
+ topLabel.setFont(new Font(topLabel.getFont().toString(),
+ Font.BOLD, 12));
+ JPanel topPanel = new JPanel(new BorderLayout());
+ topPanel.setBackground(Color.WHITE);
+ topPanel.add(topLabel, BorderLayout.CENTER);
+ topPanel.setPreferredSize(new Dimension(400,60));
+ topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //application info
+ JLabel nameLabel = new JLabel("Name: " + name);
+ nameLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ JLabel publisherLabel = new JLabel("Publisher: " + publisher);
+ publisherLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ JLabel fromLabel = new JLabel("From: " + from);
+ fromLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ alwaysTrust = new JCheckBox(
+ "Always trust content from this publisher");
+ alwaysTrust.setEnabled(true);
+
+ JPanel infoPanel = new JPanel(new GridLayout(4,1));
+ infoPanel.add(nameLabel);
+ infoPanel.add(publisherLabel);
+
+ if (!(certVerifier instanceof HttpsCertVerifier))
+ infoPanel.add(fromLabel);
+
+ infoPanel.add(alwaysTrust);
+ infoPanel.setBorder(BorderFactory.createEmptyBorder(25,25,25,25));
+
+ //run and cancel buttons
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ JButton run = new JButton("Run");
+ JButton cancel = new JButton("Cancel");
+ int buttonWidth = Math.max(run.getMinimumSize().width,
+ cancel.getMinimumSize().width);
+ int buttonHeight = run.getMinimumSize().height;
+ Dimension d = new Dimension(buttonWidth, buttonHeight);
+ run.setPreferredSize(d);
+ cancel.setPreferredSize(d);
+ run.addActionListener(createSetValueListener(parent, 0));
+ run.addActionListener(new CheckBoxListener());
+ cancel.addActionListener(createSetValueListener(parent, 1));
+ initialFocusComponent = cancel;
+ buttonPanel.add(run);
+ buttonPanel.add(cancel);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //all of the above
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ add(topPanel);
+ add(infoPanel);
+ add(buttonPanel);
+
+ JLabel bottomLabel;
+ JButton moreInfo = new JButton("More information...");
+ moreInfo.addActionListener(new MoreInfoButtonListener());
+
+ if (parent.getJarSigner().getRootInCacerts())
+ bottomLabel = new JLabel(htmlWrap(R("STrustedSource")));
+ else
+ bottomLabel = new JLabel(htmlWrap(R("SUntrustedSource")));
+
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
+ bottomPanel.add(bottomLabel);
+ bottomPanel.add(moreInfo);
+ bottomPanel.setPreferredSize(new Dimension(500,100));
+ bottomPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ add(bottomPanel);
+
+ }
+
+ private class MoreInfoButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ SecurityWarningDialog.showMoreInfoDialog(parent.getJarSigner(),
+ parent);
+ }
+ }
+
+ /**
+ * Updates the user's KeyStore of trusted Certificates.
+ */
+ private class CheckBoxListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ if (alwaysTrust != null && alwaysTrust.isSelected()) {
+ try {
+ KeyTool kt = new KeyTool();
+ Certificate c = parent.getJarSigner().getPublisher();
+ kt.importCert(c);
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("certificate is now permanently trusted");
+ }
+ } catch (Exception ex) {
+ //TODO: Let NetX show a dialog here notifying user
+ //about being unable to add cert to keystore
+ }
+ }
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/CertsInfoPane.java b/netx/net/sourceforge/jnlp/security/CertsInfoPane.java
new file mode 100644
index 0000000..85afd1b
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/CertsInfoPane.java
@@ -0,0 +1,341 @@
+/* CertsInfoPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+import java.security.MessageDigest;
+import java.math.BigInteger;
+import javax.security.auth.x500.X500Principal;
+import sun.security.x509.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeSelectionModel;
+
+import net.sourceforge.jnlp.tools.*;
+
+/**
+ * Provides the panel for the Certificate Info dialog. This dialog displays data from
+ * X509Certificate(s) used in jar signing.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+public class CertsInfoPane extends SecurityDialogPanel {
+
+ private ArrayList<CertPath> certs;
+ private JList list;
+ protected JTree tree;
+ private JTable table;
+ private JTextArea output;
+ private ListSelectionModel listSelectionModel;
+ private ListSelectionModel tableSelectionModel;
+ protected String[] certNames;
+ private String[] columnNames = { "Field", "Value" };
+ protected ArrayList<String[][]> certsData;
+
+ public CertsInfoPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ addComponents();
+ }
+
+ /**
+ * Builds the JTree out of CertPaths.
+ */
+ void buildTree() {
+ certs = parent.getJarSigner().getCerts();
+ //for now, we're only going to display the first signer, even though
+ //jars can be signed by multiple people.
+ CertPath firstPath = certs.get(0);
+ X509Certificate firstCert =
+ ((X509Certificate)firstPath.getCertificates().get(0));
+ String subjectString =
+ SecurityUtil.getCN(firstCert.getSubjectX500Principal().getName());
+ String issuerString =
+ SecurityUtil.getCN(firstCert.getIssuerX500Principal().getName());
+
+ DefaultMutableTreeNode top =
+ new DefaultMutableTreeNode(subjectString
+ + " (" + issuerString + ")");
+
+ //not self signed
+ if (!firstCert.getSubjectDN().equals(firstCert.getIssuerDN())
+ && (firstPath.getCertificates().size() > 1)) {
+ X509Certificate secondCert =
+ ((X509Certificate)firstPath.getCertificates().get(1));
+ subjectString =
+ SecurityUtil.getCN(secondCert.getSubjectX500Principal().getName());
+ issuerString =
+ SecurityUtil.getCN(secondCert.getIssuerX500Principal().getName());
+ top.add(new DefaultMutableTreeNode(subjectString
+ + " (" + issuerString + ")"));
+ }
+
+ tree = new JTree(top);
+ tree.getSelectionModel().setSelectionMode
+ (TreeSelectionModel.SINGLE_TREE_SELECTION);
+ tree.addTreeSelectionListener(new TreeSelectionHandler());
+ }
+
+ /**
+ * Fills in certsNames, certsData with data from the certificates.
+ */
+ protected void populateTable() {
+ certNames = new String[certs.get(0).getCertificates().size()];
+ certsData = new ArrayList<String[][]>();
+
+ for (int i = 0; i < certs.get(0).getCertificates().size(); i++) {
+
+ X509Certificate c = (X509Certificate) certs.get(0).getCertificates().get(i);
+ certsData.add(parseCert(c));
+ certNames[i] = SecurityUtil.getCN(c.getSubjectX500Principal().getName())
+ + " (" + SecurityUtil.getCN(c.getIssuerX500Principal().getName()) + ")";
+ }
+ }
+
+ protected String[][] parseCert(X509Certificate c) {
+
+ String version = ""+c.getVersion();
+ String serialNumber = c.getSerialNumber().toString();
+ String signatureAlg = c.getSigAlgName();
+ String issuer = c.getIssuerX500Principal().toString();
+ String validity = new CertificateValidity(c.getNotBefore(),
+ c.getNotAfter()).toString();
+ String subject = c.getSubjectX500Principal().toString();
+
+ //convert our signature into a nice human-readable form.
+ HexDumpEncoder encoder = new HexDumpEncoder();
+ String signature = encoder.encodeBuffer(c.getSignature());
+
+ String md5Hash = "";
+ String sha1Hash = "";
+ try {
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ digest.update(c.getEncoded());
+ md5Hash = makeFingerprint(digest.digest());
+
+ digest = MessageDigest.getInstance("SHA-1");
+ digest.update(c.getEncoded());
+ sha1Hash = makeFingerprint(digest.digest());
+ } catch (Exception e) {
+ //fail quietly
+ }
+
+ String[][] cert = { {"Version", version},
+ {"Serial", serialNumber},
+ {"Signature Algorithm", signatureAlg},
+ {"Issuer", issuer},
+ {"Validity", validity},
+ {"Subject", subject},
+ {"Signature", signature},
+ {"MD5 Fingerprint", md5Hash},
+ {"SHA1 Fingerprint", sha1Hash}
+ };
+ return cert;
+ }
+
+ /**
+ * Constructs the GUI components of this panel
+ */
+ protected void addComponents() {
+ buildTree();
+ populateTable();
+ /**
+ //List of Certs
+ list = new JList(certNames);
+ list.setSelectedIndex(0); //assuming there's at least 1 cert
+ listSelectionModel = list.getSelectionModel();
+ listSelectionModel.addListSelectionListener(new ListSelectionHandler());
+ JScrollPane listPane = new JScrollPane(list);
+ */
+ JScrollPane listPane = new JScrollPane(tree);
+
+ //Table of field-value pairs
+ DefaultTableModel tableModel = new DefaultTableModel(certsData.get(0),
+ columnNames);
+ table = new JTable(tableModel);
+ table.getTableHeader().setReorderingAllowed(false);
+ tableSelectionModel = table.getSelectionModel();
+ tableSelectionModel.addListSelectionListener(new TableSelectionHandler());
+ table.setFillsViewportHeight(true);
+ JScrollPane tablePane = new JScrollPane(table);
+ tablePane.setPreferredSize(new Dimension(500,200));
+
+ //Text area to display the larger values
+ output = new JTextArea();
+ output.setEditable(false);
+ JScrollPane outputPane = new JScrollPane(output,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ outputPane.setPreferredSize(new Dimension(500,200));
+
+ //split pane of the field-value pairs and textbox
+ JSplitPane rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
+ tablePane, outputPane);
+ rightSplitPane.setDividerLocation(0.50);
+ rightSplitPane.setResizeWeight(0.50);
+
+ JSplitPane mainPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
+ listPane, rightSplitPane);
+ mainPane.setDividerLocation(0.30);
+ mainPane.setResizeWeight(0.30);
+
+ JPanel buttonPane = new JPanel(new BorderLayout());
+ JButton close = new JButton("Close");
+ JButton copyToClipboard = new JButton("Copy to Clipboard");
+ close.addActionListener(createSetValueListener(parent, 0));
+ copyToClipboard.addActionListener(new CopyToClipboardHandler());
+ buttonPane.add(close, BorderLayout.EAST);
+ buttonPane.add(copyToClipboard, BorderLayout.WEST);
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ add(mainPane, BorderLayout.CENTER);
+ add(buttonPane, BorderLayout.SOUTH);
+ }
+
+ /**
+ * Copies the currently selected certificate to the system Clipboard.
+ */
+ private class CopyToClipboardHandler implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ int certIndex = 0;
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode)
+ tree.getLastSelectedPathComponent();
+ if (node == null) return;
+ if (node.isRoot())
+ certIndex = 0;
+ else if (node.isLeaf())
+ certIndex = 1;
+
+ String[][] cert = certsData.get(certIndex);
+ int rows = cert.length;
+ int cols = cert[0].length;
+
+ String certString = "";
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ certString += cert[i][j];
+ certString += " ";
+ }
+ certString += "\n";
+ }
+
+ clipboard.setContents(new StringSelection(certString), null);
+ }
+ }
+
+ /**
+ * Updates the JTable when the JTree selection has changed.
+ */
+ protected class TreeSelectionHandler implements TreeSelectionListener {
+ public void valueChanged(TreeSelectionEvent e) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode)
+ tree.getLastSelectedPathComponent();
+
+ if (node == null) return;
+ if (node.isRoot()) {
+ table.setModel(new DefaultTableModel(certsData.get(0),
+ columnNames));
+ } else if (node.isLeaf()) {
+ table.setModel(new DefaultTableModel(certsData.get(1),
+ columnNames));
+ }
+ }
+ }
+
+ /**
+ * Updates the JTable when the selection on the list has changed.
+ */
+ private class ListSelectionHandler implements ListSelectionListener {
+ public void valueChanged(ListSelectionEvent e) {
+ ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+
+ int minIndex = lsm.getMinSelectionIndex();
+ int maxIndex = lsm.getMaxSelectionIndex();
+
+ for (int i = minIndex; i <= maxIndex; i++) {
+ if (lsm.isSelectedIndex(i)) {
+ table.setModel(new DefaultTableModel(certsData.get(i),
+ columnNames));
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the JTextArea output when the selection on the JTable
+ * has changed.
+ */
+ private class TableSelectionHandler implements ListSelectionListener {
+ public void valueChanged(ListSelectionEvent e) {
+ ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+
+ int minIndex = lsm.getMinSelectionIndex();
+ int maxIndex = lsm.getMaxSelectionIndex();
+
+ for (int i = minIndex; i <= maxIndex; i++) {
+ if (lsm.isSelectedIndex(i)) {
+ output.setText((String) table.getValueAt(i,1));
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes a human readable hash fingerprint.
+ * For example: 11:22:33:44:AA:BB:CC:DD:EE:FF.
+ */
+ private String makeFingerprint(byte[] hash) {
+ String fingerprint = "";
+ for (int i = 0; i < hash.length; i++) {
+ if (!fingerprint.equals(""))
+ fingerprint += ":";
+ fingerprint += Integer.toHexString(
+ ((hash[i] & 0xFF)|0x100)).substring(1,3);
+ }
+ return fingerprint.toUpperCase();
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java b/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java
new file mode 100644
index 0000000..ca2fc08
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java
@@ -0,0 +1,238 @@
+/* HttpsCertVerifier.java
+ Copyright (C) 2009 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.io.IOException;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.tools.KeyTool;
+import sun.security.util.DerValue;
+import sun.security.util.HostnameChecker;
+import sun.security.x509.X500Name;
+
+public class HttpsCertVerifier implements CertVerifier {
+
+ private VariableX509TrustManager tm;
+ private X509Certificate[] chain;
+ private String authType;
+ private String hostName;
+ private boolean isTrusted;
+ private boolean hostMatched;
+ private ArrayList<String> details = new ArrayList<String>();
+
+ public HttpsCertVerifier(VariableX509TrustManager tm,
+ X509Certificate[] chain, String authType,
+ boolean isTrusted, boolean hostMatched,
+ String hostName) {
+ this.tm = tm;
+ this.chain = chain;
+ this.authType = authType;
+ this.hostName = hostName;
+ this.isTrusted = isTrusted;
+ this.hostMatched = hostMatched;
+ }
+
+ public boolean getAlreadyTrustPublisher() {
+ return isTrusted;
+ }
+
+ public ArrayList<CertPath> getCerts() {
+
+ ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
+ for (int i=0; i < chain.length; i++)
+ list.add(chain[i]);
+
+ ArrayList<CertPath> certPaths = new ArrayList<CertPath>();
+
+ try {
+ certPaths.add(CertificateFactory.getInstance("X.509").generateCertPath(list));
+ } catch (CertificateException ce) {
+ ce.printStackTrace();
+
+ // carry on
+ }
+
+ return certPaths;
+ }
+
+ public ArrayList<String> getDetails() {
+
+ boolean hasExpiredCert=false;
+ boolean hasExpiringCert=false;
+ boolean notYetValidCert=false;
+ boolean isUntrusted=false;
+ boolean CNMisMatch = !hostMatched;
+
+ if (! getAlreadyTrustPublisher())
+ isUntrusted = true;
+
+ for (int i=0; i < chain.length; i++)
+ {
+ X509Certificate cert = chain[i];
+
+ long now = System.currentTimeMillis();
+ long SIX_MONTHS = 180*24*60*60*1000L;
+ long notAfter = cert.getNotAfter().getTime();
+ if (notAfter < now) {
+ hasExpiredCert = true;
+ } else if (notAfter < now + SIX_MONTHS) {
+ hasExpiringCert = true;
+ }
+
+ try {
+ cert.checkValidity();
+ } catch (CertificateNotYetValidException cnyve) {
+ notYetValidCert = true;
+ } catch (CertificateExpiredException cee) {
+ hasExpiredCert = true;
+ }
+ }
+
+ String altNames = getNamesForCert(chain[0]);
+
+ if (isUntrusted || hasExpiredCert || hasExpiringCert || notYetValidCert || CNMisMatch) {
+ if (isUntrusted)
+ addToDetails(R("SUntrustedCertificate"));
+ if (hasExpiredCert)
+ addToDetails(R("SHasExpiredCert"));
+ if (hasExpiringCert)
+ addToDetails(R("SHasExpiringCert"));
+ if (notYetValidCert)
+ addToDetails(R("SNotYetValidCert"));
+ if (CNMisMatch)
+ addToDetails(R("SCNMisMatch", altNames, this.hostName));
+ }
+
+
+ return details;
+ }
+
+ private String getNamesForCert(X509Certificate c) {
+
+ String names = "";
+
+
+ // We use the specification from
+ // http://java.sun.com/j2se/1.5.0/docs/api/java/security/cert/X509Certificate.html#getSubjectAlternativeNames()
+ // to determine the type of address
+ int ALTNAME_DNS = 2;
+ int ALTNAME_IP = 7;
+
+ try {
+ Collection<List<?>> subjAltNames = c.getSubjectAlternativeNames();
+ X500Name subjectName = HostnameChecker.getSubjectX500Name(c);
+ DerValue derValue = subjectName.findMostSpecificAttribute
+ (X500Name.commonName_oid);
+ names += derValue.getAsString();
+
+ if (subjAltNames != null) {
+ for (List<?> next : subjAltNames) {
+ if ( ((Integer)next.get(0)).intValue() == ALTNAME_IP ||
+ ((Integer)next.get(0)).intValue() == ALTNAME_DNS
+ ) {
+ names += ", " + (String)next.get(1);
+ }
+ }
+ }
+
+ if (subjAltNames != null)
+ names = names.substring(2); // remove proceeding ", "
+
+ } catch (CertificateParsingException cpe) {
+ cpe.printStackTrace();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+
+ return names;
+ }
+
+ private void addToDetails(String detail) {
+ if (!details.contains(detail))
+ details.add(detail);
+ }
+
+ private static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ private static String R(String key, String arg1, String arg2) {
+ return JNLPRuntime.getMessage(key, new Object[] { arg1, arg2 });
+ }
+
+ public Certificate getPublisher() {
+ if (chain.length > 0)
+ return (Certificate)chain[0];
+ return null;
+ }
+
+ public Certificate getRoot() {
+ if (chain.length > 0)
+ return (Certificate)chain[chain.length - 1];
+ return null;
+ }
+
+ public boolean getRootInCacerts() {
+ try {
+ KeyTool kt = new KeyTool();
+ return kt.checkCacertsForCertificate(getRoot());
+ } catch (Exception e) {
+ }
+ return false;
+ }
+
+ public boolean hasSigningIssues() {
+ return false;
+ }
+
+ public boolean noSigningIssues() {
+ return false;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/MoreInfoPane.java b/netx/net/sourceforge/jnlp/security/MoreInfoPane.java
new file mode 100644
index 0000000..729eb1c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/MoreInfoPane.java
@@ -0,0 +1,110 @@
+/* MoreInfoPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+/**
+ * Provides the panel for the More Info dialog. This dialog shows details about an
+ * application's signing status.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+public class MoreInfoPane extends SecurityDialogPanel {
+
+ public MoreInfoPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ addComponents();
+ }
+
+ /**
+ * Constructs the GUI components of this panel
+ */
+ private void addComponents() {
+ ArrayList<String> details = certVerifier.getDetails();
+
+ int numLabels = details.size();
+ JPanel errorPanel = new JPanel(new GridLayout(numLabels,1));
+ errorPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ errorPanel.setPreferredSize(new Dimension(400, 70*(numLabels)));
+
+ for (int i = 0; i < numLabels; i++) {
+ ImageIcon icon = null;
+ if (details.get(i).equals(R("STrustedCertificate")))
+ icon = new ImageIcon((new sun.misc.Launcher())
+ .getClassLoader().getResource("net/sourceforge/jnlp/resources/info-small.png"));
+ else
+ icon = new ImageIcon((new sun.misc.Launcher())
+ .getClassLoader().getResource("net/sourceforge/jnlp/resources/warning-small.png"));
+
+ errorPanel.add(new JLabel(htmlWrap(details.get(i)), icon, SwingConstants.LEFT));
+ }
+
+ JPanel buttonsPanel = new JPanel(new BorderLayout());
+ JButton certDetails = new JButton("Certificate Details");
+ certDetails.addActionListener(new CertInfoButtonListener());
+ JButton close = new JButton("Close");
+ close.addActionListener(createSetValueListener(parent, 0));
+ buttonsPanel.add(certDetails, BorderLayout.WEST);
+ buttonsPanel.add(close, BorderLayout.EAST);
+ buttonsPanel.setBorder(BorderFactory.createEmptyBorder(15,15,15,15));
+
+ add(errorPanel, BorderLayout.NORTH);
+ add(buttonsPanel, BorderLayout.SOUTH);
+
+ }
+
+ private class CertInfoButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ SecurityWarningDialog.showCertInfoDialog(parent.getJarSigner(),
+ parent);
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/NotAllSignedWarningPane.java b/netx/net/sourceforge/jnlp/security/NotAllSignedWarningPane.java
new file mode 100644
index 0000000..7210a89
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/NotAllSignedWarningPane.java
@@ -0,0 +1,120 @@
+/* NotAllSignedWarningPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import net.sourceforge.jnlp.JNLPFile;
+
+
+public class NotAllSignedWarningPane extends SecurityDialogPanel {
+
+ public NotAllSignedWarningPane(SecurityWarningDialog x) {
+ super(x);
+ addComponents();
+ }
+
+ /**
+ * Creates the actual GUI components, and adds it to this panel
+ */
+ private void addComponents() {
+ JNLPFile file = parent.getFile();
+
+ String topLabelText = "Only parts of this application code are signed.";
+ String infoLabelText = "This application contains both signed and" +
+ " unsigned code. While signed code is safe if you trust the " +
+ "provider, unsigned code may imply code outside of the trusted " +
+ "provider's control.";
+ String questionLabelText = "Do you wish to proceed and run this " +
+ "application anyway?";
+
+ ImageIcon icon = new ImageIcon((new sun.misc.Launcher()).getClassLoader().getResource("net/sourceforge/jnlp/resources/warning.png"));
+ JLabel topLabel = new JLabel(htmlWrap(topLabelText), icon, SwingConstants.LEFT);
+ topLabel.setFont(new Font(topLabel.getFont().toString(),
+ Font.BOLD, 12));
+ JPanel topPanel = new JPanel(new BorderLayout());
+ topPanel.setBackground(Color.WHITE);
+ topPanel.add(topLabel, BorderLayout.CENTER);
+ topPanel.setPreferredSize(new Dimension(500,80));
+ topPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ JLabel infoLabel = new JLabel(htmlWrap(infoLabelText));
+ JPanel infoPanel = new JPanel(new BorderLayout());
+ infoPanel.add(infoLabel, BorderLayout.CENTER);
+ infoPanel.setPreferredSize(new Dimension(500,100));
+ infoPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ JLabel questionLabel = new JLabel(htmlWrap(questionLabelText));
+ JPanel questionPanel = new JPanel(new BorderLayout());
+ questionPanel.add(questionLabel, BorderLayout.CENTER);
+ questionPanel.setPreferredSize(new Dimension(500,100));
+ questionPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //run and cancel buttons
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ JButton run = new JButton("Proceed");
+ JButton cancel = new JButton("Cancel");
+ run.addActionListener(createSetValueListener(parent,0));
+ cancel.addActionListener(createSetValueListener(parent, 1));
+ initialFocusComponent = cancel;
+ buttonPanel.add(run);
+ buttonPanel.add(cancel);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ //all of the above
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ add(topPanel);
+ add(infoPanel);
+ add(questionPanel);
+ add(buttonPanel);
+
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/SecurityDialogPanel.java b/netx/net/sourceforge/jnlp/security/SecurityDialogPanel.java
new file mode 100644
index 0000000..f3b8ee8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/SecurityDialogPanel.java
@@ -0,0 +1,134 @@
+ /* SecurityDialogPanel.java
+ Copyright (C) 2008-2010 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * Provides a JPanel for use in JNLP warning dialogs.
+ */
+public abstract class SecurityDialogPanel extends JPanel {
+
+ protected SecurityWarningDialog parent;
+
+ JComponent initialFocusComponent = null;
+
+ CertVerifier certVerifier = null;
+
+ public SecurityDialogPanel(SecurityWarningDialog dialog, CertVerifier certVerifier){
+ this.parent = dialog;
+ this.certVerifier = certVerifier;
+ this.setLayout(new BorderLayout());
+ }
+
+ public SecurityDialogPanel(SecurityWarningDialog dialog) {
+ this.parent = dialog;
+ this.setLayout(new BorderLayout());
+ }
+
+ /*
+ * String translation functions
+ */
+
+ protected static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ protected static String R(String key, Object param) {
+ return JNLPRuntime.getMessage(key, new Object[] {param});
+ }
+
+ /**
+ * Needed to get word wrap working in JLabels.
+ */
+ protected String htmlWrap (String s) {
+ return "<html>"+s+"</html>";
+ }
+
+ /**
+ * Create an ActionListener suitable for use with buttons. When this {@link ActionListener}
+ * is invoked, it will set the value of the {@link SecurityWarningDialog} and then dispossed.
+ *
+ * @param buttonIndex the index of the button. By convention 0 = Yes. 1 = No, 2 = Cancel
+ * @return
+ */
+ protected ActionListener createSetValueListener(SecurityWarningDialog dialog, int buttonIndex) {
+ return new SetValueHandler(dialog, buttonIndex);
+ }
+
+ @Override
+ public void setVisible(boolean aFlag) {
+ super.setVisible(aFlag);
+ requestFocusOnDefaultButton();
+ }
+
+ public void requestFocusOnDefaultButton() {
+ if (initialFocusComponent != null) {
+ initialFocusComponent.requestFocusInWindow();
+ }
+ }
+
+ /**
+ * Creates a handler that sets a dialog's value and then disposes it when activated
+ *
+ */
+ private class SetValueHandler implements ActionListener {
+
+ Integer buttonIndex;
+ SecurityWarningDialog dialog;
+
+ public SetValueHandler(SecurityWarningDialog dialog, int buttonIndex) {
+ this.dialog = dialog;
+ this.buttonIndex = buttonIndex;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ dialog.setValue(buttonIndex);
+ dialog.dispose();
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/SecurityUtil.java b/netx/net/sourceforge/jnlp/security/SecurityUtil.java
new file mode 100644
index 0000000..2a63a21
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/SecurityUtil.java
@@ -0,0 +1,285 @@
+/* SecurityUtil.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+public class SecurityUtil {
+
+ private static final char[] password = "changeit".toCharArray();
+
+ public static String getTrustedCertsFilename() throws Exception{
+
+ String homeDir = JNLPRuntime.HOME_DIR;
+
+ if (homeDir == null) {
+ throw new Exception("Could not access home directory");
+ } else {
+ return JNLPRuntime.CERTIFICATES_FILE;
+ }
+ }
+
+ public static char[] getTrustedCertsPassword() {
+ return password;
+ }
+
+ /**
+ * Extracts the CN field from a Certificate principal string. Or, if it
+ * can't find that, return the principal unmodified.
+ *
+ * This is a simple (and hence 'wrong') version. See
+ * http://www.ietf.org/rfc/rfc2253.txt for all the gory details.
+ */
+ public static String getCN(String principal) {
+
+ /*
+ * FIXME Incomplete
+ *
+ * This does not implement RFC 2253 completely
+ *
+ * Issues:
+ * - rfc2253 talks about utf8, java uses utf16.
+ * - theoretically, java should have dealt with all byte encodings
+ * so we shouldnt even see cases like \FF
+ * - if the above is wrong, then we need to deal with cases like
+ * \FF\FF
+ */
+
+ int start = principal.indexOf("CN=");
+ if (start == -1) {
+ return principal;
+ }
+
+ StringBuilder commonName = new StringBuilder();
+
+ boolean inQuotes = false;
+ boolean escaped = false;
+
+ /*
+ * bit 0 = high order bit. bit 1 = low order bit
+ */
+ char[] hexBits = null;
+
+ for (int i = start + 3; i < principal.length(); i++) {
+ char ch = principal.charAt(i);
+ switch (ch) {
+ case '"':
+ if (escaped) {
+ commonName.append(ch);
+ escaped = false;
+ } else {
+ inQuotes = !inQuotes;
+ }
+ break;
+
+ case '\\':
+ if (escaped) {
+ commonName.append(ch);
+ escaped = false;
+ } else {
+ escaped = true;
+ }
+ break;
+
+ case ',':
+ /* fall through */
+ case ';':
+ /* fall through */
+ case '+':
+ if (escaped || inQuotes) {
+ commonName.append(ch);
+ if (escaped) {
+ escaped = false;
+ }
+ } else {
+ return commonName.toString();
+ }
+ break;
+
+ default:
+ if (escaped && isHexDigit(ch)) {
+ hexBits = new char[2];
+ hexBits[0] = ch;
+ } else if (hexBits != null) {
+ if (!isHexDigit(ch)) {
+ /* error parsing */
+ return "";
+ }
+ hexBits[1] = ch;
+ commonName.append((char) Integer.parseInt(new String(hexBits), 16));
+ hexBits = null;
+ } else {
+ commonName.append(ch);
+ }
+ escaped = false;
+ }
+ }
+
+ return commonName.toString();
+
+ }
+
+ private static boolean isHexDigit(char ch) {
+ return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'));
+ }
+
+ /**
+ * Checks the user's home directory to see if the trusted.certs file exists.
+ * If it does not exist, it tries to create an empty keystore.
+ * @return true if the trusted.certs file exists or a new trusted.certs
+ * was created successfully, otherwise false.
+ */
+ public static boolean checkTrustedCertsFile() throws Exception {
+
+ File certFile = new File(getTrustedCertsFilename());
+
+ //file does not exist
+ if (!certFile.isFile()) {
+ File dir = certFile.getAbsoluteFile().getParentFile();
+ boolean madeDir = false;
+ if (!dir.isDirectory()) {
+ madeDir = dir.mkdirs();
+ }
+
+ //made directory, or directory exists
+ if (madeDir || dir.isDirectory()) {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, password);
+ FileOutputStream fos = new FileOutputStream(certFile);
+ ks.store(fos, password);
+ fos.close();
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Returns the keystore associated with the user's trusted.certs file,
+ * or null otherwise.
+ */
+ public static KeyStore getUserKeyStore() throws Exception {
+
+ KeyStore ks = null;
+ FileInputStream fis = null;
+
+ if (checkTrustedCertsFile()) {
+
+ try {
+ File file = new File(getTrustedCertsFilename());
+ if (file.exists()) {
+ fis = new FileInputStream(file);
+ ks = KeyStore.getInstance("JKS");
+ ks.load(fis, password);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ } finally {
+ if (fis != null)
+ fis.close();
+ }
+ }
+ return ks;
+ }
+
+ /**
+ * Returns the keystore associated with the JDK cacerts file,
+ * or null otherwise.
+ */
+ public static KeyStore getCacertsKeyStore() throws Exception {
+
+ KeyStore caks = null;
+ FileInputStream fis = null;
+
+ try {
+ File file = new File(System.getProperty("java.home")
+ + "/lib/security/cacerts");
+ if (file.exists()) {
+ fis = new FileInputStream(file);
+ caks = KeyStore.getInstance("JKS");
+ caks.load(fis, null);
+ }
+ } catch (Exception e) {
+ caks = null;
+ } finally {
+ if (fis != null)
+ fis.close();
+ }
+
+ return caks;
+ }
+
+ /**
+ * Returns the keystore associated with the system certs file,
+ * or null otherwise.
+ */
+ public static KeyStore getSystemCertStore() throws Exception {
+
+ KeyStore caks = null;
+ FileInputStream fis = null;
+
+ try {
+ File file = new File(System.getProperty("javax.net.ssl.trustStore"));
+ String type = System.getProperty("javax.net.ssl.trustStoreType");
+ //String provider = "SUN";
+ char[] password = System.getProperty(
+ "javax.net.ssl.trustStorePassword").toCharArray();
+ if (file.exists()) {
+ fis = new FileInputStream(file);
+ caks = KeyStore.getInstance(type);
+ caks.load(fis, password);
+ }
+ } catch (Exception e) {
+ caks = null;
+ } finally {
+ if (fis != null)
+ fis.close();
+ }
+
+ return caks;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java b/netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java
new file mode 100644
index 0000000..14f5fe0
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java
@@ -0,0 +1,474 @@
+/* SecurityWarningDialog.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import java.awt.event.*;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Provides methods for showing security warning dialogs
+ * for a wide range of JNLP security issues.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+public class SecurityWarningDialog extends JDialog {
+
+ /** Types of dialogs we can create */
+ public static enum DialogType {
+ CERT_WARNING,
+ MORE_INFO,
+ CERT_INFO,
+ SINGLE_CERT_INFO,
+ ACCESS_WARNING,
+ NOTALLSIGNED_WARNING,
+ APPLET_WARNING
+ }
+
+ /** The types of access which may need user permission. */
+ public static enum AccessType {
+ READ_FILE,
+ WRITE_FILE,
+ CREATE_DESTKOP_SHORTCUT,
+ CLIPBOARD_READ,
+ CLIPBOARD_WRITE,
+ PRINTER,
+ NETWORK,
+ VERIFIED,
+ UNVERIFIED,
+ NOTALLSIGNED,
+ SIGNING_ERROR
+ }
+
+ /** The type of dialog we want to show */
+ private DialogType dialogType;
+
+ /** The type of access that this dialog is for */
+ private AccessType accessType;
+
+ private SecurityDialogPanel panel;
+
+ /** The application file associated with this security warning */
+ private JNLPFile file;
+
+ private CertVerifier certVerifier;
+
+ private X509Certificate cert;
+
+ /** An optional String array that's only necessary when a dialog
+ * label requires some parameters (e.g. showing which address an application
+ * is trying to connect to).
+ */
+ private Object[] extras;
+
+ /** Whether or not this object has been fully initialized */
+ private boolean initialized = false;
+
+ /**
+ * the return value of this dialog. result: 0 = Yes, 1 = No, 2 = Cancel,
+ * null = Window closed.
+ */
+ private Object value;
+
+ public SecurityWarningDialog(DialogType dialogType, AccessType accessType,
+ JNLPFile file) {
+ super();
+ this.dialogType = dialogType;
+ this.accessType = accessType;
+ this.file = file;
+ this.certVerifier = null;
+ initialized = true;
+ initDialog();
+ }
+
+ public SecurityWarningDialog(DialogType dialogType, AccessType accessType,
+ JNLPFile file, CertVerifier jarSigner) {
+ super();
+ this.dialogType = dialogType;
+ this.accessType = accessType;
+ this.file = file;
+ this.certVerifier = jarSigner;
+ initialized = true;
+ initDialog();
+ }
+
+ public SecurityWarningDialog(DialogType dialogType, AccessType accessType,
+ CertVerifier certVerifier) {
+ super();
+ this.dialogType = dialogType;
+ this.accessType = accessType;
+ this.file = null;
+ this.certVerifier = certVerifier;
+ initialized = true;
+ initDialog();
+ }
+
+ public SecurityWarningDialog(DialogType dialogType, AccessType accessType,
+ JNLPFile file, Object[] extras) {
+ super();
+ this.dialogType = dialogType;
+ this.accessType = accessType;
+ this.file = file;
+ this.certVerifier = null;
+ initialized = true;
+ this.extras = extras;
+ initDialog();
+ }
+
+ //for displaying a single certificate
+ public SecurityWarningDialog(DialogType dialogType, X509Certificate c) {
+ super();
+ this.dialogType = dialogType;
+ this.accessType = null;
+ this.file = null;
+ this.certVerifier = null;
+ this.cert = c;
+ initialized = true;
+ initDialog();
+ }
+
+ /**
+ * Returns if this dialog has been fully initialized yet.
+ * @return true if this dialog has been initialized, and false otherwise.
+ */
+ public boolean isInitialized(){
+ return initialized;
+ }
+
+ /**
+ * Shows a warning dialog for different types of system access (i.e. file
+ * open/save, clipboard read/write, printing, etc).
+ *
+ * @param accessType the type of system access requested.
+ * @param file the jnlp file associated with the requesting application.
+ * @return true if permission was granted by the user, false otherwise.
+ */
+ public static boolean showAccessWarningDialog(AccessType accessType,
+ JNLPFile file) {
+ return showAccessWarningDialog(accessType, file, null);
+ }
+
+ /**
+ * Shows a warning dialog for different types of system access (i.e. file
+ * open/save, clipboard read/write, printing, etc).
+ *
+ * @param accessType the type of system access requested.
+ * @param file the jnlp file associated with the requesting application.
+ * @param extras an optional array of Strings (typically) that gets
+ * passed to the dialog labels.
+ * @return true if permission was granted by the user, false otherwise.
+ */
+ public static boolean showAccessWarningDialog(AccessType accessType,
+ JNLPFile file, Object[] extras) {
+ SecurityWarningDialog dialog = new SecurityWarningDialog(
+ DialogType.ACCESS_WARNING, accessType, file, extras);
+ dialog.setVisible(true);
+ dialog.dispose();
+
+ Object selectedValue = dialog.getValue();
+ if (selectedValue == null) {
+ return false;
+ } else if (selectedValue instanceof Integer) {
+ if (((Integer)selectedValue).intValue() == 0)
+ return true;
+ else
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Shows a warning dialog for when the main application jars are signed,
+ * but extensions aren't
+ *
+ * @return true if permission was granted by the user, false otherwise.
+ */
+ public static boolean showNotAllSignedWarningDialog(JNLPFile file) {
+ SecurityWarningDialog dialog = new SecurityWarningDialog(
+ DialogType.NOTALLSIGNED_WARNING, AccessType.NOTALLSIGNED, file, (new Object[0]));
+ dialog.setVisible(true);
+ dialog.dispose();
+
+ Object selectedValue = dialog.getValue();
+ if (selectedValue == null) {
+ return false;
+ } else if (selectedValue instanceof Integer) {
+ if (((Integer)selectedValue).intValue() == 0)
+ return true;
+ else
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Shows a security warning dialog according to the specified type of
+ * access. If <code>type</code> is one of AccessType.VERIFIED or
+ * AccessType.UNVERIFIED, extra details will be available with regards
+ * to code signing and signing certificates.
+ *
+ * @param accessType the type of warning dialog to show
+ * @param file the JNLPFile associated with this warning
+ * @param jarSigner the JarSigner used to verify this application
+ */
+ public static boolean showCertWarningDialog(AccessType accessType,
+ JNLPFile file, CertVerifier jarSigner) {
+ SecurityWarningDialog dialog =
+ new SecurityWarningDialog(DialogType.CERT_WARNING, accessType, file,
+ jarSigner);
+ dialog.setVisible(true);
+ dialog.dispose();
+
+ Object selectedValue = dialog.getValue();
+ if (selectedValue == null) {
+ return false;
+ } else if (selectedValue instanceof Integer) {
+ if (((Integer)selectedValue).intValue() == 0)
+ return true;
+ else
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Shows more information regarding jar code signing
+ *
+ * @param jarSigner the JarSigner used to verify this application
+ * @param parent the parent option pane
+ */
+ public static void showMoreInfoDialog(
+ CertVerifier jarSigner, SecurityWarningDialog parent) {
+
+ SecurityWarningDialog dialog =
+ new SecurityWarningDialog(DialogType.MORE_INFO, null, null,
+ jarSigner);
+ dialog.setVisible(true);
+ dialog.dispose();
+ }
+
+ /**
+ * Displays CertPath information in a readable table format.
+ *
+ * @param certs the certificates used in signing.
+ */
+ public static void showCertInfoDialog(CertVerifier jarSigner,
+ SecurityWarningDialog parent) {
+ SecurityWarningDialog dialog = new SecurityWarningDialog(DialogType.CERT_INFO,
+ null, null, jarSigner);
+ dialog.setLocationRelativeTo(parent);
+ dialog.setVisible(true);
+ dialog.dispose();
+ }
+
+ /**
+ * Displays a single certificate's information.
+ *
+ * @param c
+ * @param optionPane
+ */
+ public static void showSingleCertInfoDialog(X509Certificate c,
+ JDialog parent) {
+ SecurityWarningDialog dialog = new SecurityWarningDialog(DialogType.SINGLE_CERT_INFO, c);
+ dialog.setLocationRelativeTo(parent);
+ dialog.setVisible(true);
+ dialog.dispose();
+ }
+
+ public static int showAppletWarning() {
+ SecurityWarningDialog dialog = new SecurityWarningDialog(DialogType.APPLET_WARNING,
+ null, null, (CertVerifier) null);
+ dialog.setVisible(true);
+ dialog.dispose();
+
+ Object selectedValue = dialog.getValue();
+
+ //result 0 = Yes, 1 = No, 2 = Cancel
+ if (selectedValue == null) {
+ return 2;
+ } else if (selectedValue instanceof Integer) {
+ return ((Integer)selectedValue).intValue();
+ } else {
+ return 2;
+ }
+ }
+
+ private void initDialog() {
+ setSystemLookAndFeel();
+
+ String dialogTitle = "";
+ if (dialogType == DialogType.CERT_WARNING)
+ dialogTitle = "Warning - Security";
+ else if (dialogType == DialogType.MORE_INFO)
+ dialogTitle = "More Information";
+ else if (dialogType == DialogType.CERT_INFO)
+ dialogTitle = "Details - Certificate";
+ else if (dialogType == DialogType.ACCESS_WARNING)
+ dialogTitle = "Security Warning";
+ else if (dialogType == DialogType.APPLET_WARNING)
+ dialogTitle = "Applet Warning";
+ else if (dialogType == DialogType.NOTALLSIGNED_WARNING)
+ dialogTitle = "Security Warning";
+
+ setTitle(dialogTitle);
+ setModal(true);
+
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+ installPanel();
+
+ pack();
+
+ WindowAdapter adapter = new WindowAdapter() {
+ private boolean gotFocus = false;
+ @Override
+ public void windowClosing(WindowEvent we) {
+ setValue(null);
+ }
+ @Override
+ public void windowGainedFocus(WindowEvent we) {
+ // Once window gets focus, set initial focus
+ if (!gotFocus) {
+ selectDefaultButton();
+ gotFocus = true;
+ }
+ }
+ @Override
+ public void windowOpened(WindowEvent e) {
+ if (e.getSource() instanceof SecurityWarningDialog) {
+ SecurityWarningDialog dialog = (SecurityWarningDialog) e.getSource();
+ dialog.setResizable(true);
+ centerDialog(dialog);
+ dialog.setValue(null);
+ }
+ }
+ };
+ addWindowListener(adapter);
+ addWindowFocusListener(adapter);
+
+ }
+
+ public AccessType getAccessType() {
+ return accessType;
+ }
+
+ public JNLPFile getFile() {
+ return file;
+ }
+
+ public CertVerifier getJarSigner() {
+ return certVerifier;
+ }
+
+ public X509Certificate getCert() {
+ return cert;
+ }
+
+ /**
+ * Adds the appropriate JPanel to this Dialog, based on {@link DialogType}.
+ */
+ private void installPanel() {
+
+ if (dialogType == DialogType.CERT_WARNING)
+ panel = new CertWarningPane(this, this.certVerifier);
+ else if (dialogType == DialogType.MORE_INFO)
+ panel = new MoreInfoPane(this, this.certVerifier);
+ else if (dialogType == DialogType.CERT_INFO)
+ panel = new CertsInfoPane(this, this.certVerifier);
+ else if (dialogType == DialogType.SINGLE_CERT_INFO)
+ panel = new SingleCertInfoPane(this, this.certVerifier);
+ else if (dialogType == DialogType.ACCESS_WARNING)
+ panel = new AccessWarningPane(this, extras, this.certVerifier);
+ else if (dialogType == DialogType.APPLET_WARNING)
+ panel = new AppletWarningPane(this, this.certVerifier);
+ else if (dialogType == DialogType.NOTALLSIGNED_WARNING)
+ panel = new NotAllSignedWarningPane(this);
+
+ add(panel, BorderLayout.CENTER);
+ }
+
+ private static void centerDialog(JDialog dialog) {
+ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension dialogSize = dialog.getSize();
+
+ dialog.setLocation((screen.width - dialogSize.width)/2,
+ (screen.height - dialogSize.height)/2);
+ }
+
+ private void selectDefaultButton() {
+ if (panel == null) {
+ System.out.println("initial value panel is null");
+ }
+ panel.requestFocusOnDefaultButton();
+ }
+
+ protected void setValue(Object value) {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Setting value:" + value);
+ }
+ this.value = value;
+ }
+
+ protected Object getValue() {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Returning value:" + value);
+ }
+ return value;
+ }
+
+ /**
+ * Updates the look and feel of the window to be the system look and feel
+ */
+ protected void setSystemLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ //don't worry if we can't.
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/SingleCertInfoPane.java b/netx/net/sourceforge/jnlp/security/SingleCertInfoPane.java
new file mode 100644
index 0000000..59b8ab8
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/SingleCertInfoPane.java
@@ -0,0 +1,77 @@
+/* SingleCertInfoPane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeSelectionModel;
+
+
+public class SingleCertInfoPane extends CertsInfoPane {
+
+ public SingleCertInfoPane(SecurityWarningDialog x, CertVerifier certVerifier) {
+ super(x, certVerifier);
+ }
+
+ protected void buildTree() {
+ X509Certificate cert = parent.getCert();
+ String subjectString =
+ SecurityUtil.getCN(cert.getSubjectX500Principal().getName());
+ String issuerString =
+ SecurityUtil.getCN(cert.getIssuerX500Principal().getName());
+
+ DefaultMutableTreeNode top = new DefaultMutableTreeNode(subjectString
+ + " (" + issuerString + ")");
+
+ tree = new JTree(top);
+ tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ tree.addTreeSelectionListener(new TreeSelectionHandler());
+ }
+
+ protected void populateTable() {
+ X509Certificate c = parent.getCert();
+ certNames = new String[1];
+ certsData = new ArrayList<String[][]>();
+ certsData.add(parseCert(c));
+ certNames[0] = SecurityUtil.getCN(c.getSubjectX500Principal().getName())
+ + " (" + SecurityUtil.getCN(c.getIssuerX500Principal().getName()) + ")";
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java b/netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java
new file mode 100644
index 0000000..d5ad6da
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java
@@ -0,0 +1,314 @@
+/* VariableX509TrustManager.java
+ Copyright (C) 2009 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security;
+
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import sun.security.util.HostnameChecker;
+import sun.security.validator.ValidatorException;
+
+import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
+
+/**
+ * This class implements an X509 Trust Manager. The certificates it trusts are
+ * "variable", in the sense that it can dynamically, and temporarily support
+ * different certificates that are not in the keystore.
+ */
+
+public class VariableX509TrustManager extends X509ExtendedTrustManager {
+
+ KeyStore userKeyStore = null;
+ KeyStore caKeyStore = null;
+
+ X509TrustManager userTrustManager = null;
+ X509TrustManager caTrustManager = null;
+
+ ArrayList<Certificate> temporarilyTrusted = new ArrayList<Certificate>();
+ ArrayList<Certificate> temporarilyUntrusted = new ArrayList<Certificate>();
+
+ static VariableX509TrustManager instance = null;
+
+ /**
+ * Constructor initializes the system, user and custom stores
+ */
+ public VariableX509TrustManager() {
+
+ try {
+ userKeyStore = SecurityUtil.getUserKeyStore();
+ TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
+ tmFactory.init(userKeyStore);
+
+ // tm factory initialized, now get the managers so we can assign the X509 one
+ TrustManager[] trustManagers = tmFactory.getTrustManagers();
+
+ for (int i=0; i < trustManagers.length; i++) {
+ if (trustManagers[i] instanceof X509TrustManager) {
+ userTrustManager = (X509TrustManager) trustManagers[i];
+ }
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ try {
+ caKeyStore = SecurityUtil.getCacertsKeyStore();
+ TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
+ tmFactory.init(caKeyStore);
+
+ // tm factory initialized, now get the managers so we can extract the X509 one
+ TrustManager[] trustManagers = tmFactory.getTrustManagers();
+
+ for (int i=0; i < trustManagers.length; i++) {
+ if (trustManagers[i] instanceof X509TrustManager) {
+ caTrustManager = (X509TrustManager) trustManagers[i];
+ }
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Check if client is trusted (no support for custom here, only system/user)
+ */
+ public void checkClientTrusted(X509Certificate[] chain, String authType,
+ String hostName, String algorithm)
+ throws CertificateException {
+ // First try catrustmanager, then try usertrustmanager
+ try {
+ caTrustManager.checkClientTrusted(chain, authType);
+ } catch (Exception caex) {
+ try {
+ userTrustManager.checkClientTrusted(chain, authType);
+ } catch (Exception userex) {
+ // Do nothing here. This trust manager is intended to be used
+ // only in the plugin instance vm, which does not act as a
+ // server
+ }
+ }
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ checkClientTrusted(chain, authType, null, null);
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType,
+ String hostName, String algorithm)
+ throws CertificateException {
+ checkServerTrusted(chain, authType, hostName, false);
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ checkServerTrusted(chain, authType, null, null);
+ }
+
+ /**
+ * Check if the server is trusted
+ *
+ * @param chain The cert chain
+ * @param authType The auth type algorithm
+ * @param checkOnly Whether to "check only" i.e. no user prompt, or to prompt for permission
+ */
+ public synchronized void checkServerTrusted(X509Certificate[] chain,
+ String authType, String hostName,
+ boolean checkOnly) throws CertificateException {
+ CertificateException ce = null;
+ boolean trusted = true;
+ boolean CNMatched = true;
+
+ try {
+ checkAllManagers(chain, authType);
+ } catch (CertificateException e) {
+ trusted = false;
+ ce = e;
+ }
+
+ // If the certificate is not explicitly trusted, we
+ // need to prompt the user
+ if (!isExplicitlyTrusted(chain, authType)) {
+
+ try {
+ HostnameChecker checker = HostnameChecker
+ .getInstance(HostnameChecker.TYPE_TLS);
+
+ checker.match(hostName, chain[0]); // only need to match @ 0 for
+ // CN
+
+ } catch (CertificateException e) {
+ CNMatched = false;
+ ce = e;
+ }
+ }
+
+ if (!trusted || !CNMatched) {
+ if (checkOnly) {
+ throw ce;
+ } else {
+ if (!isTemporarilyUntrusted(chain[0])) {
+ boolean b = askUser(chain, authType, trusted, CNMatched, hostName);
+
+ if (b) {
+ temporarilyTrust(chain[0]);
+ } else {
+ temporarilyUntrust(chain[0]);
+ }
+ }
+
+ checkAllManagers(chain, authType);
+ }
+ }
+ }
+
+ /**
+ * Check system, user and custom trust manager
+ */
+ private void checkAllManagers(X509Certificate[] chain, String authType) throws CertificateException {
+ // First try catrustmanager, then try usertrustmanager, and finally, check temp trusted certs
+ try {
+ caTrustManager.checkServerTrusted(chain, authType);
+ } catch (ValidatorException caex) {
+ try {
+ userTrustManager.checkServerTrusted(chain, authType);
+ } catch (ValidatorException uex) {
+ if (!temporarilyTrusted.contains(chain[0]))
+ throw (CertificateException) uex;
+ }
+ }
+ }
+
+ /**
+ * Return if the user explicitly trusted this i.e. in userTrustManager or temporarilyTrusted
+ */
+ private boolean isExplicitlyTrusted(X509Certificate[] chain, String authType) {
+ boolean explicitlyTrusted = false;
+
+ try {
+ userTrustManager.checkServerTrusted(chain, authType);
+ explicitlyTrusted = true;
+ } catch (ValidatorException uex) {
+ if (temporarilyTrusted.contains(chain[0]))
+ explicitlyTrusted = true;
+ } catch (CertificateException ce) {
+ // do nothing, this means that the cert is not explicitly trusted
+ }
+
+ return explicitlyTrusted;
+
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ // delegate to default
+ return caTrustManager.getAcceptedIssuers();
+ }
+
+ /**
+ * Temporarily untrust the given cert - do not ask the user to trust this
+ * certificate again
+ *
+ * @param c The certificate to trust
+ */
+ private void temporarilyUntrust(Certificate c) {
+ temporarilyUntrusted.add(c);
+ }
+
+ /**
+ * Was this certificate explicitly untrusted by user?
+ *
+ * @param c the certificate
+ * @return true if the user was presented with this certificate and chose
+ * not to trust it
+ */
+ private boolean isTemporarilyUntrusted(Certificate c) {
+ if (temporarilyUntrusted.contains(c)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Temporarily trust the given cert (runtime)
+ *
+ * @param c The certificate to trust
+ */
+ private void temporarilyTrust(Certificate c) {
+ temporarilyTrusted.add(c);
+ }
+
+ /**
+ * Ask user if the certificate should be trusted
+ *
+ * @param chain The certificate chain
+ * @param authType The authentication algorithm
+ * @return user's response
+ */
+ private boolean askUser(X509Certificate[] chain, String authType,
+ boolean isTrusted, boolean hostMatched,
+ String hostName) {
+ return SecurityWarningDialog.showCertWarningDialog(
+ SecurityWarningDialog.AccessType.UNVERIFIED, null,
+ new HttpsCertVerifier(this, chain, authType,
+ isTrusted, hostMatched,
+ hostName));
+ }
+
+ /**
+ * Return an instance of this singleton
+ *
+ * @return The instance
+ */
+ public static VariableX509TrustManager getInstance() {
+ if (instance == null)
+ instance = new VariableX509TrustManager();
+
+ return instance;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java b/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
new file mode 100644
index 0000000..69b98b4
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java
@@ -0,0 +1,335 @@
+/* CertificatePane.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+import net.sourceforge.jnlp.security.SecurityUtil;
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+import net.sourceforge.jnlp.tools.KeyTool;
+
+public class CertificatePane extends JPanel {
+
+ /**
+ * The certificates stored in the user's trusted.certs file.
+ */
+ private ArrayList<X509Certificate> certs = null;
+
+ /**
+ * "Issued To" and "Issued By" string pairs for certs.
+ */
+ private String[][] issuedToAndBy = null;
+ private final String[] columnNames = { "Issued To", "Issued By" };
+
+ private JTable table;
+
+ private JDialog parent;
+
+ private JComponent defaultFocusComponent = null;
+
+ /**
+ * The KeyStore associated with the user's trusted.certs file.
+ */
+ private KeyStore keyStore = null;
+
+ public CertificatePane(JDialog parent) {
+ super();
+ this.parent = parent;
+ initializeKeyStore();
+ addComponents();
+ }
+
+ /**
+ * Reads the user's trusted.cacerts keystore.
+ */
+ private void initializeKeyStore() {
+ try {
+ keyStore = SecurityUtil.getUserKeyStore();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ //create the GUI here.
+ protected void addComponents() {
+ readKeyStore();
+
+ JPanel main = new JPanel(new BorderLayout());
+
+ JPanel tablePanel = new JPanel(new BorderLayout());
+
+ //Table
+ DefaultTableModel tableModel
+ = new DefaultTableModel(issuedToAndBy, columnNames);
+ table = new JTable(tableModel);
+ table.getTableHeader().setReorderingAllowed(false);
+ table.setFillsViewportHeight(true);
+ JScrollPane tablePane = new JScrollPane(table);
+ tablePane.setPreferredSize(new Dimension(500,200));
+ tablePane.setSize(new Dimension(500,200));
+ tablePane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ JTabbedPane tabbedPane = new JTabbedPane();
+ tabbedPane.addTab("User", tablePane);
+ JPanel buttonPanel = new JPanel(new FlowLayout());
+
+ String[] buttonNames = {"Import", "Export", "Remove", "Details"};
+ char[] buttonMnemonics = { KeyEvent.VK_I,
+ KeyEvent.VK_E,
+ KeyEvent.VK_M,
+ KeyEvent.VK_D};
+ ActionListener[] listeners = { new ImportButtonListener(),
+ new ExportButtonListener(),
+ new RemoveButtonListener(),
+ new DetailsButtonListener() };
+ JButton button;
+
+ //get the max width
+ int maxWidth = 0;
+ for (int i = 0; i < buttonNames.length; i++) {
+ button = new JButton(buttonNames[i]);
+ maxWidth = Math.max(maxWidth, button.getMinimumSize().width);
+ }
+
+ for (int i = 0; i < buttonNames.length; i++) {
+ button = new JButton(buttonNames[i]);
+ button.setMnemonic(buttonMnemonics[i]);
+ button.addActionListener(listeners[i]);
+ button.setSize(maxWidth, button.getSize().height);
+ buttonPanel.add(button);
+ }
+
+ tablePanel.add(tabbedPane, BorderLayout.CENTER);
+ tablePanel.add(buttonPanel, BorderLayout.SOUTH);
+
+ JPanel closePanel = new JPanel(new BorderLayout());
+ closePanel.setBorder(BorderFactory.createEmptyBorder(7,7,7,7));
+ JButton closeButton = new JButton("Close");
+ closeButton.addActionListener(new CloseButtonListener());
+ defaultFocusComponent = closeButton;
+ closePanel.add(closeButton, BorderLayout.EAST);
+
+ main.add(tablePanel, BorderLayout.CENTER);
+ main.add(closePanel, BorderLayout.SOUTH);
+
+ add(main);
+
+ }
+
+ /**
+ * Read in the optionPane's keystore to issuedToAndBy.
+ */
+ private void readKeyStore() {
+
+ Enumeration<String> aliases = null;
+ certs = new ArrayList<X509Certificate>();
+ try {
+
+ //Get all of the X509Certificates and put them into an ArrayList
+ aliases = keyStore.aliases();
+ while (aliases.hasMoreElements()) {
+ Certificate c = keyStore.getCertificate(aliases.nextElement());
+ if (c instanceof X509Certificate)
+ certs.add((X509Certificate)c);
+ }
+
+ //get the publisher and root information
+ issuedToAndBy = new String[certs.size()][2];
+ for (int i = 0; i < certs.size(); i++) {
+ X509Certificate c = certs.get(i);
+ issuedToAndBy[i][0] =
+ SecurityUtil.getCN(c.getSubjectX500Principal().getName());
+ issuedToAndBy[i][1] =
+ SecurityUtil.getCN(c.getIssuerX500Principal().getName());
+ }
+ } catch (Exception e) {
+ //TODO
+ }
+ }
+
+ /**
+ * Re-reads the certs file and repopulates the JTable. This is typically
+ * called after a certificate was deleted from the keystore.
+ */
+ private void repopulateTable() {
+ initializeKeyStore();
+ readKeyStore();
+ DefaultTableModel tableModel
+ = new DefaultTableModel(issuedToAndBy, columnNames);
+
+ table.setModel(tableModel);
+ repaint();
+ }
+
+ public void focusOnDefaultButton() {
+ if (defaultFocusComponent != null) {
+ defaultFocusComponent.requestFocusInWindow();
+ }
+ }
+
+ private class ImportButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+
+ JFileChooser chooser = new JFileChooser();
+ int returnVal = chooser.showOpenDialog(parent);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ try {
+ KeyTool kt = new KeyTool();
+ kt.importCert(chooser.getSelectedFile());
+ repopulateTable();
+ } catch (Exception ex) {
+ // TODO: handle exception
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private class ExportButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ //For now, let's just export in -rfc mode as keytool does.
+ //we'll write to a file the exported certificate.
+
+
+ try {
+ int selectedRow = table.getSelectedRow();
+ if (selectedRow != -1) {
+ JFileChooser chooser = new JFileChooser();
+ int returnVal = chooser.showOpenDialog(parent);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ String alias = keyStore.getCertificateAlias(certs
+ .get(selectedRow));
+ if (alias != null) {
+ Certificate c = keyStore.getCertificate(alias);
+ PrintStream ps = new PrintStream(chooser.getSelectedFile().getAbsolutePath());
+ KeyTool.dumpCert(c, ps);
+ repopulateTable();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ // TODO
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private class RemoveButtonListener implements ActionListener {
+
+ /**
+ * Removes a certificate from the keyStore and writes changes to disk.
+ */
+ public void actionPerformed(ActionEvent e) {
+
+ try {
+ int selectedRow = table.getSelectedRow();
+
+ if (selectedRow != -1){
+ String alias = keyStore.getCertificateAlias(certs.get(selectedRow));
+ if (alias != null) {
+
+ int i = JOptionPane.showConfirmDialog(parent,
+ "Are you sure you want to remove the selected certificate?",
+ "Confirmation - Remove Certificate?",
+ JOptionPane.YES_NO_OPTION);
+ if (i == 0) {
+ keyStore.deleteEntry(alias);
+ FileOutputStream fos = new FileOutputStream(
+ SecurityUtil.getTrustedCertsFilename());
+ keyStore.store(fos, SecurityUtil.getTrustedCertsPassword());
+ fos.close();
+ }
+ }
+ repopulateTable();
+ }
+ } catch (Exception ex) {
+ // TODO
+ ex.printStackTrace();
+ }
+
+ }
+ }
+
+ private class DetailsButtonListener implements ActionListener {
+
+ /**
+ * Shows the details of a trusted certificate.
+ */
+ public void actionPerformed(ActionEvent e) {
+
+ int selectedRow = table.getSelectedRow();
+ if (selectedRow != -1 && selectedRow >= 0) {
+ X509Certificate c = certs.get(selectedRow);
+ SecurityWarningDialog.showSingleCertInfoDialog(c, parent);
+ }
+ }
+ }
+
+ private class CloseButtonListener implements ActionListener {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ parent.dispose();
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java b/netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java
new file mode 100644
index 0000000..403472c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java
@@ -0,0 +1,120 @@
+/* CertificateViewer.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.security.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Toolkit;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JDialog;
+import javax.swing.UIManager;
+
+public class CertificateViewer extends JDialog {
+
+ private boolean initialized = false;
+ private static final String dialogTitle = "Certificates";
+
+ CertificatePane panel;
+
+ public CertificateViewer() {
+ super((Frame)null, dialogTitle, true);
+
+ Container contentPane = getContentPane();
+ contentPane.setLayout(new BorderLayout());
+
+ panel = new CertificatePane(this);
+
+ add(panel);
+
+ pack();
+
+ WindowAdapter adapter = new WindowAdapter() {
+ private boolean gotFocus = false;
+
+ public void windowGainedFocus(WindowEvent we) {
+ // Once window gets focus, set initial focus
+ if (!gotFocus) {
+ panel.focusOnDefaultButton();
+ gotFocus = true;
+ }
+ }
+ };
+ addWindowFocusListener(adapter);
+
+ initialized = true;
+ }
+
+ public boolean isInitialized(){
+ return initialized;
+ }
+
+ private void centerDialog() {
+ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension dialogSize = getSize();
+
+ setLocation((screen.width - dialogSize.width)/2,
+ (screen.height - dialogSize.height)/2);
+ }
+
+
+ public static void showCertificateViewer() throws Exception {
+ setSystemLookAndFeel();
+
+ CertificateViewer cv = new CertificateViewer();
+ cv.setResizable(true);
+ cv.centerDialog();
+ cv.setVisible(true);
+ cv.dispose();
+ }
+
+ private static void setSystemLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ // don't worry if we can't.
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ CertificateViewer.showCertificateViewer();
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/services/ExtendedSingleInstanceService.java b/netx/net/sourceforge/jnlp/services/ExtendedSingleInstanceService.java
new file mode 100644
index 0000000..c4b57b2
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/ExtendedSingleInstanceService.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.services;
+
+import javax.jnlp.SingleInstanceService;
+
+import net.sourceforge.jnlp.JNLPFile;
+
+/**
+ * Extends SingleInstanceService to provide a few additional methods that are
+ * required to initialize SingleInstanceService and check things. These methods
+ * are not exposed publicly
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ *
+ */
+interface ExtendedSingleInstanceService extends SingleInstanceService {
+
+ /**
+ * Check if the instance identified by this jnlp file is already running
+ *
+ * @param jnlpFile The JNLPFile that specifies the application
+ *
+ * @throws InstanceExistsException if an instance of this application
+ * already exists
+ *
+ */
+ void checkSingleInstanceRunning(JNLPFile jnlpFile);
+
+ /**
+ * Start a single instance service based on the current application
+ */
+ void initializeSingleInstance();
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/InstanceExistsException.java b/netx/net/sourceforge/jnlp/services/InstanceExistsException.java
new file mode 100644
index 0000000..5de2ff5
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/InstanceExistsException.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.services;
+
+/**
+ *
+ * This class represents an exception indicating that an application instance
+ * already exists for this jnlp file
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ *
+ */
+public class InstanceExistsException extends RuntimeException {
+
+ private static final long serialVersionUID = 7950552292795498272L;
+
+ public InstanceExistsException(String message) {
+ super(message);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/ServiceUtil.java b/netx/net/sourceforge/jnlp/services/ServiceUtil.java
new file mode 100644
index 0000000..b6f7682
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/ServiceUtil.java
@@ -0,0 +1,293 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.jnlp.BasicService;
+import javax.jnlp.ClipboardService;
+import javax.jnlp.DownloadService;
+import javax.jnlp.ExtensionInstallerService;
+import javax.jnlp.FileOpenService;
+import javax.jnlp.FileSaveService;
+import javax.jnlp.PersistenceService;
+import javax.jnlp.PrintService;
+import javax.jnlp.ServiceManager;
+import javax.jnlp.SingleInstanceService;
+import javax.jnlp.UnavailableServiceException;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+
+/**
+ * Provides static methods to interact useful for using the JNLP
+ * services.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ * @version $Revision: 1.8 $
+ */
+public class ServiceUtil {
+
+ private static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ /**
+ * Returns the BasicService reference, or null if the service is
+ * unavailable.
+ */
+ public static BasicService getBasicService() {
+ return (BasicService) getService("javax.jnlp.BasicService");
+ }
+
+ /**
+ * Returns the ClipboardService reference, or null if the service is
+ * unavailable.
+ */
+ public static ClipboardService getClipboardService() {
+ return (ClipboardService) getService("javax.jnlp.ClipboardService");
+ }
+
+ /**
+ * Returns the DownloadService reference, or null if the service is
+ * unavailable.
+ */
+ public static DownloadService getDownloadService() {
+ return (DownloadService) getService("javax.jnlp.DownloadService");
+ }
+
+ /**
+ * Returns the ExtensionInstallerService reference, or null if the service is
+ * unavailable.
+ */
+ public static ExtensionInstallerService getExtensionInstallerService() {
+ return (ExtensionInstallerService) getService("javax.jnlp.ExtensionInstallerService");
+ }
+
+ /**
+ * Returns the FileOpenService reference, or null if the service is
+ * unavailable.
+ */
+ public static FileOpenService getFileOpenService() {
+ return (FileOpenService) getService("javax.jnlp.FileOpenService");
+ }
+
+ /**
+ * Returns the FileSaveService reference, or null if the service is
+ * unavailable.
+ */
+ public static FileSaveService getFileSaveService() {
+ return (FileSaveService) getService("javax.jnlp.FileSaveService");
+ }
+
+ /**
+ * Returns the PersistenceService reference, or null if the service is
+ * unavailable.
+ */
+ public static PersistenceService getPersistenceService() {
+ return (PersistenceService) getService("javax.jnlp.PersistenceService");
+ }
+
+ /**
+ * Returns the PrintService reference, or null if the service is
+ * unavailable.
+ */
+ public static PrintService getPrintService() {
+ return (PrintService) getService("javax.jnlp.PrintService");
+ }
+
+ /**
+ * Returns the SingleInstanceService reference, or null if the service is
+ * unavailable.
+ */
+ public static SingleInstanceService getSingleInstanceService() {
+ return (SingleInstanceService) getService("javax.jnlp.SingleInstanceService");
+ }
+
+ /**
+ * Checks that this application (represented by the jnlp) isnt already running
+ * @param jnlpFile the {@link JNLPFile} that specifies the application
+ *
+ * @throws InstanceExistsException if an instance of this application already exists
+ */
+ public static void checkExistingSingleInstance(JNLPFile jnlpFile) {
+ ExtendedSingleInstanceService esis = (ExtendedSingleInstanceService) getSingleInstanceService();
+ esis.checkSingleInstanceRunning(jnlpFile);
+ }
+
+ /**
+ * Returns the service, or null instead of an UnavailableServiceException
+ */
+ private static Object getService(String name) {
+ try {
+ return ServiceManager.lookup(name);
+ }
+ catch (UnavailableServiceException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Creates a Proxy object implementing the specified interface
+ * when makes all calls in the security context of the system
+ * classes (ie, AllPermissions). This means that the services
+ * must be more than extremely careful in the operations they
+ * perform.
+ */
+ static Object createPrivilegedProxy(Class iface, final Object receiver) {
+ return java.lang.reflect.Proxy.newProxyInstance(XServiceManagerStub.class.getClassLoader(),
+ new Class[] { iface },
+ new PrivilegedHandler(receiver));
+ }
+
+ /**
+ * calls the object's method using privileged access
+ */
+ private static class PrivilegedHandler implements InvocationHandler {
+ private final Object receiver;
+
+ PrivilegedHandler(Object receiver) {
+ this.receiver = receiver;
+ }
+
+ public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("call privileged method: "+method.getName());
+ if (args != null)
+ for (int i=0; i < args.length; i++)
+ System.err.println(" arg: "+args[i]);
+ }
+
+ PrivilegedExceptionAction invoker = new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ return method.invoke(receiver, args);
+ }
+ };
+
+ try {
+ Object result = AccessController.doPrivileged(invoker);
+
+ if (JNLPRuntime.isDebug())
+ System.err.println(" result: "+result);
+
+ return result;
+ } catch (PrivilegedActionException e) {
+ // Any exceptions thrown by the actual methods are wrapped by a
+ // InvocationTargetException, which is further wrapped by the
+ // PrivilegedActionException. Lets unwrap them to make the
+ // proxy transparent to the callers
+ if (e.getCause() instanceof InvocationTargetException) {
+ throw e.getCause().getCause();
+ } else {
+ throw e.getCause();
+ }
+ }
+
+ }
+ };
+
+ /**
+ * Returns whether the app requesting a service is signed. If the app is
+ * unsigned, the user is prompted with a dialog asking if the action
+ * should be allowed.
+ * @param type the type of access being requested
+ * @param extras extra Strings (usually) that are passed to the dialog for
+ * message formatting.
+ * @return true if the access was granted, false otherwise.
+ */
+ public static boolean checkAccess(SecurityWarningDialog.AccessType type,
+ Object... extras) {
+ return checkAccess(null, type, extras);
+ }
+
+ /**
+ * Returns whether the app requesting a service has the right permissions.
+ * If it doesn't, user is prompted for permissions.
+ *
+ * @param app the application which is requesting the check. If null, the current
+ * application is used.
+ * @param type the type of access being requested
+ * @param extras extra Strings (usually) that are passed to the dialog for
+ * message formatting.
+ * @return true if the access was granted, false otherwise.
+ */
+ public static boolean checkAccess(ApplicationInstance app,
+ SecurityWarningDialog.AccessType type,
+ Object... extras) {
+
+ if (app == null)
+ app = JNLPRuntime.getApplication();
+
+ boolean codeTrusted = true;
+
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+
+ for (int i=0; i < stack.length; i++) {
+
+ Class c = null;
+
+ try {
+ c = Class.forName(stack[i].getClassName());
+ } catch (Exception e1) {
+ try {
+ c = Class.forName(stack[i].getClassName(), false, app.getClassLoader());
+ } catch (Exception e2) {
+ System.err.println(e2.getMessage());
+ }
+ }
+
+ // Everything up to the desired class/method must be trusted
+ if (c == null || // class not found
+ ( c.getProtectionDomain().getCodeSource() != null && // class is not in bootclasspath
+ c.getProtectionDomain().getCodeSource().getCodeSigners() == null) // class is trusted
+ ) {
+ codeTrusted = false;
+ }
+ }
+
+ if (!codeTrusted) {
+ final SecurityWarningDialog.AccessType tmpType = type;
+ final Object[] tmpExtras = extras;
+ final ApplicationInstance tmpApp = app;
+
+ //We need to do this to allow proper icon loading for unsigned
+ //applets, otherwise permissions won't be granted to load icons
+ //from resources.jar.
+ Boolean b = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ boolean b = SecurityWarningDialog.showAccessWarningDialog(tmpType,
+ tmpApp.getJNLPFile(), tmpExtras);
+ return new Boolean(b);
+ }
+ });
+
+ return b.booleanValue();
+ }
+
+ return true; //allow
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/services/SingleInstanceLock.java b/netx/net/sourceforge/jnlp/services/SingleInstanceLock.java
new file mode 100644
index 0000000..5108642
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/SingleInstanceLock.java
@@ -0,0 +1,194 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.services;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.BindException;
+import java.net.ServerSocket;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.FileUtils;
+
+/**
+ * This class represents a Lock for single instance jnlp applications
+ *
+ * The lock is per-session, per user.
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ */
+class SingleInstanceLock {
+
+ JNLPFile jnlpFile;
+ File lockFile = null;
+
+ public static final int INVALID_PORT = Integer.MIN_VALUE;
+
+ int port = INVALID_PORT;
+
+ /**
+ * Create an object to manage the instance lock for the specified JNLP file.
+ *
+ * @param jnlpFile the jnlpfile to create the lock for
+ */
+ public SingleInstanceLock(JNLPFile jnlpFile) {
+ this.jnlpFile = jnlpFile;
+ lockFile = getLockFile();
+
+ }
+
+ /**
+ * Create/overwrite the instance lock for the jnlp file.
+ *
+ * @param localPort the network port for the lock
+ * @throws IOException on any io problems
+ */
+ public void createWithPort(int localPort) throws IOException {
+
+ BufferedWriter lockFileWriter = new BufferedWriter(new FileWriter(lockFile, false));
+ lockFileWriter.write(String.valueOf(localPort));
+ lockFileWriter.newLine();
+ lockFileWriter.flush();
+ lockFileWriter.close();
+
+ }
+
+ /**
+ * Returns true if the lock if valid. That is, the lock exists, and port it
+ * points to is listening for incoming messages.
+ */
+ public boolean isValid() {
+ return (exists() && getPort() != INVALID_PORT && !isPortFree(getPort()));
+ }
+
+ /**
+ * Returns the port in this lock file.
+ */
+ public int getPort() {
+ if (!exists()) {
+ return INVALID_PORT;
+ }
+
+ try {
+ parseFile();
+ } catch (NumberFormatException e) {
+ port = INVALID_PORT;
+ } catch (IOException e) {
+ port = INVALID_PORT;
+ }
+ return port;
+
+ }
+
+ /**
+ * Returns true if the lock file already exists.
+ */
+ private boolean exists() {
+ return lockFile.exists();
+ }
+
+ /**
+ * Returns true if the port is free.
+ */
+ private boolean isPortFree(int port) {
+ try {
+ ServerSocket socket = new ServerSocket(port);
+ socket.close();
+ return true;
+ } catch (BindException e) {
+ return false;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Return a file object that represents the lock file. The lock file itself
+ * may or may not exist.
+ */
+ private File getLockFile() {
+ File baseDir = new File(JNLPRuntime.LOCKS_DIR);
+
+ if (!baseDir.isDirectory() && !baseDir.mkdirs()) {
+ throw new RuntimeException(R("RNoLockDir", baseDir));
+ }
+ String lockFileName = getLockFileName();
+ File applicationLockFile = new File(baseDir, lockFileName);
+ return applicationLockFile;
+ }
+
+ /**
+ * Returns the name of the lock file.
+ */
+ private String getLockFileName() {
+ String initialName = "";
+
+ if (jnlpFile.getSourceLocation() != null) {
+ initialName = initialName + jnlpFile.getSourceLocation();
+ } else {
+ initialName = initialName + jnlpFile.getFileLocation();
+ }
+
+ if (jnlpFile.getFileVersion() != null) {
+ initialName = initialName + jnlpFile.getFileVersion().toString();
+ }
+
+ initialName = initialName + getCurrentDisplay();
+ return FileUtils.sanitizeFileName(initialName);
+
+ }
+
+ /**
+ * Parse the lock file.
+ *
+ * @throws NumberFormatException
+ * @throws IOException
+ */
+ private void parseFile() throws NumberFormatException, IOException {
+ BufferedReader lockFileReader = new BufferedReader(new FileReader(lockFile));
+ int port = Integer.valueOf(lockFileReader.readLine());
+ lockFileReader.close();
+ this.port = port;
+ }
+
+ /**
+ * Returns a string identifying this display.
+ *
+ * Implementation note: On systems with X support, this is the DISPLAY
+ * variable
+ *
+ * @return a string that is guaranteed to be not null.
+ */
+ private String getCurrentDisplay() {
+ String display = System.getenv("DISPLAY");
+ return (display == null) ? "" : display;
+ }
+
+ private static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ private static String R(String key, Object param) {
+ return JNLPRuntime.getMessage(key, new Object[] { param });
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XBasicService.java b/netx/net/sourceforge/jnlp/services/XBasicService.java
new file mode 100644
index 0000000..a7ef906
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XBasicService.java
@@ -0,0 +1,232 @@
+// Copyright (C) 2001 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jnlp.BasicService;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import net.sourceforge.jnlp.InformationDesc;
+import net.sourceforge.jnlp.JARDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.Launcher;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.PropertiesFile;
+
+/**
+ * The BasicService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.10 $
+ */
+class XBasicService implements BasicService {
+
+ /** command used to exec the native browser */
+ private String command = null;
+
+ /** whether the command was loaded / prompted for */
+ private boolean initialized = false;
+
+
+ protected XBasicService() {
+ }
+
+ /**
+ * Returns the codebase of the application, applet, or
+ * installer. If the codebase was not specified in the JNLP
+ * element then the main JAR's location is returned. If no main
+ * JAR was specified then the location of the JAR containing the
+ * main class is returned.
+ */
+ public URL getCodeBase() {
+ ApplicationInstance app = JNLPRuntime.getApplication();
+
+ if (app != null) {
+ JNLPFile file = app.getJNLPFile();
+
+ // return the codebase.
+ if (file.getCodeBase() != null)
+ return file.getCodeBase();
+
+ // else return the main JAR's URL.
+ JARDesc mainJar = file.getResources().getMainJAR();
+ if (mainJar != null)
+ return mainJar.getLocation();
+
+ // else find JAR where main class was defined.
+ //
+ // JNLPFile file = app.getJNLPFile();
+ // String mainClass = file.getLaunchInfo().getMainClass()+".class";
+ // URL jarUrl = app.getClassLoader().getResource(mainClass);
+ // go through list of JARDesc to find one matching jarUrl
+ }
+
+ return null;
+ }
+
+ /**
+ * Return true if the Environment is Offline
+ */
+ public boolean isOffline() {
+
+ URL url = findFirstURLFromJNLPFile();
+
+ try {
+ url.openConnection().getInputStream().close();
+ return false;
+ } catch (IOException exception) {
+ return true;
+ }
+ }
+
+ /**
+ * Return the first URL from the jnlp file
+ * Or a default URL if no url found in JNLP file
+ */
+ private URL findFirstURLFromJNLPFile() {
+
+ ApplicationInstance app = JNLPRuntime.getApplication();
+
+ if (app != null) {
+ JNLPFile jnlpFile = app.getJNLPFile();
+
+ URL sourceURL = jnlpFile.getSourceLocation();
+ if (sourceURL != null) {
+ return sourceURL;
+ }
+
+ URL codeBaseURL = jnlpFile.getCodeBase();
+ if (codeBaseURL != null) {
+ return codeBaseURL;
+ }
+
+ InformationDesc informationDesc = jnlpFile.getInformation();
+ URL homePage = informationDesc.getHomepage();
+ if (homePage != null) {
+ return homePage;
+ }
+
+ JARDesc[] jarDescs = jnlpFile.getResources().getJARs();
+ for (JARDesc jarDesc: jarDescs) {
+ return jarDesc.getLocation();
+ }
+ }
+
+ // this section is only reached if the jnlp file has no jars.
+ // that doesnt seem very likely.
+ URL arbitraryURL;
+ try {
+ arbitraryURL = new URL("http://icedtea.classpath.org");
+ } catch (MalformedURLException malformedURL) {
+ throw new RuntimeException(malformedURL);
+ }
+
+ return arbitraryURL;
+ }
+
+ /**
+ * Return true if a Web Browser is Supported
+ */
+ public boolean isWebBrowserSupported() {
+ initialize();
+
+ return command != null;
+ }
+
+ /**
+ * Show a document.
+ *
+ * @return whether the document was opened
+ */
+ public boolean showDocument(URL url) {
+ initialize();
+
+ if (url.toString().endsWith(".jnlp")) {
+ try {
+ new Launcher().launchExternal(url);
+ return true;
+ }
+ catch (Exception ex) {
+ return false;
+ }
+ }
+
+ if (command != null) {
+ try {
+ // this is bogus because the command may require options;
+ // should use a StreamTokenizer or similar to get tokens
+ // outside of quotes.
+ Runtime.getRuntime().exec(command + " " + url.toString());
+ //Runtime.getRuntime().exec(new String[]{command,url.toString()});
+
+ return true;
+ }
+ catch(IOException ex){
+ if (JNLPRuntime.isDebug())
+ ex.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ private void initialize() {
+ if (initialized)
+ return;
+ initialized = true;
+
+ if(isWindows()) {
+ command = "rundll32 url.dll,FileProtocolHandler ";
+ }
+ else {
+ PropertiesFile props = JNLPRuntime.getProperties();
+ command = props.getProperty("browser.command");
+
+ if(command == null) { // prompt & store
+ command = promptForCommand(null);
+
+ if(command != null) {
+ props.setProperty("browser.command", command);
+ props.store();
+ }
+ }
+ }
+ }
+
+ private boolean isWindows() {
+ String os = System.getProperty("os.name");
+ if(os != null && os.startsWith("Windows"))
+ return true;
+ else
+ return false;
+ }
+
+ private String promptForCommand(String cmd) {
+ return JOptionPane.showInputDialog(new JPanel(),
+ "Browser Location:",
+ "Specify Browser Location",
+ JOptionPane.PLAIN_MESSAGE
+ );
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XClipboardService.java b/netx/net/sourceforge/jnlp/services/XClipboardService.java
new file mode 100644
index 0000000..860d859
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XClipboardService.java
@@ -0,0 +1,81 @@
+/* XClipboardService.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.services;
+
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+
+import java.awt.datatransfer.Transferable;
+import java.awt.Toolkit;
+
+/**
+ * The ClipboardService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+class XClipboardService implements ClipboardService {
+
+ protected XClipboardService() {
+ }
+
+ /**
+ * Returns the contents of the system clipboard.
+ */
+ public java.awt.datatransfer.Transferable getContents(){
+
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.CLIPBOARD_READ)) {
+ Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
+ return (Transferable) ServiceUtil.createPrivilegedProxy(
+ Transferable.class, t);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the contents of the system clipboard.
+ */
+ public void setContents(java.awt.datatransfer.Transferable contents) {
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.CLIPBOARD_WRITE)) {
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
+ contents, null);
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XDownloadService.java b/netx/net/sourceforge/jnlp/services/XDownloadService.java
new file mode 100644
index 0000000..174071a
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XDownloadService.java
@@ -0,0 +1,179 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+
+/**
+ * The DownloadService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+class XDownloadService implements DownloadService {
+
+ protected XDownloadService() {
+ }
+
+ // comments copied from DownloadService interface
+
+ /**
+ * Returns a listener that will automatically display download
+ * progress to the user.
+ */
+ public DownloadServiceListener getDefaultProgressWindow() {
+ return null;
+ }
+
+ /**
+ * Returns whether the part in an extension (specified by the
+ * url and version) is cached locally.
+ */
+ public boolean isExtensionPartCached(URL ref, String version, String part) {
+ return true;
+ }
+
+ /**
+ * Returns whether the parts in an extension (specified by the
+ * url and version) are cached locally.
+ */
+ public boolean isExtensionPartCached(URL ref, String version, String[] parts) {
+ return true;
+ }
+
+ /**
+ * Returns whether the part of the calling application is cached
+ * locally. If called by code specified by an extension
+ * descriptor, the specified part refers to the extension not
+ * the application.
+ */
+ public boolean isPartCached(String part) {
+ return true;
+ }
+
+ /**
+ * Returns whether all of the parts of the calling application
+ * are cached locally. If called by code in an extension, the
+ * part refers the the part of the extension not the
+ * application.
+ */
+ public boolean isPartCached(String[] parts) {
+ return true;
+ }
+
+ /**
+ * Returns whether the resource is cached locally. This method
+ * only returns true if the resource is specified by the calling
+ * application or extension.
+ */
+ public boolean isResourceCached(URL ref, String version) {
+ return true;
+ }
+
+ /**
+ * Downloads the parts of an extension.
+ *
+ * @throws IOException
+ */
+ public void loadExtensionPart(URL ref, String version, String[] parts, DownloadServiceListener progress) throws IOException {
+ }
+
+ /**
+ * Downloads a part of an extension.
+ *
+ * @throws IOException
+ */
+ public void loadExtensionPart(URL ref, String version, String part, DownloadServiceListener progress) throws IOException {
+ }
+
+ /**
+ * Downloads the parts.
+ *
+ * @throws IOException
+ */
+ public void loadPart(String[] parts, DownloadServiceListener progress) throws IOException {
+ }
+
+ /**
+ * Downloads the part.
+ *
+ * @throws IOException
+ */
+ public void loadPart(String part, DownloadServiceListener progress) throws IOException {
+ }
+
+ /**
+ * Downloads a resource.
+ *
+ * @throws IOException
+ */
+ public void loadResource(URL ref, String version, DownloadServiceListener progress) throws IOException {
+ }
+
+ /**
+ * Notify the system that an extension's part is no longer
+ * important to cache.
+ *
+ * @throws IOException
+ */
+ public void removeExtensionPart(URL ref, String version, String part) throws IOException {
+ }
+
+ /**
+ * Notify the system that an extension's parts are no longer
+ * important to cache.
+ *
+ * @throws IOException
+ */
+ public void removeExtensionPart(URL ref, String version, String[] parts) throws IOException {
+ }
+
+ /**
+ * Notifies the system that a part is no longer important to
+ * cache.
+ *
+ * @throws IOException
+ */
+ public void removePart(String part) throws IOException {
+ }
+
+ /**
+ * Notifies the system that the parts is no longer important to
+ * cache.
+ *
+ * @throws IOException
+ */
+ public void removePart(String[] parts) throws IOException {
+ }
+
+ /**
+ * Notifies the system that the resource is no longer important
+ * to cache.
+ *
+ * @throws IOException
+ */
+ public void removeResource(URL ref, String version) throws IOException {
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XExtendedService.java b/netx/net/sourceforge/jnlp/services/XExtendedService.java
new file mode 100644
index 0000000..9eafba5
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XExtendedService.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.services;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.jnlp.ExtendedService;
+import javax.jnlp.FileContents;
+
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+
+/**
+ * Implementation of ExtendedService
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ *
+ */
+public class XExtendedService implements ExtendedService {
+
+ public FileContents openFile(File file) throws IOException {
+
+ /* FIXME: this opens a file with read/write mode, not just read or write */
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.READ_FILE,
+ new Object[]{ file.getAbsolutePath() })) {
+ return (FileContents) ServiceUtil.createPrivilegedProxy(FileContents.class,
+ new XFileContents(file));
+ } else {
+ return null;
+ }
+
+ }
+
+ public FileContents[] openFiles(File[] files) throws IOException {
+ FileContents[] contents = new FileContents[files.length];
+ for (int i = 0; i < files.length; i++) {
+ contents[i] = openFile(files[i]);
+ }
+ return contents;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XExtensionInstallerService.java b/netx/net/sourceforge/jnlp/services/XExtensionInstallerService.java
new file mode 100644
index 0000000..7fea6bb
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XExtensionInstallerService.java
@@ -0,0 +1,121 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+
+/**
+ * The ExtensionInstallerService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+class XExtensionInstallerService implements ExtensionInstallerService {
+
+ protected XExtensionInstallerService() {
+ }
+
+ /**
+ *
+ */
+ public URL getExtensionLocation() {
+ return null;
+ }
+
+ /**
+ *
+ */
+ public String getExtensionVersion() {
+ return null;
+ }
+
+ /**
+ *
+ */
+ public String getInstalledJRE(java.net.URL url, java.lang.String version) {
+ return null;
+ }
+
+ /**
+ *
+ */
+ public String getInstallPath() {
+ return null;
+ }
+
+ /**
+ *
+ */
+ public void hideProgressBar() {
+ }
+
+ /**
+ *
+ */
+ public void hideStatusWindow() {
+ }
+
+ /**
+ *
+ */
+ public void installFailed() {
+ }
+
+ /**
+ *
+ */
+ public void installSucceeded(boolean needsReboot) {
+ }
+
+ /**
+ *
+ */
+ public void setHeading(java.lang.String heading) {
+ }
+
+ /**
+ *
+ */
+ public void setJREInfo(java.lang.String platformVersion, java.lang.String jrePath) {
+ }
+
+ /**
+ *
+ */
+ public void setNativeLibraryInfo(java.lang.String path) {
+ }
+
+ /**
+ *
+ */
+ public void setStatus(java.lang.String status) {
+ }
+
+ /**
+ *
+ */
+ public void updateProgress(int value) {
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XFileContents.java b/netx/net/sourceforge/jnlp/services/XFileContents.java
new file mode 100644
index 0000000..c7b331a
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XFileContents.java
@@ -0,0 +1,121 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * File contents.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+class XFileContents implements FileContents {
+
+ /** the file */
+ private File file;
+
+ /**
+ * Create a file contents implementation for the file.
+ */
+ protected XFileContents(File file) {
+ this.file = file;
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public boolean canRead() throws IOException {
+ return file.canRead();
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public boolean canWrite() throws IOException {
+ return file.canWrite();
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public InputStream getInputStream() throws IOException {
+ return new FileInputStream(file);
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public long getLength() throws IOException {
+ return file.length();
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public long getMaxLength() throws IOException {
+ return Long.MAX_VALUE;
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public String getName() throws IOException {
+ return file.getName();
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public OutputStream getOutputStream(boolean overwrite) throws IOException {
+ // file.getPath compatible with pre-1.4 JREs
+ return new FileOutputStream(file.getPath(), !overwrite);
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public JNLPRandomAccessFile getRandomAccessFile(String mode) throws IOException {
+ return new XJNLPRandomAccessFile(file, mode);
+ }
+
+ /**
+ *
+ * @throws IOException if an I/O exception occurs.
+ */
+ public long setMaxLength(long maxlength) throws IOException {
+ return maxlength;
+ }
+
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XFileOpenService.java b/netx/net/sourceforge/jnlp/services/XFileOpenService.java
new file mode 100644
index 0000000..8f2c110
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XFileOpenService.java
@@ -0,0 +1,113 @@
+/* XFileOpenService.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.security.SecurityWarningDialog;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import java.security.*;
+
+/**
+ * The FileOpenService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+class XFileOpenService implements FileOpenService {
+
+ protected XFileOpenService() {
+ }
+
+ /**
+ * Prompts the user to select a single file.
+ */
+ public FileContents openFileDialog (java.lang.String pathHint,
+ java.lang.String[] extensions) throws java.io.IOException {
+
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.READ_FILE)) {
+
+ //open a file dialog here, let the user choose the file.
+ JFileChooser chooser = new JFileChooser();
+ int chosen = chooser.showOpenDialog(null);
+ if (chosen == JFileChooser.APPROVE_OPTION) {
+ return (FileContents) ServiceUtil.createPrivilegedProxy(
+ FileContents.class,
+ new XFileContents(chooser.getSelectedFile()));
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Prompts the user to select one or more files.
+ */
+ public FileContents[] openMultiFileDialog (java.lang.String pathHint,
+ java.lang.String[] extensions) throws java.io.IOException {
+
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setMultiSelectionEnabled(true);
+ int chosen = chooser.showOpenDialog(null);
+
+ if (chosen == JFileChooser.APPROVE_OPTION) {
+ File[] files = chooser.getSelectedFiles();
+ int length = files.length;
+ XFileContents[] xfiles = new XFileContents[length];
+ for (int i = 0; i < length; i++)
+ xfiles[i] = new XFileContents(files[i]);
+ return (FileContents[]) ServiceUtil.createPrivilegedProxy(
+ FileContents.class, xfiles);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/services/XFileSaveService.java b/netx/net/sourceforge/jnlp/services/XFileSaveService.java
new file mode 100644
index 0000000..d3fbe2e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XFileSaveService.java
@@ -0,0 +1,140 @@
+/* XFileSaveService.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.security.*;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import java.security.*;
+
+/**
+ * The FileSaveService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Joshua Sumali</a>
+ */
+class XFileSaveService implements FileSaveService {
+
+ protected XFileSaveService() {
+ }
+
+ /**
+ * Prompts the user to save a file.
+ */
+ public FileContents saveFileDialog(java.lang.String pathHint,
+ java.lang.String[] extensions, java.io.InputStream stream,
+ java.lang.String name) throws java.io.IOException {
+
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) {
+ JFileChooser chooser = new JFileChooser();
+ int chosen = chooser.showSaveDialog(null);
+
+ if (chosen == JFileChooser.APPROVE_OPTION) {
+ writeToFile(stream, chooser.getSelectedFile());
+ return (FileContents) ServiceUtil.createPrivilegedProxy(
+ FileContents.class,
+ new XFileContents(chooser.getSelectedFile()));
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Prompts the user to save a file, with an optional pre-set filename.
+ */
+ public FileContents saveAsFileDialog(java.lang.String pathHint,
+ java.lang.String[] extensions, FileContents contents) throws java.io.IOException {
+
+ if (ServiceUtil.checkAccess(SecurityWarningDialog.AccessType.WRITE_FILE)) {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setSelectedFile(new File(contents.getName()));
+ int chosen = chooser.showSaveDialog(null);
+
+ if (chosen == JFileChooser.APPROVE_OPTION) {
+ writeToFile(contents.getInputStream(),
+ chooser.getSelectedFile());
+
+ return (FileContents) ServiceUtil.createPrivilegedProxy(
+ FileContents.class,
+ new XFileContents(chooser.getSelectedFile()));
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Writes actual file to disk.
+ */
+ private void writeToFile(InputStream stream, File file) throws IOException {
+ if (!file.createNewFile()) { //file exists
+ boolean replace = (JOptionPane.showConfirmDialog(null,
+ file.getAbsolutePath() + " already exists.\n"
+ +"Do you want to replace it?",
+ "Warning - File Exists", JOptionPane.YES_NO_OPTION) == 0);
+ if (!replace)
+ return;
+ } else {
+ file.createNewFile();
+ }
+
+ if (file.canWrite()) {
+ FileOutputStream out = new FileOutputStream(file);
+ byte[] b = new byte[256];
+ int read = 0;
+ while ((read = stream.read(b)) > 0)
+ out.write(b, 0, read);
+ out.flush();
+ out.close();
+ } else {
+ throw new IOException("Unable to open file for writing");
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/services/XJNLPRandomAccessFile.java b/netx/net/sourceforge/jnlp/services/XJNLPRandomAccessFile.java
new file mode 100644
index 0000000..8e97743
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XJNLPRandomAccessFile.java
@@ -0,0 +1,203 @@
+/* XJNLPRandomAccessFile.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+package net.sourceforge.jnlp.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import javax.jnlp.JNLPRandomAccessFile;
+
+public class XJNLPRandomAccessFile implements JNLPRandomAccessFile {
+
+ private RandomAccessFile raf;
+
+ public XJNLPRandomAccessFile(File file, String mode) throws IOException {
+ raf = new RandomAccessFile(file, mode);
+
+ }
+
+ public void close() throws IOException {
+ raf.close();
+ }
+
+ public long getFilePointer() throws IOException {
+ return raf.getFilePointer();
+ }
+
+ public long length() throws IOException {
+ return raf.length();
+ }
+
+ public int read() throws IOException {
+ return raf.read();
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ return raf.read(b, off, len);
+ }
+
+ public int read(byte[] b) throws IOException {
+ return raf.read(b);
+ }
+
+ public boolean readBoolean() throws IOException {
+ return raf.readBoolean();
+ }
+
+ public byte readByte() throws IOException {
+ return raf.readByte();
+ }
+
+ public char readChar() throws IOException {
+ return raf.readChar();
+ }
+
+ public double readDouble() throws IOException {
+ return raf.readDouble();
+ }
+
+ public float readFloat() throws IOException {
+ return raf.readFloat();
+ }
+
+ public void readFully(byte[] b) throws IOException {
+ raf.readFully(b);
+ }
+
+ public void readFully(byte[] b, int off, int len) throws IOException {
+ raf.readFully(b, off, len);
+ }
+
+ public int readInt() throws IOException {
+ return raf.readInt();
+ }
+
+ public String readLine() throws IOException {
+ return raf.readLine();
+ }
+
+ public long readLong() throws IOException {
+ return raf.readLong();
+ }
+
+ public short readShort() throws IOException {
+ return raf.readShort();
+ }
+
+ public String readUTF() throws IOException {
+ return raf.readUTF();
+ }
+
+ public int readUnsignedByte() throws IOException {
+ return raf.readUnsignedByte();
+ }
+
+ public int readUnsignedShort() throws IOException {
+ return raf.readUnsignedShort();
+ }
+
+ public void seek(long pos) throws IOException {
+ raf.seek(pos);
+ }
+
+ public void setLength(long newLength) throws IOException {
+ raf.setLength(newLength);
+ }
+
+ public int skipBytes(int n) throws IOException {
+ return raf.skipBytes(n);
+ }
+
+ public void write(int b) throws IOException {
+ raf.write(b);
+
+ }
+
+ public void write(byte[] b) throws IOException {
+ raf.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ raf.write(b, off, len);
+ }
+
+ public void writeBoolean(boolean v) throws IOException {
+ raf.writeBoolean(v);
+ }
+
+ public void writeByte(int v) throws IOException {
+ raf.writeByte(v);
+ }
+
+ public void writeBytes(String s) throws IOException {
+ raf.writeBytes(s);
+ }
+
+ public void writeChar(int v) throws IOException {
+ raf.writeChar(v);
+ }
+
+ public void writeChars(String s) throws IOException {
+ raf.writeChars(s);
+ }
+
+ public void writeDouble(double v) throws IOException {
+ raf.writeDouble(v);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ raf.writeFloat(v);
+ }
+
+ public void writeInt(int v) throws IOException {
+ raf.writeInt(v);
+ }
+
+ public void writeLong(long v) throws IOException {
+ raf.writeLong(v);
+ }
+
+ public void writeShort(int v) throws IOException {
+ raf.writeShort(v);
+ }
+
+ public void writeUTF(String str) throws IOException {
+ raf.writeUTF(str);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XPersistenceService.java b/netx/net/sourceforge/jnlp/services/XPersistenceService.java
new file mode 100644
index 0000000..db45163
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XPersistenceService.java
@@ -0,0 +1,177 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.cache.*;
+import net.sourceforge.jnlp.runtime.*;
+
+/**
+ * The BasicService JNLP service.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.7 $
+ */
+class XPersistenceService implements PersistenceService {
+
+ // todo: recheck delete, etc to make sure security is tight
+
+ protected XPersistenceService() {
+ }
+
+ /**
+ * Checks whether the application has access to URL area
+ * requested. If the method returns normally then the specified
+ * location can be accessed by the current application.
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ protected void checkLocation(URL location) throws MalformedURLException {
+ ApplicationInstance app = JNLPRuntime.getApplication();
+ if (app == null)
+ throw new MalformedURLException("Cannot determine the current application.");
+
+ URL source = app.getJNLPFile().getCodeBase();
+
+ if (!source.getHost().equalsIgnoreCase(location.getHost()))
+ throw new MalformedURLException("Cannot access data from a different host.");
+
+ // test for above codebase, not perfect but works for now
+
+ String requestPath = location.getFile();
+ if (-1 != requestPath.lastIndexOf("/"))
+ requestPath = requestPath.substring(0, requestPath.lastIndexOf("/"));
+ else
+ requestPath = "";
+
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("codebase path: "+source.getFile());
+ System.out.println("request path: "+requestPath);
+ }
+
+ if (!source.getFile().startsWith(requestPath))
+ throw new MalformedURLException("Cannot access data below source URL path.");
+ }
+
+ /**
+ * Converts a URL into a file in the persistence store.
+ *
+ * @return the file
+ */
+ protected File toCacheFile(URL location) throws MalformedURLException {
+ return CacheUtil.urlToPath(location, "pcache");
+ }
+
+ /**
+ *
+ * @return the maximum size of storage that got granted, in bytes
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public long create(URL location, long maxsize) throws MalformedURLException, IOException {
+ checkLocation(location);
+
+ File file = toCacheFile(location);
+ file.getParentFile().mkdirs();
+
+ if (!file.createNewFile())
+ throw new IOException("File already exists.");
+
+ return maxsize;
+ }
+
+ /**
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public void delete(URL location) throws MalformedURLException, IOException {
+ checkLocation(location);
+
+ toCacheFile(location).delete();
+ }
+
+ /**
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public FileContents get(URL location) throws MalformedURLException, IOException, FileNotFoundException {
+ checkLocation(location);
+
+ File file = toCacheFile(location);
+ if (!file.exists())
+ throw new FileNotFoundException("Persistence store for "
+ + location.toString() + " is not found.");
+ file.getParentFile().mkdirs();
+
+ return (FileContents) ServiceUtil.createPrivilegedProxy(FileContents.class, new XFileContents(file));
+ }
+
+ /**
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public String[] getNames(URL location) throws MalformedURLException, IOException {
+ checkLocation(location);
+
+
+ File file = toCacheFile(location);
+ if (!file.isDirectory())
+ return new String[0];
+
+ List result = new ArrayList();
+
+ // check whether this is right: only add files and not directories.
+ File entries[] = file.listFiles();
+ for (int i=0; i < entries.length; i++)
+ if (entries[i].isFile())
+ result.add(entries[i].getName());
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ /**
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public int getTag(URL location) throws MalformedURLException, IOException {
+ checkLocation(location);
+
+ // todo: actually implement tags
+
+ if (toCacheFile(location).exists())
+ return PersistenceService.CACHED;
+
+ return PersistenceService.CACHED;
+ }
+
+ /**
+ *
+ * @throws MalformedURLException if the application cannot access the location
+ */
+ public void setTag(URL location, int tag) throws MalformedURLException, IOException {
+ checkLocation(location);
+
+ // todo: actually implement tags
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XPrintService.java b/netx/net/sourceforge/jnlp/services/XPrintService.java
new file mode 100644
index 0000000..f2f75dd
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XPrintService.java
@@ -0,0 +1,123 @@
+/* XPrintService.java
+ Copyright (C) 2008 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.services;
+
+import java.awt.print.PageFormat;
+import java.awt.print.Pageable;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+
+import javax.jnlp.*;
+import javax.swing.JOptionPane;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+public class XPrintService implements PrintService {
+
+ // If pj is null, then we do not have a printer to use.
+ private PrinterJob pj;
+
+ public XPrintService() {
+ pj = PrinterJob.getPrinterJob();
+ }
+
+ public PageFormat getDefaultPage() {
+ if (pj != null)
+ return pj.defaultPage();
+ else {
+ showWarning();
+ return new PageFormat(); // might not have default settings.
+ }
+ }
+
+ public PageFormat showPageFormatDialog(PageFormat page) {
+ if (pj != null)
+ return pj.pageDialog(page);
+ else {
+ showWarning();
+ return page;
+ }
+
+ }
+
+ public boolean print(Pageable document) {
+ if (pj != null) {
+ pj.setPageable(document);
+ if (pj.printDialog()) {
+ try {
+ pj.print();
+ return true;
+ } catch(PrinterException pe) {
+ System.err.println("Could not print: " + pe);
+ return false;
+ }
+ }
+ } else
+ showWarning();
+
+ return false;
+ }
+
+ public boolean print(Printable painter) {
+ if (pj != null) {
+ pj.setPrintable(painter);
+ if (pj.printDialog()) {
+ try {
+ pj.print();
+ return true;
+ } catch(PrinterException pe) {
+ System.err.println("Could not print: " + pe);
+ return false;
+ }
+
+ }
+ } else
+ showWarning();
+
+ return false;
+ }
+
+ private void showWarning() {
+ JOptionPane.showMessageDialog(null,
+ "Unable to find a default printer.",
+ "Warning",
+ JOptionPane.WARNING_MESSAGE);
+ System.err.println("Unable to print: Unable to find default printer.");
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/services/XServiceManagerStub.java b/netx/net/sourceforge/jnlp/services/XServiceManagerStub.java
new file mode 100644
index 0000000..756b412
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XServiceManagerStub.java
@@ -0,0 +1,106 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import java.lang.reflect.*;
+import java.security.*;
+import javax.jnlp.*;
+
+import net.sourceforge.jnlp.*;
+
+/**
+ * Lookup table for services.
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.6 $
+ */
+public class XServiceManagerStub implements ServiceManagerStub {
+
+ // todo: only include ExtensionInstallerService if an installer
+ // is getting the service, otherwise return null.
+
+ // todo: fix services to do their own privileged actions that
+ // run less code in the secure environment (or avoid privileged
+ // actions by giving permission to the code source).
+
+ private static String serviceNames[] = {
+ "javax.jnlp.BasicService", // required
+ "javax.jnlp.DownloadService", // required
+ "javax.jnlp.ExtendedService",
+ "javax.jnlp.ExtensionInstallerService", // required
+ "javax.jnlp.PersistenceService",
+ "javax.jnlp.FileOpenService",
+ "javax.jnlp.FileSaveService",
+ "javax.jnlp.ClipboardService",
+ "javax.jnlp.PrintService",
+ "javax.jnlp.SingleInstanceService"
+ };
+
+ private static Object services[] = {
+ ServiceUtil.createPrivilegedProxy(BasicService.class, new XBasicService()),
+ ServiceUtil.createPrivilegedProxy(DownloadService.class, new XDownloadService()),
+ ServiceUtil.createPrivilegedProxy(ExtendedService.class, new XExtendedService()),
+ ServiceUtil.createPrivilegedProxy(ExtensionInstallerService.class, new XExtensionInstallerService()),
+ ServiceUtil.createPrivilegedProxy(PersistenceService.class, new XPersistenceService()),
+ ServiceUtil.createPrivilegedProxy(FileOpenService.class, new XFileOpenService()),
+ ServiceUtil.createPrivilegedProxy(FileSaveService.class, new XFileSaveService()),
+ ServiceUtil.createPrivilegedProxy(ClipboardService.class, new XClipboardService()),
+ ServiceUtil.createPrivilegedProxy(PrintService.class, new XPrintService()),
+ ServiceUtil.createPrivilegedProxy(ExtendedSingleInstanceService.class, new XSingleInstanceService())
+ };
+
+
+ public XServiceManagerStub() {
+ }
+
+ /**
+ * Returns the service names.
+ */
+ public String[] getServiceNames() {
+ // make sure it is a copy because we might be returning to
+ // code we don't own.
+ String result[] = new String[serviceNames.length];
+ System.arraycopy(serviceNames, 0, result, 0, serviceNames.length);
+
+ return result;
+ }
+
+ /**
+ * Returns the service.
+ *
+ * @throws UnavailableServiceException if service is not available
+ */
+ public Object lookup(String name) throws UnavailableServiceException {
+ // exact match
+ for (int i=0; i < serviceNames.length; i++)
+ if (serviceNames[i].equals(name))
+ return services[i];
+
+ // substring match
+ for (int i=0; i < serviceNames.length; i++)
+ if (-1 != serviceNames[i].indexOf(name))
+ return services[i];
+
+ throw new UnavailableServiceException(""+name);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/XSingleInstanceService.java b/netx/net/sourceforge/jnlp/services/XSingleInstanceService.java
new file mode 100644
index 0000000..1a40794
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/XSingleInstanceService.java
@@ -0,0 +1,236 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.services;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jnlp.SingleInstanceListener;
+import javax.management.InstanceAlreadyExistsException;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * This class implements SingleInstanceService
+ *
+ * @author <a href="mailto:[email protected]">Omair Majid</a>
+ */
+public class XSingleInstanceService implements ExtendedSingleInstanceService {
+
+ boolean initialized = false;
+ List<SingleInstanceListener> listeners = new LinkedList<SingleInstanceListener>();
+
+ /**
+ * Implements a server that listens for arguments from new instances of this
+ * application
+ *
+ */
+ class SingleInstanceServer implements Runnable {
+
+ SingleInstanceLock lockFile = null;
+
+ public SingleInstanceServer(SingleInstanceLock lockFile) {
+ this.lockFile = lockFile;
+ }
+
+ public void run() {
+ ServerSocket listeningSocket = null;
+ try {
+ listeningSocket = new ServerSocket(0);
+ lockFile.createWithPort(listeningSocket.getLocalPort());
+
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Starting SingleInstanceServer on port" + listeningSocket);
+ }
+
+ while (true) {
+ try {
+ Socket communicationSocket = listeningSocket.accept();
+ ObjectInputStream ois = new ObjectInputStream(communicationSocket
+ .getInputStream());
+ String[] arguments = (String[]) ois.readObject();
+ notifySingleInstanceListeners(arguments);
+ } catch (Exception exception) {
+ // not much to do here...
+ exception.printStackTrace();
+ }
+
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (listeningSocket != null) {
+ try {
+ listeningSocket.close();
+ } catch (IOException e) {
+ // Give up.
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new XSingleInstanceService
+ */
+ protected XSingleInstanceService() {
+ }
+
+ /**
+ * Initialize the new SingleInstanceService
+ *
+ * @throws InstanceAlreadyExistsException if the instance already exists
+ */
+ public void initializeSingleInstance() {
+ if (!initialized) {
+ // this is called after the application has started. so safe to use
+ // JNLPRuntime.getApplication()
+ checkSingleInstanceRunning(JNLPRuntime.getApplication().getJNLPFile());
+ initialized = true;
+ SingleInstanceLock lockFile;
+ JNLPFile jnlpFile = JNLPRuntime.getApplication().getJNLPFile();
+ lockFile = new SingleInstanceLock(jnlpFile);
+ if (!lockFile.isValid()) {
+ startListeningServer(lockFile);
+ }
+ }
+
+ }
+
+ /**
+ * Check if another instance of this application is already running
+ *
+ * @param jnlpFile The {@link JNLPFile} that specifies the application
+ *
+ * @throws InstanceExistsException if an instance of this application
+ * already exists
+ */
+ public void checkSingleInstanceRunning(JNLPFile jnlpFile) {
+ SingleInstanceLock lockFile = new SingleInstanceLock(jnlpFile);
+ if (lockFile.isValid()) {
+ int port = lockFile.getPort();
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Lock file is valid (port=" + port + "). Exiting.");
+ }
+ try {
+ sendProgramArgumentsToExistingApplication(port, jnlpFile.getApplication()
+ .getArguments());
+ throw new InstanceExistsException(String.valueOf(port));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Start the listening server to accept arguments from new instances of
+ * applications
+ *
+ * @param lockFile
+ * the {@link SingleInstanceLock} that the server should use
+ */
+ private void startListeningServer(SingleInstanceLock lockFile) {
+ SingleInstanceServer server = new SingleInstanceServer(lockFile);
+ Thread serverThread = new Thread(server);
+ /*
+ * mark as daemon so the JVM can shutdown if the server is the only
+ * thread running
+ */
+ serverThread.setDaemon(true);
+ serverThread.start();
+ }
+
+ /**
+ * Send the arguments for this application to the main instance
+ *
+ * @param port the port at which the SingleInstanceServer is listening at
+ * @param arguments the new arguments
+ * @throws IOException on any io exception
+ */
+ private void sendProgramArgumentsToExistingApplication(int port, String[] arguments)
+ throws IOException {
+ try {
+ Socket serverCommunicationSocket = new Socket((String) null, port);
+ ObjectOutputStream argumentStream = new ObjectOutputStream(serverCommunicationSocket
+ .getOutputStream());
+ argumentStream.writeObject(arguments);
+ argumentStream.close();
+ serverCommunicationSocket.close();
+
+ } catch (UnknownHostException unknownHost) {
+ if (JNLPRuntime.isDebug()) {
+ System.out.println("Unable to find localhost");
+ }
+ throw new RuntimeException(unknownHost);
+ }
+ }
+
+ /**
+ * Notify any SingleInstanceListener with new arguments
+ *
+ * @param arguments the new arguments to the application
+ */
+ private void notifySingleInstanceListeners(String[] arguments) {
+ for (SingleInstanceListener listener : listeners) {
+ // TODO this proxy is privileged. should i worry about security in
+ // methods being called?
+ listener.newActivation(arguments);
+ }
+ }
+
+ /**
+ * Add the specified SingleInstanceListener
+ *
+ * @throws InstanceExistsException, which is likely to terminate the
+ * application but not guaranteed to
+ */
+ public void addSingleInstanceListener(SingleInstanceListener sil) {
+ initializeSingleInstance();
+
+ if (sil == null) {
+ return;
+ }
+
+ listeners.add(sil);
+ }
+
+ /**
+ * Remove the specified SingleInstanceListener
+ *
+ * @throws InstanceExistsException if an instance of this single instance
+ * application already exists
+ *
+ */
+ public void removeSingleInstanceListener(SingleInstanceListener sil) {
+ initializeSingleInstance();
+
+ if (sil == null) {
+ return;
+ }
+
+ listeners.remove(sil);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/services/package.html b/netx/net/sourceforge/jnlp/services/package.html
new file mode 100644
index 0000000..413b6f6
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/services/package.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+
+This package contains the classes that implement the standard
+services defined by the JNLP specification.
+
+<h2>Package Specification</h2>
+
+<ul>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/download-spec.html">JNLP specification</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+<li><a target="_top" href="http://jnlp.sourceforge.net/netx/">Netx JNLP Client</a>
+<li><a target="_top" href="http://java.sun.com/products/javawebstart/">Java Web Start JNLP Client</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>
+
+
diff --git a/netx/net/sourceforge/jnlp/tools/CharacterEncoder.java b/netx/net/sourceforge/jnlp/tools/CharacterEncoder.java
new file mode 100644
index 0000000..2d8af4f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/CharacterEncoder.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright 1995-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+
+/**
+ * This class defines the encoding half of character encoders.
+ * A character encoder is an algorithim for transforming 8 bit binary
+ * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text)
+ * for transmition over text channels such as e-mail and network news.
+ *
+ * The character encoders have been structured around a central theme
+ * that, in general, the encoded text has the form:
+ *
+ * <pre>
+ * [Buffer Prefix]
+ * [Line Prefix][encoded data atoms][Line Suffix]
+ * [Buffer Suffix]
+ * </pre>
+ *
+ * In the CharacterEncoder and CharacterDecoder classes, one complete
+ * chunk of data is referred to as a <i>buffer</i>. Encoded buffers
+ * are all text, and decoded buffers (sometimes just referred to as
+ * buffers) are binary octets.
+ *
+ * To create a custom encoder, you must, at a minimum, overide three
+ * abstract methods in this class.
+ * <DL>
+ * <DD>bytesPerAtom which tells the encoder how many bytes to
+ * send to encodeAtom
+ * <DD>encodeAtom which encodes the bytes sent to it as text.
+ * <DD>bytesPerLine which tells the encoder the maximum number of
+ * bytes per line.
+ * </DL>
+ *
+ * Several useful encoders have already been written and are
+ * referenced in the See Also list below.
+ *
+ * @author Chuck McManis
+ * @see CharacterDecoder;
+ * @see UCEncoder
+ * @see UUEncoder
+ * @see BASE64Encoder
+ */
+public abstract class CharacterEncoder {
+
+ /** Stream that understands "printing" */
+ protected PrintStream pStream;
+
+ /** Return the number of bytes per atom of encoding */
+ abstract protected int bytesPerAtom();
+
+ /** Return the number of bytes that can be encoded per line */
+ abstract protected int bytesPerLine();
+
+ /**
+ * Encode the prefix for the entire buffer. By default is simply
+ * opens the PrintStream for use by the other functions.
+ */
+ protected void encodeBufferPrefix(OutputStream aStream) throws IOException {
+ pStream = new PrintStream(aStream);
+ }
+
+ /**
+ * Encode the suffix for the entire buffer.
+ */
+ protected void encodeBufferSuffix(OutputStream aStream) throws IOException {
+ }
+
+ /**
+ * Encode the prefix that starts every output line.
+ */
+ protected void encodeLinePrefix(OutputStream aStream, int aLength)
+ throws IOException {
+ }
+
+ /**
+ * Encode the suffix that ends every output line. By default
+ * this method just prints a <newline> into the output stream.
+ */
+ protected void encodeLineSuffix(OutputStream aStream) throws IOException {
+ pStream.println();
+ }
+
+ /** Encode one "atom" of information into characters. */
+ abstract protected void encodeAtom(OutputStream aStream, byte someBytes[],
+ int anOffset, int aLength) throws IOException;
+
+ /**
+ * This method works around the bizarre semantics of BufferedInputStream's
+ * read method.
+ */
+ protected int readFully(InputStream in, byte buffer[])
+ throws java.io.IOException {
+ for (int i = 0; i < buffer.length; i++) {
+ int q = in.read();
+ if (q == -1)
+ return i;
+ buffer[i] = (byte)q;
+ }
+ return buffer.length;
+ }
+
+ /**
+ * Encode bytes from the input stream, and write them as text characters
+ * to the output stream. This method will run until it exhausts the
+ * input stream, but does not print the line suffix for a final
+ * line that is shorter than bytesPerLine().
+ */
+ public void encode(InputStream inStream, OutputStream outStream)
+ throws IOException {
+ int j;
+ int numBytes;
+ byte tmpbuffer[] = new byte[bytesPerLine()];
+
+ encodeBufferPrefix(outStream);
+
+ while (true) {
+ numBytes = readFully(inStream, tmpbuffer);
+ if (numBytes == 0) {
+ break;
+ }
+ encodeLinePrefix(outStream, numBytes);
+ for (j = 0; j < numBytes; j += bytesPerAtom()) {
+
+ if ((j + bytesPerAtom()) <= numBytes) {
+ encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
+ } else {
+ encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
+ }
+ }
+ if (numBytes < bytesPerLine()) {
+ break;
+ } else {
+ encodeLineSuffix(outStream);
+ }
+ }
+ encodeBufferSuffix(outStream);
+ }
+
+ /**
+ * Encode the buffer in <i>aBuffer</i> and write the encoded
+ * result to the OutputStream <i>aStream</i>.
+ */
+ public void encode(byte aBuffer[], OutputStream aStream)
+ throws IOException {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
+ encode(inStream, aStream);
+ }
+
+ /**
+ * A 'streamless' version of encode that simply takes a buffer of
+ * bytes and returns a string containing the encoded buffer.
+ */
+ public String encode(byte aBuffer[]) {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
+ String retVal = null;
+ try {
+ encode(inStream, outStream);
+ // explicit ascii->unicode conversion
+ retVal = outStream.toString("8859_1");
+ } catch (Exception IOException) {
+ // This should never happen.
+ throw new Error("CharacterEncoder.encode internal error");
+ }
+ return (retVal);
+ }
+
+ /**
+ * Return a byte array from the remaining bytes in this ByteBuffer.
+ * <P>
+ * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+ * <P>
+ * To avoid an extra copy, the implementation will attempt to return the
+ * byte array backing the ByteBuffer. If this is not possible, a
+ * new byte array will be created.
+ */
+ private byte [] getBytes(ByteBuffer bb) {
+ /*
+ * This should never return a BufferOverflowException, as we're
+ * careful to allocate just the right amount.
+ */
+ byte [] buf = null;
+
+ /*
+ * If it has a usable backing byte buffer, use it. Use only
+ * if the array exactly represents the current ByteBuffer.
+ */
+ if (bb.hasArray()) {
+ byte [] tmp = bb.array();
+ if ((tmp.length == bb.capacity()) &&
+ (tmp.length == bb.remaining())) {
+ buf = tmp;
+ bb.position(bb.limit());
+ }
+ }
+
+ if (buf == null) {
+ /*
+ * This class doesn't have a concept of encode(buf, len, off),
+ * so if we have a partial buffer, we must reallocate
+ * space.
+ */
+ buf = new byte[bb.remaining()];
+
+ /*
+ * position() automatically updated
+ */
+ bb.get(buf);
+ }
+
+ return buf;
+ }
+
+ /**
+ * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
+ * result to the OutputStream <i>aStream</i>.
+ * <P>
+ * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+ */
+ public void encode(ByteBuffer aBuffer, OutputStream aStream)
+ throws IOException {
+ byte [] buf = getBytes(aBuffer);
+ encode(buf, aStream);
+ }
+
+ /**
+ * A 'streamless' version of encode that simply takes a ByteBuffer
+ * and returns a string containing the encoded buffer.
+ * <P>
+ * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+ */
+ public String encode(ByteBuffer aBuffer) {
+ byte [] buf = getBytes(aBuffer);
+ return encode(buf);
+ }
+
+ /**
+ * Encode bytes from the input stream, and write them as text characters
+ * to the output stream. This method will run until it exhausts the
+ * input stream. It differs from encode in that it will add the
+ * line at the end of a final line that is shorter than bytesPerLine().
+ */
+ public void encodeBuffer(InputStream inStream, OutputStream outStream)
+ throws IOException {
+ int j;
+ int numBytes;
+ byte tmpbuffer[] = new byte[bytesPerLine()];
+
+ encodeBufferPrefix(outStream);
+
+ while (true) {
+ numBytes = readFully(inStream, tmpbuffer);
+ if (numBytes == 0) {
+ break;
+ }
+ encodeLinePrefix(outStream, numBytes);
+ for (j = 0; j < numBytes; j += bytesPerAtom()) {
+ if ((j + bytesPerAtom()) <= numBytes) {
+ encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
+ } else {
+ encodeAtom(outStream, tmpbuffer, j, (numBytes)- j);
+ }
+ }
+ encodeLineSuffix(outStream);
+ if (numBytes < bytesPerLine()) {
+ break;
+ }
+ }
+ encodeBufferSuffix(outStream);
+ }
+
+ /**
+ * Encode the buffer in <i>aBuffer</i> and write the encoded
+ * result to the OutputStream <i>aStream</i>.
+ */
+ public void encodeBuffer(byte aBuffer[], OutputStream aStream)
+ throws IOException {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
+ encodeBuffer(inStream, aStream);
+ }
+
+ /**
+ * A 'streamless' version of encode that simply takes a buffer of
+ * bytes and returns a string containing the encoded buffer.
+ */
+ public String encodeBuffer(byte aBuffer[]) {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
+ try {
+ encodeBuffer(inStream, outStream);
+ } catch (Exception IOException) {
+ // This should never happen.
+ throw new Error("CharacterEncoder.encodeBuffer internal error");
+ }
+ return (outStream.toString());
+ }
+
+ /**
+ * Encode the <i>aBuffer</i> ByteBuffer and write the encoded
+ * result to the OutputStream <i>aStream</i>.
+ * <P>
+ * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+ */
+ public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
+ throws IOException {
+ byte [] buf = getBytes(aBuffer);
+ encodeBuffer(buf, aStream);
+ }
+
+ /**
+ * A 'streamless' version of encode that simply takes a ByteBuffer
+ * and returns a string containing the encoded buffer.
+ * <P>
+ * The ByteBuffer's position will be advanced to ByteBuffer's limit.
+ */
+ public String encodeBuffer(ByteBuffer aBuffer) {
+ byte [] buf = getBytes(aBuffer);
+ return encodeBuffer(buf);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/tools/HexDumpEncoder.java b/netx/net/sourceforge/jnlp/tools/HexDumpEncoder.java
new file mode 100644
index 0000000..4f7c27f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/HexDumpEncoder.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import java.io.PrintStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * This class encodes a buffer into the classic: "Hexadecimal Dump" format of
+ * the past. It is useful for analyzing the contents of binary buffers.
+ * The format produced is as follows:
+ * <pre>
+ * xxxx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ................
+ * </pre>
+ * Where xxxx is the offset into the buffer in 16 byte chunks, followed
+ * by ascii coded hexadecimal bytes followed by the ASCII representation of
+ * the bytes or '.' if they are not valid bytes.
+ *
+ * @author Chuck McManis
+ */
+
+public class HexDumpEncoder extends CharacterEncoder {
+
+ private int offset;
+ private int thisLineLength;
+ private int currentByte;
+ private byte thisLine[] = new byte[16];
+
+ static void hexDigit(PrintStream p, byte x) {
+ char c;
+
+ c = (char) ((x >> 4) & 0xf);
+ if (c > 9)
+ c = (char) ((c-10) + 'A');
+ else
+ c = (char)(c + '0');
+ p.write(c);
+ c = (char) (x & 0xf);
+ if (c > 9)
+ c = (char)((c-10) + 'A');
+ else
+ c = (char)(c + '0');
+ p.write(c);
+ }
+
+ protected int bytesPerAtom() {
+ return (1);
+ }
+
+ protected int bytesPerLine() {
+ return (16);
+ }
+
+ protected void encodeBufferPrefix(OutputStream o) throws IOException {
+ offset = 0;
+ super.encodeBufferPrefix(o);
+ }
+
+ protected void encodeLinePrefix(OutputStream o, int len) throws IOException {
+ hexDigit(pStream, (byte)((offset >>> 8) & 0xff));
+ hexDigit(pStream, (byte)(offset & 0xff));
+ pStream.print(": ");
+ currentByte = 0;
+ thisLineLength = len;
+ }
+
+ protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException {
+ thisLine[currentByte] = buf[off];
+ hexDigit(pStream, buf[off]);
+ pStream.print(" ");
+ currentByte++;
+ if (currentByte == 8)
+ pStream.print(" ");
+ }
+
+ protected void encodeLineSuffix(OutputStream o) throws IOException {
+ if (thisLineLength < 16) {
+ for (int i = thisLineLength; i < 16; i++) {
+ pStream.print(" ");
+ if (i == 7)
+ pStream.print(" ");
+ }
+ }
+ pStream.print(" ");
+ for (int i = 0; i < thisLineLength; i++) {
+ if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) {
+ pStream.print(".");
+ } else {
+ pStream.write(thisLine[i]);
+ }
+ }
+ pStream.println();
+ offset += thisLineLength;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/tools/JarRunner.java b/netx/net/sourceforge/jnlp/tools/JarRunner.java
new file mode 100644
index 0000000..52e86da
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/JarRunner.java
@@ -0,0 +1,15 @@
+package net.sourceforge.jnlp.tools;
+
+import net.sourceforge.jnlp.tools.JarSigner;
+public class JarRunner {
+
+
+ public static void main(String[] args) throws Exception{
+
+
+ //JarSigner.main(args);
+ JarSigner js = new JarSigner();
+ js.verifyJar(args[0]);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/tools/JarSigner.java b/netx/net/sourceforge/jnlp/tools/JarSigner.java
new file mode 100644
index 0000000..05ba28f
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/JarSigner.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+import java.util.jar.*;
+import java.text.Collator;
+import java.text.MessageFormat;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertPath;
+import java.security.*;
+import sun.security.x509.*;
+import sun.security.util.*;
+
+import net.sourceforge.jnlp.*;
+import net.sourceforge.jnlp.cache.*;
+import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.security.*;
+
+/**
+ * <p>The jarsigner utility.
+ *
+ * @author Roland Schemers
+ * @author Jan Luehe
+ */
+
+public class JarSigner implements CertVerifier {
+
+ private static String R(String key) {
+ return JNLPRuntime.getMessage(key);
+ }
+
+ private static final Collator collator = Collator.getInstance();
+ static {
+ // this is for case insensitive string comparisions
+ collator.setStrength(Collator.PRIMARY);
+ }
+
+ private static final String META_INF = "META-INF/";
+
+ // prefix for new signature-related files in META-INF directory
+ private static final String SIG_PREFIX = META_INF + "SIG-";
+
+
+ private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
+
+ static final String VERSION = "1.0";
+
+ static final int IN_KEYSTORE = 0x01;
+ static final int IN_SCOPE = 0x02;
+
+ static enum verifyResult {UNSIGNED, SIGNED_OK, SIGNED_NOT_OK}
+
+ // signer's certificate chain (when composing)
+ X509Certificate[] certChain;
+
+ /*
+ * private key
+ */
+ PrivateKey privateKey;
+ KeyStore store;
+
+ IdentityScope scope;
+
+ String keystore; // key store file
+ boolean nullStream = false; // null keystore input stream (NONE)
+ boolean token = false; // token-based keystore
+ String jarfile; // jar file to sign
+ String alias; // alias to sign jar with
+ char[] storepass; // keystore password
+ boolean protectedPath; // protected authentication path
+ String storetype; // keystore type
+ String providerName; // provider name
+ Vector<String> providers = null; // list of providers
+ HashMap<String,String> providerArgs = new HashMap<String, String>(); // arguments for provider constructors
+ char[] keypass; // private key password
+ String sigfile; // name of .SF file
+ String sigalg; // name of signature algorithm
+ String digestalg = "SHA1"; // name of digest algorithm
+ String signedjar; // output filename
+ String tsaUrl; // location of the Timestamping Authority
+ String tsaAlias; // alias for the Timestamping Authority's certificate
+ boolean verify = false; // verify the jar
+ boolean verbose = false; // verbose output when signing/verifying
+ boolean showcerts = false; // show certs when verifying
+ boolean debug = false; // debug
+ boolean signManifest = true; // "sign" the whole manifest
+ boolean externalSF = true; // leave the .SF out of the PKCS7 block
+
+ private boolean hasExpiredCert = false;
+ private boolean hasExpiringCert = false;
+ private boolean notYetValidCert = false;
+
+ private boolean badKeyUsage = false;
+ private boolean badExtendedKeyUsage = false;
+ private boolean badNetscapeCertType = false;
+
+ private boolean alreadyTrustPublisher = false;
+ private boolean rootInCacerts = false;
+
+ /**
+ * The single certPath used in this JarSiging. We're only keeping
+ * track of one here, since in practice there's only one signer
+ * for a JNLP Application.
+ */
+ private CertPath certPath = null;
+
+ private boolean noSigningIssues = true;
+
+ private boolean anyJarsSigned = false;
+
+ /** all of the jar files that were verified */
+ private ArrayList<String> verifiedJars = null;
+
+ /** all of the jar files that were not verified */
+ private ArrayList<String> unverifiedJars = null;
+
+ /** the certificates used for jar verification */
+ private ArrayList<CertPath> certs = null;
+
+ /** details of this signing */
+ private ArrayList<String> details = new ArrayList<String>();
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getAlreadyTrustPublisher()
+ */
+ public boolean getAlreadyTrustPublisher() {
+ return alreadyTrustPublisher;
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getRootInCacerts()
+ */
+ public boolean getRootInCacerts() {
+ return rootInCacerts;
+ }
+
+ public CertPath getCertPath() {
+ return certPath;
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#hasSigningIssues()
+ */
+ public boolean hasSigningIssues() {
+ return hasExpiredCert || notYetValidCert || badKeyUsage
+ || badExtendedKeyUsage || badNetscapeCertType;
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#noSigningIssues()
+ */
+ public boolean noSigningIssues() {
+ return noSigningIssues;
+ }
+
+ public boolean anyJarsSigned() {
+ return anyJarsSigned;
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getDetails()
+ */
+ public ArrayList<String> getDetails() {
+ return details;
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getCerts()
+ */
+ public ArrayList<CertPath> getCerts() {
+ return certs;
+ }
+
+ public void verifyJars(List<JARDesc> jars, ResourceTracker tracker)
+ throws Exception {
+
+ certs = new ArrayList<CertPath>();
+ for (int i = 0; i < jars.size(); i++) {
+
+ JARDesc jar = (JARDesc) jars.get(i);
+ verifiedJars = new ArrayList<String>();
+ unverifiedJars = new ArrayList<String>();
+
+ try {
+
+ File jarFile = tracker.getCacheFile(jar.getLocation());
+
+ // some sort of resource download/cache error. Nothing to add
+ // in that case ... but don't fail here
+ if (jarFile == null) {
+ return;
+ }
+
+ String localFile = jarFile.getAbsolutePath();
+ verifyResult result = verifyJar(localFile);
+
+ if (result == verifyResult.UNSIGNED) {
+ unverifiedJars.add(localFile);
+ } else if (result == verifyResult.SIGNED_NOT_OK) {
+ noSigningIssues = false;
+ verifiedJars.add(localFile);
+ } else if (result == verifyResult.SIGNED_OK) {
+ verifiedJars.add(localFile);
+ }
+ } catch (Exception e){
+ // We may catch exceptions from using verifyJar()
+ // or from checkTrustedCerts
+ throw e;
+ }
+ }
+ }
+
+ public verifyResult verifyJar(String jarName) throws Exception {
+ boolean anySigned = false;
+ boolean hasUnsignedEntry = false;
+ JarFile jarFile = null;
+
+ // certs could be uninitialized if one calls this method directly
+ if (certs == null)
+ certs = new ArrayList<CertPath>();
+
+ try {
+ jarFile = new JarFile(jarName, true);
+ Vector<JarEntry> entriesVec = new Vector<JarEntry>();
+ byte[] buffer = new byte[8192];
+
+ JarEntry je;
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ je = entries.nextElement();
+ entriesVec.addElement(je);
+
+ InputStream is = jarFile.getInputStream(je);
+ try {
+ int n;
+ while ((n = is.read(buffer, 0, buffer.length)) != -1) {
+ // we just read. this will throw a SecurityException
+ // if a signature/digest check fails.
+ }
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ if (jarFile.getManifest() != null) {
+ if (verbose) System.out.println();
+ Enumeration<JarEntry> e = entriesVec.elements();
+
+ long now = System.currentTimeMillis();
+
+ while (e.hasMoreElements()) {
+ je = e.nextElement();
+ String name = je.getName();
+ CodeSigner[] signers = je.getCodeSigners();
+ boolean isSigned = (signers != null);
+ anySigned |= isSigned;
+ hasUnsignedEntry |= !je.isDirectory() && !isSigned
+ && !signatureRelated(name);
+ if (isSigned) {
+ // TODO: Perhaps we should check here that
+ // signers.length is only of size 1, and throw an
+ // exception if it's not?
+ for (int i = 0; i < signers.length; i++) {
+ CertPath certPath = signers[i].getSignerCertPath();
+ if (!certs.contains(certPath))
+ certs.add(certPath);
+
+ //we really only want the first certPath
+ if (!certPath.equals(this.certPath)){
+ this.certPath = certPath;
+ }
+
+ Certificate cert = signers[i].getSignerCertPath()
+ .getCertificates().get(0);
+ if (cert instanceof X509Certificate) {
+ checkCertUsage((X509Certificate)cert, null);
+ if (!showcerts) {
+ long notAfter = ((X509Certificate)cert)
+ .getNotAfter().getTime();
+
+ if (notAfter < now) {
+ hasExpiredCert = true;
+ } else if (notAfter < now + SIX_MONTHS) {
+ hasExpiringCert = true;
+ }
+ }
+ }
+ }
+ }
+ } //while e has more elements
+ } //if man not null
+
+ //Alert the user if any of the following are true.
+ if (!anySigned) {
+ return verifyResult.UNSIGNED;
+ } else {
+ anyJarsSigned = true;
+
+ //warnings
+ if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
+ badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+ notYetValidCert) {
+
+ addToDetails(R("SRunWithoutRestrictions"));
+
+ if (badKeyUsage)
+ addToDetails(R("SBadKeyUsage"));
+ if (badExtendedKeyUsage)
+ addToDetails(R("SBadExtendedKeyUsage"));
+ if (badNetscapeCertType)
+ addToDetails(R("SBadNetscapeCertType"));
+ if (hasUnsignedEntry)
+ addToDetails(R("SHasUnsignedEntry"));
+ if (hasExpiredCert)
+ addToDetails(R("SHasExpiredCert"));
+ if (hasExpiringCert)
+ addToDetails(R("SHasExpiringCert"));
+ if (notYetValidCert)
+ addToDetails(R("SNotYetValidCert"));
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ } finally { // close the resource
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ }
+
+ // check if the certs added above are in the trusted path
+ checkTrustedCerts();
+
+ //anySigned does not guarantee that all files were signed.
+ return (anySigned && !(hasUnsignedEntry || hasExpiredCert
+ || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType
+ || notYetValidCert)) ? verifyResult.SIGNED_OK : verifyResult.SIGNED_NOT_OK;
+ }
+
+ /**
+ * Checks the user's trusted.certs file and the cacerts file to see
+ * if a publisher's and/or CA's certificate exists there.
+ */
+ private void checkTrustedCerts() throws Exception {
+ if (certPath != null) {
+ try {
+ KeyTool kt = new KeyTool();
+ alreadyTrustPublisher = kt.isTrusted(getPublisher());
+ rootInCacerts = kt.checkCacertsForCertificate(getRoot());
+ } catch (Exception e) {
+ // TODO: Warn user about not being able to
+ // look through their cacerts/trusted.certs
+ // file depending on exception.
+ throw e;
+ }
+
+ if (!rootInCacerts)
+ addToDetails(R("SUntrustedCertificate"));
+ else
+ addToDetails(R("STrustedCertificate"));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getPublisher()
+ */
+ public Certificate getPublisher() {
+ if (certPath != null) {
+ List<? extends Certificate> certList
+ = certPath.getCertificates();
+ if (certList.size() > 0) {
+ return (Certificate)certList.get(0);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see net.sourceforge.jnlp.tools.CertVerifier2#getRoot()
+ */
+ public Certificate getRoot() {
+ if (certPath != null) {
+ List<? extends Certificate> certList
+ = certPath.getCertificates();
+ if (certList.size() > 0) {
+ return (Certificate)certList.get(
+ certList.size() - 1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private void addToDetails(String detail) {
+ if (!details.contains(detail))
+ details.add(detail);
+ }
+
+ Hashtable<Certificate, String> storeHash =
+ new Hashtable<Certificate, String>();
+
+ /**
+ * signature-related files include:
+ * . META-INF/MANIFEST.MF
+ * . META-INF/SIG-*
+ * . META-INF/*.SF
+ * . META-INF/*.DSA
+ * . META-INF/*.RSA
+ *
+ * Required for verifyJar()
+ */
+ private boolean signatureRelated(String name) {
+ String ucName = name.toUpperCase();
+ if (ucName.equals(JarFile.MANIFEST_NAME) ||
+ ucName.equals(META_INF) ||
+ (ucName.startsWith(SIG_PREFIX) &&
+ ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
+ return true;
+ }
+
+ if (ucName.startsWith(META_INF) &&
+ SignatureFileVerifier.isBlockOrSF(ucName)) {
+ // .SF/.DSA/.RSA files in META-INF subdirs
+ // are not considered signature-related
+ return (ucName.indexOf("/") == ucName.lastIndexOf("/"));
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if userCert is designed to be a code signer
+ * @param userCert the certificate to be examined
+ * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
+ * NetscapeCertType has codeSigning flag turned on.
+ * If null, the class field badKeyUsage, badExtendedKeyUsage,
+ * badNetscapeCertType will be set.
+ *
+ * Required for verifyJar()
+ */
+ void checkCertUsage(X509Certificate userCert, boolean[] bad) {
+
+ // Can act as a signer?
+ // 1. if KeyUsage, then [0] should be true
+ // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
+ // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
+ // 1,2,3 must be true
+
+ if (bad != null) {
+ bad[0] = bad[1] = bad[2] = false;
+ }
+
+ boolean[] keyUsage = userCert.getKeyUsage();
+ if (keyUsage != null) {
+ if (keyUsage.length < 1 || !keyUsage[0]) {
+ if (bad != null) {
+ bad[0] = true;
+ } else {
+ badKeyUsage = true;
+ }
+ }
+ }
+
+ try {
+ List<String> xKeyUsage = userCert.getExtendedKeyUsage();
+ if (xKeyUsage != null) {
+ if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
+ && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
+ if (bad != null) {
+ bad[1] = true;
+ } else {
+ badExtendedKeyUsage = true;
+ }
+ }
+ }
+ } catch (java.security.cert.CertificateParsingException e) {
+ // shouldn't happen
+ }
+
+ try {
+ // OID_NETSCAPE_CERT_TYPE
+ byte[] netscapeEx = userCert.getExtensionValue
+ ("2.16.840.1.113730.1.1");
+ if (netscapeEx != null) {
+ DerInputStream in = new DerInputStream(netscapeEx);
+ byte[] encoded = in.getOctetString();
+ encoded = new DerValue(encoded).getUnalignedBitString()
+ .toByteArray();
+
+ NetscapeCertTypeExtension extn =
+ new NetscapeCertTypeExtension(encoded);
+
+ Boolean val = (Boolean)extn.get(
+ NetscapeCertTypeExtension.OBJECT_SIGNING);
+ if (!val) {
+ if (bad != null) {
+ bad[2] = true;
+ } else {
+ badNetscapeCertType = true;
+ }
+ }
+ }
+ } catch (IOException e) {
+ //
+ }
+ }
+
+
+ /**
+ * Returns if all jars are signed.
+ *
+ * @return True if all jars are signed, false if there are one or more unsigned jars
+ */
+ public boolean allJarsSigned() {
+ return this.unverifiedJars.size() == 0;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/tools/JarSignerResources.java b/netx/net/sourceforge/jnlp/tools/JarSignerResources.java
new file mode 100644
index 0000000..50655ab
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/JarSignerResources.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+/**
+ * <p> This class represents the <code>ResourceBundle</code>
+ * for JarSigner.
+ *
+ */
+public class JarSignerResources extends java.util.ListResourceBundle {
+
+ private static final Object[][] contents = {
+
+ // shared (from jarsigner)
+ {" ", " "},
+ {" ", " "},
+ {" ", " "},
+ {", ", ", "},
+
+ {"provName not a provider", "{0} not a provider"},
+ {"signerClass is not a signing mechanism", "{0} is not a signing mechanism"},
+ {"jarsigner error: ", "jarsigner error: "},
+ {"Illegal option: ", "Illegal option: "},
+ {"-keystore must be NONE if -storetype is {0}",
+ "-keystore must be NONE if -storetype is {0}"},
+ {"-keypass can not be specified if -storetype is {0}",
+ "-keypass can not be specified if -storetype is {0}"},
+ {"If -protected is specified, then -storepass and -keypass must not be specified",
+ "If -protected is specified, then -storepass and -keypass must not be specified"},
+ {"If keystore is not password protected, then -storepass and -keypass must not be specified",
+ "If keystore is not password protected, then -storepass and -keypass must not be specified"},
+ {"Usage: jarsigner [options] jar-file alias",
+ "Usage: jarsigner [options] jar-file alias"},
+ {" jarsigner -verify [options] jar-file",
+ " jarsigner -verify [options] jar-file"},
+ {"[-keystore <url>] keystore location",
+ "[-keystore <url>] keystore location"},
+ {"[-storepass <password>] password for keystore integrity",
+ "[-storepass <password>] password for keystore integrity"},
+ {"[-storetype <type>] keystore type",
+ "[-storetype <type>] keystore type"},
+ {"[-keypass <password>] password for private key (if different)",
+ "[-keypass <password>] password for private key (if different)"},
+ {"[-sigfile <file>] name of .SF/.DSA file",
+ "[-sigfile <file>] name of .SF/.DSA file"},
+ {"[-signedjar <file>] name of signed JAR file",
+ "[-signedjar <file>] name of signed JAR file"},
+ {"[-digestalg <algorithm>] name of digest algorithm",
+ "[-digestalg <algorithm>] name of digest algorithm"},
+ {"[-sigalg <algorithm>] name of signature algorithm",
+ "[-sigalg <algorithm>] name of signature algorithm"},
+ {"[-verify] verify a signed JAR file",
+ "[-verify] verify a signed JAR file"},
+ {"[-verbose] verbose output when signing/verifying",
+ "[-verbose] verbose output when signing/verifying"},
+ {"[-certs] display certificates when verbose and verifying",
+ "[-certs] display certificates when verbose and verifying"},
+ {"[-tsa <url>] location of the Timestamping Authority",
+ "[-tsa <url>] location of the Timestamping Authority"},
+ {"[-tsacert <alias>] public key certificate for Timestamping Authority",
+ "[-tsacert <alias>] public key certificate for Timestamping Authority"},
+ {"[-altsigner <class>] class name of an alternative signing mechanism",
+ "[-altsigner <class>] class name of an alternative signing mechanism"},
+ {"[-altsignerpath <pathlist>] location of an alternative signing mechanism",
+ "[-altsignerpath <pathlist>] location of an alternative signing mechanism"},
+ {"[-internalsf] include the .SF file inside the signature block",
+ "[-internalsf] include the .SF file inside the signature block"},
+ {"[-sectionsonly] don't compute hash of entire manifest",
+ "[-sectionsonly] don't compute hash of entire manifest"},
+ {"[-protected] keystore has protected authentication path",
+ "[-protected] keystore has protected authentication path"},
+ {"[-providerName <name>] provider name",
+ "[-providerName <name>] provider name"},
+ {"[-providerClass <class> name of cryptographic service provider's",
+ "[-providerClass <class> name of cryptographic service provider's"},
+ {" [-providerArg <arg>]] ... master class file and constructor argument",
+ " [-providerArg <arg>]] ... master class file and constructor argument"},
+ {"s", "s"},
+ {"m", "m"},
+ {"k", "k"},
+ {"i", "i"},
+ {" s = signature was verified ",
+ " s = signature was verified "},
+ {" m = entry is listed in manifest",
+ " m = entry is listed in manifest"},
+ {" k = at least one certificate was found in keystore",
+ " k = at least one certificate was found in keystore"},
+ {" i = at least one certificate was found in identity scope",
+ " i = at least one certificate was found in identity scope"},
+ {"no manifest.", "no manifest."},
+ {"jar is unsigned. (signatures missing or not parsable)",
+ "jar is unsigned. (signatures missing or not parsable)"},
+ {"jar verified.", "jar verified."},
+ {"jarsigner: ", "jarsigner: "},
+ {"signature filename must consist of the following characters: A-Z, 0-9, _ or -",
+ "signature filename must consist of the following characters: A-Z, 0-9, _ or -"},
+ {"unable to open jar file: ", "unable to open jar file: "},
+ {"unable to create: ", "unable to create: "},
+ {" adding: ", " adding: "},
+ {" updating: ", " updating: "},
+ {" signing: ", " signing: "},
+ {"attempt to rename signedJarFile to jarFile failed",
+ "attempt to rename {0} to {1} failed"},
+ {"attempt to rename jarFile to origJar failed",
+ "attempt to rename {0} to {1} failed"},
+ {"unable to sign jar: ", "unable to sign jar: "},
+ {"Enter Passphrase for keystore: ", "Enter Passphrase for keystore: "},
+ {"keystore load: ", "keystore load: "},
+ {"certificate exception: ", "certificate exception: "},
+ {"unable to instantiate keystore class: ",
+ "unable to instantiate keystore class: "},
+ {"Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.",
+ "Certificate chain not found for: {0}. {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."},
+ {"found non-X.509 certificate in signer's chain",
+ "found non-X.509 certificate in signer's chain"},
+ {"incomplete certificate chain", "incomplete certificate chain"},
+ {"Enter key password for alias: ", "Enter key password for {0}: "},
+ {"unable to recover key from keystore",
+ "unable to recover key from keystore"},
+ {"key associated with alias not a private key",
+ "key associated with {0} not a private key"},
+ {"you must enter key password", "you must enter key password"},
+ {"unable to read password: ", "unable to read password: "},
+ {"certificate is valid from", "certificate is valid from {0} to {1}"},
+ {"certificate expired on", "certificate expired on {0}"},
+ {"certificate is not valid until",
+ "certificate is not valid until {0}"},
+ {"certificate will expire on", "certificate will expire on {0}"},
+ {"requesting a signature timestamp",
+ "requesting a signature timestamp"},
+ {"TSA location: ", "TSA location: "},
+ {"TSA certificate: ", "TSA certificate: "},
+ {"no response from the Timestamping Authority. ",
+ "no response from the Timestamping Authority. "},
+ {"When connecting from behind a firewall then an HTTP proxy may need to be specified. ",
+ "When connecting from behind a firewall then an HTTP proxy may need to be specified. "},
+ {"Supply the following options to jarsigner: ",
+ "Supply the following options to jarsigner: "},
+ {"Certificate not found for: alias. alias must reference a valid KeyStore entry containing an X.509 public key certificate for the Timestamping Authority.",
+ "Certificate not found for: {0}. {1} must reference a valid KeyStore entry containing an X.509 public key certificate for the Timestamping Authority."},
+ {"using an alternative signing mechanism",
+ "using an alternative signing mechanism"},
+ {"entry was signed on", "entry was signed on {0}"},
+ {"Warning: ", "Warning: "},
+ {"This jar contains unsigned entries which have not been integrity-checked. ",
+ "This jar contains unsigned entries which have not been integrity-checked. "},
+ {"This jar contains entries whose signer certificate has expired. ",
+ "This jar contains entries whose signer certificate has expired. "},
+ {"This jar contains entries whose signer certificate will expire within six months. ",
+ "This jar contains entries whose signer certificate will expire within six months. "},
+ {"This jar contains entries whose signer certificate is not yet valid. ",
+ "This jar contains entries whose signer certificate is not yet valid. "},
+ {"Re-run with the -verbose option for more details.",
+ "Re-run with the -verbose option for more details."},
+ {"Re-run with the -verbose and -certs options for more details.",
+ "Re-run with the -verbose and -certs options for more details."},
+ {"The signer certificate has expired.",
+ "The signer certificate has expired."},
+ {"The signer certificate will expire within six months.",
+ "The signer certificate will expire within six months."},
+ {"The signer certificate is not yet valid.",
+ "The signer certificate is not yet valid."},
+ {"The signer certificate's KeyUsage extension doesn't allow code signing.",
+ "The signer certificate's KeyUsage extension doesn't allow code signing."},
+ {"The signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
+ "The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
+ {"The signer certificate's NetscapeCertType extension doesn't allow code signing.",
+ "The signer certificate's NetscapeCertType extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
+ {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
+ "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
+ {"[{0} extension does not support code signing]",
+ "[{0} extension does not support code signing]"},
+ };
+
+ /**
+ * Returns the contents of this <code>ResourceBundle</code>.
+ *
+ * <p>
+ *
+ * @return the contents of this <code>ResourceBundle</code>.
+ */
+ public Object[][] getContents() {
+ return contents;
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/tools/KeyStoreUtil.java b/netx/net/sourceforge/jnlp/tools/KeyStoreUtil.java
new file mode 100644
index 0000000..5f497ef
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/KeyStoreUtil.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+/**
+ * <p> This class provides several utilities to <code>KeyStore</code>.
+ *
+ * @since 1.6.0
+ */
+public class KeyStoreUtil {
+
+ // Class and methods marked as public so that they can be
+ // accessed by JarSigner, which although lies in a package
+ // with the same name, but bundled in tools.jar and loaded
+ // by another class loader, hence in a different *runtime*
+ // package.
+ //
+ // See JVM Spec, 5.3 and 5.4.4
+
+ private KeyStoreUtil() {
+ // this class is not meant to be instantiated
+ }
+
+
+ /**
+ * Returns true if KeyStore has a password. This is true except for
+ * MSCAPI KeyStores
+ */
+ public static boolean isWindowsKeyStore(String storetype) {
+ return storetype.equalsIgnoreCase("Windows-MY")
+ || storetype.equalsIgnoreCase("Windows-ROOT");
+ }
+
+ /**
+ * Returns standard-looking names for storetype
+ */
+ public static String niceStoreTypeName(String storetype) {
+ if (storetype.equalsIgnoreCase("Windows-MY")) {
+ return "Windows-MY";
+ } else if(storetype.equalsIgnoreCase("Windows-ROOT")) {
+ return "Windows-ROOT";
+ } else {
+ return storetype.toUpperCase();
+ }
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/tools/KeyTool.java b/netx/net/sourceforge/jnlp/tools/KeyTool.java
new file mode 100644
index 0000000..f7780c4
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/tools/KeyTool.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package net.sourceforge.jnlp.tools;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import net.sourceforge.jnlp.security.SecurityUtil;
+
+import sun.misc.BASE64Encoder;
+import sun.security.provider.X509Factory;
+
+/**
+ * This tool manages the user's trusted certificates
+ *
+ * @author Jan Luehe
+ * @author Joshua Sumali
+ */
+public class KeyTool {
+
+ // The user's keystore.
+ private KeyStore usercerts = null;
+ // JDK cacerts
+ private KeyStore cacerts = null;
+ // System ca-bundle.crt
+ private KeyStore systemcerts = null;
+
+ private String fullCertPath = SecurityUtil.getTrustedCertsFilename();
+
+ private FileOutputStream fos = null;
+
+ /**
+ * Whether we trust the system cacerts file.
+ */
+ private boolean trustcacerts = true;
+
+ /**
+ * Whether we print certificates in rfc, base64 encoding.
+ */
+ private boolean rfc = true;
+
+ private final char[] password = "changeit".toCharArray();
+
+ /**
+ * Whether we prompt for user input.
+ */
+ private boolean noprompt = true;
+
+ public KeyTool() throws Exception {
+
+ // Initialize all the keystores.
+ usercerts = SecurityUtil.getUserKeyStore();
+ cacerts = SecurityUtil.getCacertsKeyStore();
+ systemcerts = SecurityUtil.getSystemCertStore();
+ }
+
+ /**
+ * Adds a trusted certificate to the user's keystore.
+ * @return true if the add was successful, false otherwise.
+ */
+ public boolean importCert(File file) throws Exception {
+
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
+ CertificateFactory cf = CertificateFactory.getInstance("X509");
+ X509Certificate cert = null;
+
+ if (bis.available() >= 1) {
+ try {
+ cert = (X509Certificate)cf.generateCertificate(bis);
+ } catch (ClassCastException cce) {
+ throw new Exception("Input file is not an X509 Certificate");
+ } catch (CertificateException ce) {
+ throw new Exception("Input file is not an X509 Certificate");
+ }
+ }
+
+ return importCert((Certificate)cert);
+ }
+
+ /**
+ * Adds a trusted certificate to the user's keystore.
+ * @return true if the add was successful, false otherwise.
+ */
+ public boolean importCert(Certificate cert) throws Exception {
+
+ String alias = usercerts.getCertificateAlias(cert);
+
+ if (alias != null) { //cert already exists
+ return true;
+ } else {
+ String newAlias = getRandomAlias();
+ //check to make sure this alias doesn't exist
+ while (usercerts.getCertificate(newAlias) != null)
+ newAlias = getRandomAlias();
+ return addTrustedCert(newAlias, cert);
+ }
+ }
+
+ /**
+ * Generates a random alias for storing a trusted Certificate.
+ */
+ private String getRandomAlias() {
+ Random r = new Random();
+ String token = Long.toString(Math.abs(r.nextLong()), 36);
+ return "trustedCert-" + token;
+ }
+
+ /**
+ * Prints all keystore entries.
+ */
+ private void doPrintEntries(PrintStream out) throws Exception {
+
+ out.println("KeyStore type: " + usercerts.getType());
+ out.println("KeyStore provider: " + usercerts.getProvider().toString());
+ out.println();
+
+ for (Enumeration<String> e = usercerts.aliases(); e.hasMoreElements();) {
+ String alias = e.nextElement();
+ doPrintEntry(alias, out, false);
+ }
+ }
+
+ /**
+ * Prints a single keystore entry.
+ */
+ private void doPrintEntry(String alias, PrintStream out,
+ boolean printWarning) throws Exception {
+
+ if (usercerts.containsAlias(alias) == false) {
+ throw new Exception("Alias does not exist");
+ }
+
+ if (usercerts.entryInstanceOf(alias,
+ KeyStore.TrustedCertificateEntry.class)) {
+ Certificate cert = usercerts.getCertificate(alias);
+
+ out.println("Alias: " + alias);
+ out.println("Date Created: " + usercerts.getCreationDate(alias));
+ out.println("Subject: " + SecurityUtil.getCN(((X509Certificate)usercerts
+ .getCertificate(alias)).getSubjectX500Principal().getName()));
+ out.println("Certificate fingerprint (MD5): "
+ + getCertFingerPrint("MD5", cert));
+ out.println();
+ }
+ }
+
+ /**
+ * Gets the requested finger print of the certificate.
+ */
+ private String getCertFingerPrint(String mdAlg, Certificate cert)
+ throws Exception {
+ byte[] encCertInfo = cert.getEncoded();
+ MessageDigest md = MessageDigest.getInstance(mdAlg);
+ byte[] digest = md.digest(encCertInfo);
+ return toHexString(digest);
+ }
+
+ /**
+ * Converts a byte to hex digit and writes to the supplied buffer
+ */
+ private void byte2hex(byte b, StringBuffer buf) {
+ char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ int high = ((b & 0xf0) >> 4);
+ int low = (b & 0x0f);
+ buf.append(hexChars[high]);
+ buf.append(hexChars[low]);
+ }
+
+ /**
+ * Converts a byte array to hex string
+ */
+ private String toHexString(byte[] block) {
+ StringBuffer buf = new StringBuffer();
+ int len = block.length;
+ for (int i = 0; i < len; i++) {
+ byte2hex(block[i], buf);
+ if (i < len-1) {
+ buf.append(":");
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Adds a certificate to the keystore, and writes new keystore to disk.
+ */
+ private boolean addTrustedCert(String alias, Certificate cert)
+ throws Exception {
+
+ if (isSelfSigned((X509Certificate)cert)) {
+ //will throw exception if this fails
+ cert.verify(cert.getPublicKey());
+ }
+
+ if (noprompt) {
+ usercerts.setCertificateEntry(alias, cert);
+ fos = new FileOutputStream(fullCertPath);
+ usercerts.store(fos, password);
+ fos.close();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the given certificate is trusted, false otherwise.
+ */
+ public boolean isTrusted(Certificate cert) throws Exception {
+ if (cert != null) {
+ if (usercerts.getCertificateAlias(cert) != null) {
+ return true; // found in own keystore
+ }
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the certificate is self-signed, false otherwise.
+ */
+ private boolean isSelfSigned(X509Certificate cert) {
+ return cert.getSubjectDN().equals(cert.getIssuerDN());
+ }
+
+ /**
+ * Checks if a given certificate is part of the user's cacerts
+ * keystore.
+ * @param c the certificate to check
+ * @returns true if the certificate is in the user's cacerts and
+ * false otherwise
+ */
+ public boolean checkCacertsForCertificate(Certificate c) throws Exception {
+ if (c != null) {
+
+ String alias = null;
+
+ //first try jdk cacerts.
+ if (cacerts != null) {
+ alias = cacerts.getCertificateAlias(c);
+
+ //if we can't find it here, try the system certs.
+ if (alias == null && systemcerts != null)
+ alias = systemcerts.getCertificateAlias(c);
+ }
+ //otherwise try the system certs if you can't use the jdk certs.
+ else if (systemcerts != null)
+ alias = systemcerts.getCertificateAlias(c);
+
+ return (alias != null);
+ } else
+ return false;
+ }
+
+ /**
+ * Establishes a certificate chain (using trusted certificates in the
+ * keystore), starting with the user certificate
+ * and ending at a self-signed certificate found in the keystore.
+ *
+ * @param userCert the user certificate of the alias
+ * @param certToVerify the single certificate provided in the reply
+ */
+ public boolean establishCertChain(Certificate userCert,
+ Certificate certToVerify)
+ throws Exception
+ {
+ if (userCert != null) {
+ // Make sure that the public key of the certificate reply matches
+ // the original public key in the keystore
+ PublicKey origPubKey = userCert.getPublicKey();
+ PublicKey replyPubKey = certToVerify.getPublicKey();
+ if (!origPubKey.equals(replyPubKey)) {
+ // TODO: something went wrong -- throw exception
+ throw new Exception(
+ "Public keys in reply and keystore don't match");
+ }
+
+ // If the two certs are identical, we're done: no need to import
+ // anything
+ if (certToVerify.equals(userCert)) {
+ throw new Exception(
+ "Certificate reply and certificate in keystore are identical");
+ }
+ }
+
+ // Build a hash table of all certificates in the keystore.
+ // Use the subject distinguished name as the key into the hash table.
+ // All certificates associated with the same subject distinguished
+ // name are stored in the same hash table entry as a vector.
+ Hashtable<Principal, Vector<Certificate>> certs = null;
+ if (usercerts.size() > 0) {
+ certs = new Hashtable<Principal, Vector<Certificate>>(11);
+ keystorecerts2Hashtable(usercerts, certs);
+ }
+ if (trustcacerts) { //if we're trusting the cacerts
+ KeyStore caks = SecurityUtil.getCacertsKeyStore();
+ if (caks!=null && caks.size()>0) {
+ if (certs == null) {
+ certs = new Hashtable<Principal, Vector<Certificate>>(11);
+ }
+ keystorecerts2Hashtable(caks, certs);
+ }
+ }
+
+ // start building chain
+ Vector<Certificate> chain = new Vector<Certificate>(2);
+ if (buildChain((X509Certificate)certToVerify, chain, certs)) {
+ Certificate[] newChain = new Certificate[chain.size()];
+ // buildChain() returns chain with self-signed root-cert first and
+ // user-cert last, so we need to invert the chain before we store
+ // it
+ int j=0;
+ for (int i=chain.size()-1; i>=0; i--) {
+ newChain[j] = chain.elementAt(i);
+ j++;
+ }
+ //return newChain;
+ return newChain != null;
+ } else {
+ throw new Exception("Failed to establish chain from reply");
+ }
+ }
+
+ /**
+ * Stores the (leaf) certificates of a keystore in a hashtable.
+ * All certs belonging to the same CA are stored in a vector that
+ * in turn is stored in the hashtable, keyed by the CA's subject DN
+ */
+ private void keystorecerts2Hashtable(KeyStore ks,
+ Hashtable<Principal, Vector<Certificate>> hash)
+ throws Exception {
+
+ for (Enumeration<String> aliases = ks.aliases();
+ aliases.hasMoreElements(); ) {
+ String alias = aliases.nextElement();
+ Certificate cert = ks.getCertificate(alias);
+ if (cert != null) {
+ Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
+ Vector<Certificate> vec = hash.get(subjectDN);
+ if (vec == null) {
+ vec = new Vector<Certificate>();
+ vec.addElement(cert);
+ } else {
+ if (!vec.contains(cert)) {
+ vec.addElement(cert);
+ }
+ }
+ hash.put(subjectDN, vec);
+ }
+ }
+ }
+
+ /**
+ * Recursively tries to establish chain from pool of trusted certs.
+ *
+ * @param certToVerify the cert that needs to be verified.
+ * @param chain the chain that's being built.
+ * @param certs the pool of trusted certs
+ *
+ * @return true if successful, false otherwise.
+ */
+ private boolean buildChain(X509Certificate certToVerify,
+ Vector<Certificate> chain,
+ Hashtable<Principal, Vector<Certificate>> certs) {
+ Principal subject = certToVerify.getSubjectDN();
+ Principal issuer = certToVerify.getIssuerDN();
+ if (subject.equals(issuer)) {
+ // reached self-signed root cert;
+ // no verification needed because it's trusted.
+ chain.addElement(certToVerify);
+ return true;
+ }
+
+ // Get the issuer's certificate(s)
+ Vector<Certificate> vec = certs.get(issuer);
+ if (vec == null) {
+ return false;
+ }
+
+ // Try out each certificate in the vector, until we find one
+ // whose public key verifies the signature of the certificate
+ // in question.
+ for (Enumeration<Certificate> issuerCerts = vec.elements();
+ issuerCerts.hasMoreElements(); ) {
+ X509Certificate issuerCert
+ = (X509Certificate)issuerCerts.nextElement();
+ PublicKey issuerPubKey = issuerCert.getPublicKey();
+ try {
+ certToVerify.verify(issuerPubKey);
+ } catch (Exception e) {
+ continue;
+ }
+ if (buildChain(issuerCert, chain, certs)) {
+ chain.addElement(certToVerify);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void dumpCert(Certificate cert, PrintStream out)
+ throws IOException, CertificateException {
+
+ boolean printRfc = true;
+ if (printRfc) {
+ BASE64Encoder encoder = new BASE64Encoder();
+ out.println(X509Factory.BEGIN_CERT);
+ encoder.encodeBuffer(cert.getEncoded(), out);
+ out.println(X509Factory.END_CERT);
+ } else {
+ out.write(cert.getEncoded()); // binary
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ KeyTool kt = new KeyTool();
+ kt.doPrintEntries(System.out);
+ }
+}
diff --git a/netx/net/sourceforge/jnlp/util/FileUtils.java b/netx/net/sourceforge/jnlp/util/FileUtils.java
new file mode 100644
index 0000000..aa1c316
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/FileUtils.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.util;
+
+import java.io.File;
+import java.io.IOException;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * This class contains a few file-related utility functions.
+ *
+ * @author Omair Majid
+ */
+
+public final class FileUtils {
+
+ /**
+ * list of characters not allowed in filenames
+ */
+ private static final char INVALID_CHARS[] = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' };
+
+ private static final char SANITIZED_CHAR = '_';
+
+ /**
+ * Clean up a string by removing characters that can't appear in a local
+ * file name.
+ *
+ * @param path
+ * the path to sanitize
+ * @return a sanitized version of the input which is suitable for using as a
+ * file path
+ */
+ public static String sanitizePath(String path) {
+
+ for (int i = 0; i < INVALID_CHARS.length; i++)
+ if (INVALID_CHARS[i] != File.separatorChar)
+ if (-1 != path.indexOf(INVALID_CHARS[i]))
+ path = path.replace(INVALID_CHARS[i], SANITIZED_CHAR);
+
+ return path;
+ }
+
+ /**
+ * Given an input, return a sanitized form of the input suitable for use as
+ * a file/directory name
+ *
+ * @param input
+ * @return a sanitized version of the input
+ */
+ public static String sanitizeFileName(String filename) {
+
+ for (int i = 0; i < INVALID_CHARS.length; i++)
+ if (-1 != filename.indexOf(INVALID_CHARS[i]))
+ filename = filename.replace(INVALID_CHARS[i], SANITIZED_CHAR);
+
+ return filename;
+ }
+
+ /**
+ * Returns a String that is suitable for using in GUI elements for
+ * displaying (long) paths to users.
+ *
+ * @param path a path that should be shortened
+ * @return a shortened path suitable for displaying to the user
+ */
+ public static String displayablePath(String path) {
+ final int DEFAULT_LENGTH = 40;
+ return displayablePath(path, DEFAULT_LENGTH);
+ }
+
+ /**
+ * Return a String that is suitable for using in GUI elements for displaying
+ * paths to users. If the path is longer than visibleChars, it is truncated
+ * in a display-friendly way
+ *
+ * @param path a path that should be shorted
+ * @param visibleChars the maximum number of characters that path should fit
+ * into. Also the length of the returned string
+ * @return a shortened path that contains limited number of chars
+ */
+ public static String displayablePath(String path, int visibleChars) {
+ /*
+ * use a very simple method: prefix + "..." + suffix
+ *
+ * where prefix is the beginning part of path (as much as we can squeeze in)
+ * and suffix is the end path of path
+ */
+
+ if (path == null || path.length() <= visibleChars) {
+ return path;
+ }
+
+ final String OMITTED = "...";
+ final int OMITTED_LENGTH = OMITTED.length();
+ final int MIN_PREFIX_LENGTH = 4;
+ final int MIN_SUFFIX_LENGTH = 4;
+ /*
+ * we want to show things other than OMITTED. if we have too few for
+ * suffix and prefix, then just return as much as we can of the filename
+ */
+ if (visibleChars < (OMITTED_LENGTH + MIN_PREFIX_LENGTH + MIN_SUFFIX_LENGTH)) {
+ return path.substring(path.length() - visibleChars);
+ }
+
+ int affixLength = (visibleChars - OMITTED_LENGTH)/2;
+ String prefix = path.substring(0, affixLength);
+ String suffix = path.substring(path.length() - affixLength);
+
+ return prefix + OMITTED + suffix;
+ }
+
+ /**
+ * Recursively delete everything under a directory. Works on either files or
+ * directories
+ *
+ * @param file the file object representing what to delete. Can be either a
+ * file or a directory.
+ * @param base the directory under which the file and its subdirectories must be located
+ * @throws IOException on an io exception or if trying to delete something
+ * outside the base
+ */
+ public static void recursiveDelete(File file, File base) throws IOException {
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Deleting: " + file);
+ }
+
+ if (!(file.getCanonicalPath().startsWith(base.getCanonicalPath()))) {
+ throw new IOException("Trying to delete a file outside Netx's basedir: "
+ + file.getCanonicalPath());
+ }
+
+ if (file.isDirectory()) {
+ File[] children = file.listFiles();
+ for (int i = 0; i < children.length; i++) {
+ recursiveDelete(children[i], base);
+ }
+ }
+ if (!file.delete()) {
+ throw new IOException("Unable to delete file: " + file);
+ }
+
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/util/PropertiesFile.java b/netx/net/sourceforge/jnlp/util/PropertiesFile.java
new file mode 100644
index 0000000..80a7ec2
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/PropertiesFile.java
@@ -0,0 +1,146 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.util;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.*;
+
+/**
+ * A properties object backed by a specified file without throwing
+ * exceptions. The properties are automatically loaded from the
+ * file when the first property is requested, but the save method
+ * must be called before changes are saved to the file.<p>
+ *
+ * This class does not report IO exceptions.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.4 $
+ */
+public class PropertiesFile extends Properties {
+
+ /** the file to save to */
+ File file;
+
+ /** the header string */
+ String header = "netx file";
+
+ /** lazy loaded on getProperty */
+ boolean loaded = false;
+
+
+ /**
+ * Create a properties object backed by the specified file.
+ *
+ * @param file the file to save and load to
+ */
+ public PropertiesFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Create a properties object backed by the specified file.
+ *
+ * @param file the file to save and load to
+ * @param header the file header
+ */
+ public PropertiesFile(File file, String header) {
+ this.file = file;
+ this.header = header;
+ }
+
+ /**
+ * Returns the value of the specified key, or null if the key
+ * does not exist.
+ */
+ public String getProperty(String key) {
+ if (!loaded)
+ load();
+
+ return super.getProperty(key);
+ }
+
+ /**
+ * Returns the value of the specified key, or the default value
+ * if the key does not exist.
+ */
+ public String getProperty(String key, String defaultValue) {
+ if (!loaded)
+ load();
+
+ return super.getProperty(key, defaultValue);
+ }
+
+ /**
+ * Sets the value for the specified key.
+ *
+ * @return the previous value
+ */
+ public Object setProperty(String key, String value) {
+ if (!loaded)
+ load();
+
+ return super.setProperty(key, value);
+ }
+
+ /**
+ * Returns the file backing this properties object.
+ */
+ public File getStoreFile() {
+ return file;
+ }
+
+ /**
+ * Ensures that the file backing these properties has been
+ * loaded; call this method before calling any method defined by
+ * a superclass.
+ */
+ public void load() {
+ loaded = true;
+
+ try {
+ if (!file.exists())
+ return;
+
+ InputStream s = new FileInputStream(file);
+ load(s);
+ }
+ catch (IOException ex) {
+ // eat
+ }
+ }
+
+ /**
+ * Saves the properties to the file.
+ */
+ public void store() {
+ if (!loaded)
+ return; // nothing could have changed so save unnecessary load/save
+
+ try {
+ OutputStream s = new FileOutputStream(file);
+ store(s, header);
+ }
+ catch (IOException ex) {
+ // eat
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/util/Reflect.java b/netx/net/sourceforge/jnlp/util/Reflect.java
new file mode 100644
index 0000000..2b1301c
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/Reflect.java
@@ -0,0 +1,146 @@
+// Copyright (C) 2003 Jon A. Maxwell (JAM)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.util;
+
+import java.util.*;
+import java.lang.reflect.*;
+
+
+/**
+ * Provides simply, convenient methods to invoke methods by
+ * name. This class is used to consolidate reflection needed to
+ * access methods specific to Sun's JVM or to remain backward
+ * compatible while supporting method in newer JVMs.<p>
+ *
+ * Most methods of this class invoke the first method on the
+ * specified object that matches the name and number of
+ * parameters. The type of the parameters are not considered, so
+ * do not attempt to use this class to invoke overloaded
+ * methods.<p>
+ *
+ * Instances of this class are not synchronized.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.1 $
+ */
+public class Reflect {
+
+ // todo: check non-null parameter types, try to send to proper
+ // method if overloaded ones exist on the target object
+
+ // todo: optimize slightly using hashtable of Methods
+
+ private boolean accessible;
+
+ private static Object zero[] = new Object[0];
+
+
+ /**
+ * Create a new Reflect instance.
+ */
+ public Reflect() {
+ //
+ }
+
+ /**
+ * Create a new Reflect instance.
+ *
+ * @param accessible whether to bypass access permissions
+ */
+ public Reflect(boolean accessible) {
+ this.accessible = accessible;
+ }
+
+ /**
+ * Invoke a zero-parameter static method by name.
+ */
+ public Object invokeStatic(String className, String method) {
+ return invokeStatic(className, method, zero);
+ }
+
+ /**
+ * Invoke the static method using the specified parameters.
+ */
+ public Object invokeStatic(String className, String method, Object args[]) {
+ try {
+ Class c = Class.forName(className, true, Reflect.class.getClassLoader());
+
+ Method m = getMethod(c, method, args);
+ if (m.isAccessible() != accessible)
+ m.setAccessible(accessible);
+
+ return m.invoke(null, args);
+ }
+ catch (Exception ex) { // eat
+ return null;
+ }
+ }
+
+ /**
+ * Invoke a zero-parameter method by name on the specified
+ * object.
+ */
+ public Object invoke(Object object, String method) {
+ return invoke(object, method, zero);
+ }
+
+ /**
+ * Invoke a method by name with the specified parameters.
+ *
+ * @return the result of the method, or null on exception.
+ */
+ public Object invoke(Object object, String method, Object args[]) {
+ try {
+ Method m = getMethod(object.getClass(), method, args);
+ if (m.isAccessible() != accessible)
+ m.setAccessible(accessible);
+
+ return m.invoke(object, args);
+ }
+ catch (Exception ex) { // eat
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Return the Method matching the specified name and number of
+ * arguments.
+ */
+ public Method getMethod(Class type, String method, Object args[]) {
+ try {
+ for (Class c = type; c != null; c = c.getSuperclass()) {
+ Method methods[] = c.getMethods();
+
+ for (int i=0; i < methods.length; i++) {
+ if (methods[i].getName().equals(method)) {
+ Class parameters[] = methods[i].getParameterTypes();
+
+ if (parameters.length == args.length)
+ return methods[i];
+ }
+ }
+ }
+ }
+ catch (Exception ex) { // eat
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/util/WeakList.java b/netx/net/sourceforge/jnlp/util/WeakList.java
new file mode 100644
index 0000000..b8b205e
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/WeakList.java
@@ -0,0 +1,126 @@
+// Copyright (C) 2002-2003 Jon A. Maxwell (JAM)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+package net.sourceforge.jnlp.util;
+
+import java.lang.ref.*;
+import java.util.*;
+
+
+/**
+ * This list stores objects automatically using weak references.
+ * Objects are added and removed from the list as normal, but may
+ * turn to null at any point (ie, indexOf(x) followed by get(x)
+ * may return null). The weak references are only removed when
+ * the trimToSize method is called so that the indices remain
+ * constant otherwise.<p>
+ *
+ * @author <a href="mailto:[email protected]">Jon A. Maxwell (JAM)</a> - initial author
+ * @version $Revision: 1.3 $
+ */
+public class WeakList extends AbstractList {
+
+ /* list of weak references */
+ private ArrayList refs = new ArrayList();
+
+
+ /**
+ * Create a weak random-access list.
+ */
+ public WeakList() {
+ }
+
+ /**
+ * Extract the hard reference out of a weak reference.
+ */
+ private Object deref(Object o) {
+ if (o != null && o instanceof WeakReference)
+ return ((WeakReference)o).get();
+ else
+ return null;
+ }
+
+ /**
+ * Returns the object at the specified index, or null if the
+ * object has been collected.
+ */
+ public Object get(int index) {
+ return deref(refs.get(index));
+ }
+
+ /**
+ * Returns the size of the list, including already collected
+ * objects.
+ */
+ public int size() {
+ return refs.size();
+ }
+
+ /**
+ * Sets the object at the specified position and returns the
+ * previous object at that position or null if it was already
+ * collected.
+ */
+ public Object set(int index, Object element) {
+ return deref(refs.set(index, new WeakReference(element)));
+ }
+
+ /**
+ * Inserts the object at the specified position in the list.
+ * Automatically creates a weak reference to the object.
+ */
+ public void add(int index, Object element) {
+ refs.add(index, new WeakReference(element));
+ }
+
+ /**
+ * Removes the object at the specified position and returns it
+ * or returns null if it was already collected.
+ */
+ public Object remove(int index) {
+ return deref(refs.remove(index));
+ }
+
+ /**
+ * Returns a list of hard references to the objects. The
+ * returned list does not include the collected elements, so its
+ * indices do not necessarily correlate with those of this list.
+ */
+ public List hardList() {
+ List result = new ArrayList();
+
+ for (int i=0; i < size(); i++) {
+ Object tmp = get(i);
+
+ if (tmp != null)
+ result.add(tmp);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compacts the list by removing references to collected
+ * objects.
+ */
+ public void trimToSize() {
+ for (int i=size(); i-->0;)
+ if (get(i)==null)
+ remove(i);
+ }
+
+}
diff --git a/netx/net/sourceforge/jnlp/util/XDesktopEntry.java b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java
new file mode 100644
index 0000000..8527fed
--- /dev/null
+++ b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java
@@ -0,0 +1,213 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+package net.sourceforge.jnlp.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import net.sourceforge.jnlp.IconDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.StreamEater;
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * This class builds a (freedesktop.org) desktop entry out of a {@link JNLPFile}
+ * . This entry can be used to install desktop shortcuts. See xdg-desktop-icon
+ * (1) and http://standards.freedesktop.org/desktop-entry-spec/latest/ for more
+ * information
+ *
+ * @author Omair Majid
+ *
+ */
+public class XDesktopEntry {
+
+ public static final String JAVA_ICON_NAME = "java.png";
+
+ private JNLPFile file = null;
+ private int iconSize = -1;
+ private String iconLocation = null;
+
+ private int[] VALID_ICON_SIZES = new int[] { 16, 22, 32, 48, 64, 128 };
+
+ /**
+ * Create a XDesktopEntry for the given JNLP file
+ *
+ * @param file a {@link JNLPFile} that indicates the application to launch
+ */
+ public XDesktopEntry(JNLPFile file) {
+ this.file = file;
+
+ /* looks like a good initial value */
+ iconSize = VALID_ICON_SIZES[2];
+ }
+
+ /**
+ * Returns the contents of the {@link XDesktopEntry} through the
+ * {@link Reader} interface.
+ */
+ public Reader getContentsAsReader() {
+
+ String pathToJavaws = System.getProperty("java.home") + File.separator + "bin"
+ + File.separator + "javaws";
+ File cacheFile = CacheUtil.urlToPath(file.getSourceLocation(), "cache");
+
+ String fileContents = "[Desktop Entry]\n";
+ fileContents += "Version=1.0\n";
+ fileContents += "Name=" + file.getTitle() + "\n";
+ fileContents += "GenericName=Java Web Start Application\n";
+ fileContents += "Comment=" + file.getInformation().getDescription() + "\n";
+ fileContents += "Type=Application\n";
+ if (iconLocation != null) {
+ fileContents += "Icon=" + iconLocation + "\n";
+ } else {
+ fileContents += "Icon=" + JAVA_ICON_NAME + "\n";
+
+ }
+ if (file.getInformation().getVendor() != null) {
+ fileContents += "Vendor=" + file.getInformation().getVendor() + "\n";
+ }
+
+ //Shortcut executes the jnlp from cache and system preferred java..
+ fileContents += "Exec=" + "javaws" + " \"" + cacheFile.getAbsolutePath() + "\"\n";
+
+ return new StringReader(fileContents);
+
+ }
+
+ /**
+ * Get the size of the icon (in pixels) for the desktop shortcut
+ */
+ public int getIconSize() {
+ return iconSize;
+ }
+
+ /**
+ * Set the icon size to use for the desktop shortcut
+ *
+ * @param size the size (in pixels) of the icon to use. Commonly used sizes
+ * are of 16, 22, 32, 48, 64 and 128
+ */
+ public void setIconSize(int size) {
+ iconSize = size;
+ }
+
+ /**
+ * Create a desktop shortcut for this desktop entry
+ */
+ public void createDesktopShortcut() {
+ try {
+ cacheIcon();
+ installDesktopLauncher();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Install this XDesktopEntry into the user's desktop as a launcher
+ */
+ private void installDesktopLauncher() {
+ File shortcutFile = new File(JNLPRuntime.TMP_DIR + File.separator
+ + FileUtils.sanitizeFileName(file.getTitle()) + ".desktop");
+ try {
+
+ /*
+ * Write out a Java String (UTF-16) as a UTF-8 file
+ */
+
+ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(shortcutFile),
+ Charset.forName("UTF-8"));
+ Reader reader = getContentsAsReader();
+
+ char[] buffer = new char[1024];
+ int ret = 0;
+ while (-1 != (ret = reader.read(buffer))) {
+ writer.write(buffer, 0, ret);
+ }
+
+ reader.close();
+ writer.close();
+
+ /*
+ * Install the desktop entry
+ */
+
+ String[] execString = new String[] { "xdg-desktop-icon", "install", "--novendor",
+ shortcutFile.getCanonicalPath() };
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Execing: " + Arrays.toString(execString));
+ }
+ Process installer = Runtime.getRuntime().exec(execString);
+ new StreamEater(installer.getInputStream()).start();
+ new StreamEater(installer.getErrorStream()).start();
+
+ try {
+ installer.waitFor();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if (!shortcutFile.delete()) {
+ throw new IOException("Unable to delete temporary file:" + shortcutFile);
+ }
+
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Cache the icon for the desktop entry
+ */
+ private void cacheIcon() {
+
+ URL iconLocation = file.getInformation().getIconLocation(IconDesc.SHORTCUT, iconSize,
+ iconSize);
+
+ if (iconLocation == null) {
+ iconLocation = file.getInformation().getIconLocation(IconDesc.DEFAULT, iconSize,
+ iconSize);
+ }
+
+ if (iconLocation != null) {
+ String location = CacheUtil.getCachedResource(iconLocation, null, UpdatePolicy.SESSION)
+ .toString();
+ if (!location.startsWith("file:")) {
+ throw new RuntimeException("Unable to cache icon");
+ }
+
+ this.iconLocation = location.substring("file:".length());
+
+ if (JNLPRuntime.isDebug()) {
+ System.err.println("Cached desktop shortcut icon: " + this.iconLocation);
+ }
+ }
+ }
+
+}
diff --git a/netx/net/sourceforge/nanoxml/XMLElement.java b/netx/net/sourceforge/nanoxml/XMLElement.java
new file mode 100644
index 0000000..520136e
--- /dev/null
+++ b/netx/net/sourceforge/nanoxml/XMLElement.java
@@ -0,0 +1,1334 @@
+/* XMLElement.java
+ *
+ * $Revision: 1.2 $
+ * $Date: 2002/08/03 04:36:34 $
+ * $Name: $
+ *
+ * This file is part of NanoXML 2 Lite.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in
+ * a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *****************************************************************************/
+
+/* JAM: hacked the source to remove unneeded methods and comments. */
+
+package net.sourceforge.nanoxml;
+
+import java.io.*;
+import java.util.*;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+
+/**
+ * XMLElement is a representation of an XML object. The object is able to parse
+ * XML code.
+ * <P><DL>
+ * <DT><B>Parsing XML Data</B></DT>
+ * <DD>
+ * You can parse XML data using the following code:
+ * <UL><CODE>
+ * XMLElement xml = new XMLElement();<BR>
+ * FileReader reader = new FileReader("filename.xml");<BR>
+ * xml.parseFromReader(reader);
+ * </CODE></UL></DD></DL>
+ * <DL><DT><B>Retrieving Attributes</B></DT>
+ * <DD>
+ * You can enumerate the attributes of an element using the method
+ * {@link #enumerateAttributeNames() enumerateAttributeNames}.
+ * The attribute values can be retrieved using the method
+ * {@link #getStringAttribute(java.lang.String) getStringAttribute}.
+ * The following example shows how to list the attributes of an element:
+ * <UL><CODE>
+ * XMLElement element = ...;<BR>
+ * Enumeration enum = element.getAttributeNames();<BR>
+ * while (enum.hasMoreElements()) {<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;String key = (String) enum.nextElement();<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;String value = element.getStringAttribute(key);<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(key + " = " + value);<BR>
+ * }
+ * </CODE></UL></DD></DL>
+ * <DL><DT><B>Retrieving Child Elements</B></DT>
+ * <DD>
+ * You can enumerate the children of an element using
+ * {@link #enumerateChildren() enumerateChildren}.
+ * The number of child elements can be retrieved using
+ * {@link #countChildren() countChildren}.
+ * </DD></DL>
+ * <DL><DT><B>Elements Containing Character Data</B></DT>
+ * <DD>
+ * If an elements contains character data, like in the following example:
+ * <UL><CODE>
+ * &lt;title&gt;The Title&lt;/title&gt;
+ * </CODE></UL>
+ * you can retrieve that data using the method
+ * {@link #getContent() getContent}.
+ * </DD></DL>
+ * <DL><DT><B>Subclassing XMLElement</B></DT>
+ * <DD>
+ * When subclassing XMLElement, you need to override the method
+ * {@link #createAnotherElement() createAnotherElement}
+ * which has to return a new copy of the receiver.
+ * </DD></DL>
+ * <P>
+ *
+ * @see net.sourceforge.nanoxml.XMLParseException
+ *
+ * @author Marc De Scheemaecker
+ * &lt;<A href="mailto:[email protected]">[email protected]</A>&gt;
+ * @version $Name: $, $Revision: 1.2 $
+ */
+public class XMLElement
+{
+
+ /**
+ * The attributes given to the element.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field can be empty.
+ * <li>The field is never <code>null</code>.
+ * <li>The keys and the values are strings.
+ * </ul></dd></dl>
+ */
+ private Hashtable attributes;
+
+
+ /**
+ * Child elements of the element.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field can be empty.
+ * <li>The field is never <code>null</code>.
+ * <li>The elements are instances of <code>XMLElement</code>
+ * or a subclass of <code>XMLElement</code>.
+ * </ul></dd></dl>
+ */
+ private Vector children;
+
+
+ /**
+ * The name of the element.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field is <code>null</code> iff the element is not
+ * initialized by either parse or setName.
+ * <li>If the field is not <code>null</code>, it's not empty.
+ * <li>If the field is not <code>null</code>, it contains a valid
+ * XML identifier.
+ * </ul></dd></dl>
+ */
+ private String name;
+
+
+ /**
+ * The #PCDATA content of the object.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field is <code>null</code> iff the element is not a
+ * #PCDATA element.
+ * <li>The field can be any string, including the empty string.
+ * </ul></dd></dl>
+ */
+ private String contents;
+
+
+ /**
+ * Conversion table for &amp;...; entities. The keys are the entity names
+ * without the &amp; and ; delimiters.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field is never <code>null</code>.
+ * <li>The field always contains the following associations:
+ * "lt"&nbsp;=&gt;&nbsp;"&lt;", "gt"&nbsp;=&gt;&nbsp;"&gt;",
+ * "quot"&nbsp;=&gt;&nbsp;"\"", "apos"&nbsp;=&gt;&nbsp;"'",
+ * "amp"&nbsp;=&gt;&nbsp;"&amp;"
+ * <li>The keys are strings
+ * <li>The values are char arrays
+ * </ul></dd></dl>
+ */
+ private Hashtable entities;
+
+
+ /**
+ * The line number where the element starts.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li><code>lineNr &gt= 0</code>
+ * </ul></dd></dl>
+ */
+ private int lineNr;
+
+
+ /**
+ * <code>true</code> if the case of the element and attribute names
+ * are case insensitive.
+ */
+ private boolean ignoreCase;
+
+
+ /**
+ * <code>true</code> if the leading and trailing whitespace of #PCDATA
+ * sections have to be ignored.
+ */
+ private boolean ignoreWhitespace;
+
+
+ /**
+ * Character read too much.
+ * This character provides push-back functionality to the input reader
+ * without having to use a PushbackReader.
+ * If there is no such character, this field is '\0'.
+ */
+ private char charReadTooMuch;
+
+ /**
+ * Character read too much for the comment remover.
+ */
+ private char sanitizeCharReadTooMuch;
+
+ /**
+ * The reader provided by the caller of the parse method.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>The field is not <code>null</code> while the parse method
+ * is running.
+ * </ul></dd></dl>
+ */
+ private Reader reader;
+
+
+ /**
+ * The current line number in the source content.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li>parserLineNr &gt; 0 while the parse method is running.
+ * </ul></dd></dl>
+ */
+ private int parserLineNr;
+
+
+ /**
+ * Creates and initializes a new XML element.
+ * Calling the construction is equivalent to:
+ * <ul><code>new XMLElement(new Hashtable(), false, true)
+ * </code></ul>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>countChildren() => 0
+ * <li>enumerateChildren() => empty enumeration
+ * <li>enumeratePropertyNames() => empty enumeration
+ * <li>getChildren() => empty vector
+ * <li>getContent() => ""
+ * <li>getLineNr() => 0
+ * <li>getName() => null
+ * </ul></dd></dl>
+ *
+ */
+ public XMLElement()
+ {
+ this(new Hashtable(), false, true, true);
+ }
+
+
+ /**
+ * Creates and initializes a new XML element.
+ * <P>
+ * This constructor should <I>only</I> be called from
+ * {@link #createAnotherElement() createAnotherElement}
+ * to create child elements.
+ *
+ * @param entities
+ * The entity conversion table.
+ * @param skipLeadingWhitespace
+ * <code>true</code> if leading and trailing whitespace in PCDATA
+ * content has to be removed.
+ * @param fillBasicConversionTable
+ * <code>true</code> if the basic entities need to be added to
+ * the entity list (client code calling this constructor).
+ * @param ignoreCase
+ * <code>true</code> if the case of element and attribute names have
+ * to be ignored.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>entities != null</code>
+ * <li>if <code>fillBasicConversionTable == false</code>
+ * then <code>entities</code> contains at least the following
+ * entries: <code>amp</code>, <code>lt</code>, <code>gt</code>,
+ * <code>apos</code> and <code>quot</code>
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>countChildren() => 0
+ * <li>enumerateChildren() => empty enumeration
+ * <li>enumeratePropertyNames() => empty enumeration
+ * <li>getChildren() => empty vector
+ * <li>getContent() => ""
+ * <li>getLineNr() => 0
+ * <li>getName() => null
+ * </ul></dd></dl><dl>
+ *
+ */
+ protected XMLElement(Hashtable entities,
+ boolean skipLeadingWhitespace,
+ boolean fillBasicConversionTable,
+ boolean ignoreCase)
+ {
+ this.ignoreWhitespace = skipLeadingWhitespace;
+ this.ignoreCase = ignoreCase;
+ this.name = null;
+ this.contents = "";
+ this.attributes = new Hashtable();
+ this.children = new Vector();
+ this.entities = entities;
+ this.lineNr = 0;
+ Enumeration e = this.entities.keys();
+ while (e.hasMoreElements()) {
+ Object key = e.nextElement();
+ Object value = this.entities.get(key);
+ if (value instanceof String) {
+ value = ((String) value).toCharArray();
+ this.entities.put(key, value);
+ }
+ }
+ if (fillBasicConversionTable) {
+ this.entities.put("amp", new char[] { '&' });
+ this.entities.put("quot", new char[] { '"' });
+ this.entities.put("apos", new char[] { '\'' });
+ this.entities.put("lt", new char[] { '<' });
+ this.entities.put("gt", new char[] { '>' });
+ }
+ }
+
+
+ /**
+ * Adds a child element.
+ *
+ * @param child
+ * The child element to add.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>child != null</code>
+ * <li><code>child.getName() != null</code>
+ * <li><code>child</code> does not have a parent element
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>countChildren() => old.countChildren() + 1
+ * <li>enumerateChildren() => old.enumerateChildren() + child
+ * <li>getChildren() => old.enumerateChildren() + child
+ * </ul></dd></dl><dl>
+ *
+ */
+ public void addChild(XMLElement child)
+ {
+ this.children.addElement(child);
+ }
+
+
+ /**
+ * Adds or modifies an attribute.
+ *
+ * @param name
+ * The name of the attribute.
+ * @param value
+ * The value of the attribute.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * <li><code>name</code> is a valid XML identifier
+ * <li><code>value != null</code>
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>enumerateAttributeNames()
+ * => old.enumerateAttributeNames() + name
+ * <li>getAttribute(name) => value
+ * </ul></dd></dl><dl>
+ *
+ */
+ public void setAttribute(String name,
+ Object value)
+ {
+ if (this.ignoreCase) {
+ name = name.toUpperCase();
+ }
+ this.attributes.put(name, value.toString());
+ }
+
+
+ /**
+ * Returns the number of child elements of the element.
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li><code>result >= 0</code>
+ * </ul></dd></dl>
+ *
+ */
+ public int countChildren()
+ {
+ return this.children.size();
+ }
+
+
+ /**
+ * Enumerates the attribute names.
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li><code>result != null</code>
+ * </ul></dd></dl>
+ *
+ */
+ public Enumeration enumerateAttributeNames()
+ {
+ return this.attributes.keys();
+ }
+
+
+ /**
+ * Enumerates the child elements.
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li><code>result != null</code>
+ * </ul></dd></dl>
+ *
+ */
+ public Enumeration enumerateChildren()
+ {
+ return this.children.elements();
+ }
+
+
+ /**
+ * Returns the PCDATA content of the object. If there is no such content,
+ * <CODE>null</CODE> is returned.
+ *
+ */
+ public String getContent()
+ {
+ return this.contents;
+ }
+
+
+ /**
+ * Returns the line nr in the source data on which the element is found.
+ * This method returns <code>0</code> there is no associated source data.
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li><code>result >= 0</code>
+ * </ul></dd></dl>
+ */
+ public int getLineNr()
+ {
+ return this.lineNr;
+ }
+
+
+ /**
+ * Returns an attribute of the element.
+ * If the attribute doesn't exist, <code>null</code> is returned.
+ *
+ * @param name The name of the attribute.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * <li><code>name</code> is a valid XML identifier
+ * </ul></dd></dl><dl>
+ *
+ */
+ public Object getAttribute(String name)
+ {
+ if (this.ignoreCase) {
+ name = name.toUpperCase();
+ }
+ Object value = this.attributes.get(name);
+ return value;
+ }
+
+
+ /**
+ * Returns the name of the element.
+ *
+ */
+ public String getName()
+ {
+ return this.name;
+ }
+
+
+ /**
+ * Reads one XML element from a java.io.Reader and parses it.
+ *
+ * @param reader
+ * The reader from which to retrieve the XML data.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>reader != null</code>
+ * <li><code>reader</code> is not closed
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>the state of the receiver is updated to reflect the XML element
+ * parsed from the reader
+ * <li>the reader points to the first character following the last
+ * '&gt;' character of the XML element
+ * </ul></dd></dl><dl>
+ *
+ * @throws java.io.IOException
+ * If an error occured while reading the input.
+ * @throws net.sourceforge.nanoxml.XMLParseException
+ * If an error occured while parsing the read data.
+ */
+ public void parseFromReader(Reader reader)
+ throws IOException, XMLParseException
+ {
+ this.parseFromReader(reader, /*startingLineNr*/ 1);
+ }
+
+
+ /**
+ * Reads one XML element from a java.io.Reader and parses it.
+ *
+ * @param reader
+ * The reader from which to retrieve the XML data.
+ * @param startingLineNr
+ * The line number of the first line in the data.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>reader != null</code>
+ * <li><code>reader</code> is not closed
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>the state of the receiver is updated to reflect the XML element
+ * parsed from the reader
+ * <li>the reader points to the first character following the last
+ * '&gt;' character of the XML element
+ * </ul></dd></dl><dl>
+ *
+ * @throws java.io.IOException
+ * If an error occured while reading the input.
+ * @throws net.sourceforge.nanoxml.XMLParseException
+ * If an error occured while parsing the read data.
+ */
+ public void parseFromReader(Reader reader,
+ int startingLineNr)
+ throws IOException, XMLParseException
+ {
+ this.charReadTooMuch = '\0';
+ this.reader = reader;
+ this.parserLineNr = startingLineNr;
+
+ for (;;) {
+ char ch = this.scanWhitespace();
+
+ if (ch != '<') {
+ throw this.expectedInput("<", ch);
+ }
+
+ ch = this.readChar();
+
+ if ((ch == '!') || (ch == '?')) {
+ this.skipSpecialTag(0);
+ } else {
+ this.unreadChar(ch);
+ this.scanElement(this);
+ return;
+ }
+ }
+ }
+
+
+ /**
+ * Creates a new similar XML element.
+ * <P>
+ * You should override this method when subclassing XMLElement.
+ */
+ protected XMLElement createAnotherElement()
+ {
+ return new XMLElement(this.entities,
+ this.ignoreWhitespace,
+ false,
+ this.ignoreCase);
+ }
+
+
+ /**
+ * Changes the content string.
+ *
+ * @param content
+ * The new content string.
+ */
+ public void setContent(String content)
+ {
+ this.contents = content;
+ }
+
+
+ /**
+ * Changes the name of the element.
+ *
+ * @param name
+ * The new name.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * <li><code>name</code> is a valid XML identifier
+ * </ul></dd></dl>
+ *
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+
+ /**
+ * Scans an identifier from the current reader.
+ * The scanned identifier is appended to <code>result</code>.
+ *
+ * @param result
+ * The buffer in which the scanned identifier will be put.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>result != null</code>
+ * <li>The next character read from the reader is a valid first
+ * character of an XML identifier.
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>The next character read from the reader won't be an identifier
+ * character.
+ * </ul></dd></dl><dl>
+ */
+ protected void scanIdentifier(StringBuffer result)
+ throws IOException
+ {
+ for (;;) {
+ char ch = this.readChar();
+ if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z'))
+ && ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.')
+ && (ch != ':') && (ch != '-') && (ch <= '\u007E')) {
+ this.unreadChar(ch);
+ return;
+ }
+ result.append(ch);
+ }
+ }
+
+
+ /**
+ * This method scans an identifier from the current reader.
+ *
+ * @return the next character following the whitespace.
+ */
+ protected char scanWhitespace()
+ throws IOException
+ {
+ for (;;) {
+ char ch = this.readChar();
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ return ch;
+ }
+ }
+ }
+
+
+ /**
+ * This method scans an identifier from the current reader.
+ * The scanned whitespace is appended to <code>result</code>.
+ *
+ * @return the next character following the whitespace.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>result != null</code>
+ * </ul></dd></dl>
+ */
+ protected char scanWhitespace(StringBuffer result)
+ throws IOException
+ {
+ for (;;) {
+ char ch = this.readChar();
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ result.append(ch);
+ case '\r':
+ break;
+ default:
+ return ch;
+ }
+ }
+ }
+
+
+ /**
+ * This method scans a delimited string from the current reader.
+ * The scanned string without delimiters is appended to
+ * <code>string</code>.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>string != null</code>
+ * <li>the next char read is the string delimiter
+ * </ul></dd></dl>
+ */
+ protected void scanString(StringBuffer string)
+ throws IOException
+ {
+ char delimiter = this.readChar();
+ if ((delimiter != '\'') && (delimiter != '"')) {
+ throw this.expectedInput("' or \"");
+ }
+ for (;;) {
+ char ch = this.readChar();
+ if (ch == delimiter) {
+ return;
+ } else if (ch == '&') {
+ this.resolveEntity(string);
+ } else {
+ string.append(ch);
+ }
+ }
+ }
+
+
+ /**
+ * Scans a #PCDATA element. CDATA sections and entities are resolved.
+ * The next &lt; char is skipped.
+ * The scanned data is appended to <code>data</code>.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>data != null</code>
+ * </ul></dd></dl>
+ */
+ protected void scanPCData(StringBuffer data)
+ throws IOException
+ {
+ for (;;) {
+ char ch = this.readChar();
+ if (ch == '<') {
+ ch = this.readChar();
+ if (ch == '!') {
+ this.checkCDATA(data);
+ } else {
+ this.unreadChar(ch);
+ return;
+ }
+ } else if (ch == '&') {
+ this.resolveEntity(data);
+ } else {
+ data.append(ch);
+ }
+ }
+ }
+
+
+ /**
+ * Scans a special tag and if the tag is a CDATA section, append its
+ * content to <code>buf</code>.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>buf != null</code>
+ * <li>The first &lt; has already been read.
+ * </ul></dd></dl>
+ */
+ protected boolean checkCDATA(StringBuffer buf)
+ throws IOException
+ {
+ char ch = this.readChar();
+ if (ch != '[') {
+ this.unreadChar(ch);
+ this.skipSpecialTag(0);
+ return false;
+ } else if (! this.checkLiteral("CDATA[")) {
+ this.skipSpecialTag(1); // one [ has already been read
+ return false;
+ } else {
+ int delimiterCharsSkipped = 0;
+ while (delimiterCharsSkipped < 3) {
+ ch = this.readChar();
+ switch (ch) {
+ case ']':
+ if (delimiterCharsSkipped < 2) {
+ delimiterCharsSkipped += 1;
+ } else {
+ buf.append(']');
+ buf.append(']');
+ delimiterCharsSkipped = 0;
+ }
+ break;
+ case '>':
+ if (delimiterCharsSkipped < 2) {
+ for (int i = 0; i < delimiterCharsSkipped; i++) {
+ buf.append(']');
+ }
+ delimiterCharsSkipped = 0;
+ buf.append('>');
+ } else {
+ delimiterCharsSkipped = 3;
+ }
+ break;
+ default:
+ for (int i = 0; i < delimiterCharsSkipped; i += 1) {
+ buf.append(']');
+ }
+ buf.append(ch);
+ delimiterCharsSkipped = 0;
+ }
+ }
+ return true;
+ }
+ }
+
+
+ /**
+ * Skips a comment.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li>The first &lt;!-- has already been read.
+ * </ul></dd></dl>
+ */
+ protected void skipComment()
+ throws IOException
+ {
+ int dashesToRead = 2;
+ while (dashesToRead > 0) {
+ char ch = this.readChar();
+ if (ch == '-') {
+ dashesToRead -= 1;
+ } else {
+ dashesToRead = 2;
+ }
+
+ // Be more tolerant of extra -- (double dashes)
+ // in comments.
+ if (dashesToRead == 0) {
+ ch = this.readChar();
+ if (ch == '>') {
+ return;
+ } else {
+ dashesToRead = 2;
+ this.unreadChar(ch);
+ }
+ }
+ }
+ /*
+ if (this.readChar() != '>') {
+ throw this.expectedInput(">");
+ }
+ */
+ }
+
+
+ /**
+ * Skips a special tag or comment.
+ *
+ * @param bracketLevel The number of open square brackets ([) that have
+ * already been read.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li>The first &lt;! has already been read.
+ * <li><code>bracketLevel >= 0</code>
+ * </ul></dd></dl>
+ */
+ protected void skipSpecialTag(int bracketLevel)
+ throws IOException
+ {
+ int tagLevel = 1; // <
+ char stringDelimiter = '\0';
+ if (bracketLevel == 0) {
+ char ch = this.readChar();
+ if (ch == '[') {
+ bracketLevel += 1;
+ } else if (ch == '-') {
+ ch = this.readChar();
+ if (ch == '[') {
+ bracketLevel += 1;
+ } else if (ch == ']') {
+ bracketLevel -= 1;
+ } else if (ch == '-') {
+ this.skipComment();
+ return;
+ }
+ }
+ }
+ while (tagLevel > 0) {
+ char ch = this.readChar();
+ if (stringDelimiter == '\0') {
+ if ((ch == '"') || (ch == '\'')) {
+ stringDelimiter = ch;
+ } else if (bracketLevel <= 0) {
+ if (ch == '<') {
+ tagLevel += 1;
+ } else if (ch == '>') {
+ tagLevel -= 1;
+ }
+ }
+ if (ch == '[') {
+ bracketLevel += 1;
+ } else if (ch == ']') {
+ bracketLevel -= 1;
+ }
+ } else {
+ if (ch == stringDelimiter) {
+ stringDelimiter = '\0';
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Scans the data for literal text.
+ * Scanning stops when a character does not match or after the complete
+ * text has been checked, whichever comes first.
+ *
+ * @param literal the literal to check.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>literal != null</code>
+ * </ul></dd></dl>
+ */
+ protected boolean checkLiteral(String literal)
+ throws IOException
+ {
+ int length = literal.length();
+ for (int i = 0; i < length; i += 1) {
+ if (this.readChar() != literal.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Reads a character from a reader.
+ */
+ protected char readChar()
+ throws IOException
+ {
+ if (this.charReadTooMuch != '\0') {
+ char ch = this.charReadTooMuch;
+ this.charReadTooMuch = '\0';
+ return ch;
+ } else {
+ int i = this.reader.read();
+ if (i < 0) {
+ throw this.unexpectedEndOfData();
+ } else if (i == 10) {
+ this.parserLineNr += 1;
+ return '\n';
+ } else {
+ return (char) i;
+ }
+ }
+ }
+
+
+ /**
+ * Scans an XML element.
+ *
+ * @param elt The element that will contain the result.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li>The first &lt; has already been read.
+ * <li><code>elt != null</code>
+ * </ul></dd></dl>
+ */
+ protected void scanElement(XMLElement elt)
+ throws IOException
+ {
+ StringBuffer buf = new StringBuffer();
+ this.scanIdentifier(buf);
+ String name = buf.toString();
+ elt.setName(name);
+ char ch = this.scanWhitespace();
+ while ((ch != '>') && (ch != '/')) {
+ buf.setLength(0);
+ this.unreadChar(ch);
+ this.scanIdentifier(buf);
+ String key = buf.toString();
+ ch = this.scanWhitespace();
+ if (ch != '=') {
+ throw this.expectedInput("=");
+ }
+ this.unreadChar(this.scanWhitespace());
+ buf.setLength(0);
+ this.scanString(buf);
+ elt.setAttribute(key, buf);
+ ch = this.scanWhitespace();
+ }
+ if (ch == '/') {
+ ch = this.readChar();
+ if (ch != '>') {
+ throw this.expectedInput(">");
+ }
+ return;
+ }
+ buf.setLength(0);
+ ch = this.scanWhitespace(buf);
+ if (ch != '<') {
+ this.unreadChar(ch);
+ this.scanPCData(buf);
+ } else {
+ for (;;) {
+ ch = this.readChar();
+ if (ch == '!') {
+ if (this.checkCDATA(buf)) {
+ this.scanPCData(buf);
+ break;
+ } else {
+ ch = this.scanWhitespace(buf);
+ if (ch != '<') {
+ this.unreadChar(ch);
+ this.scanPCData(buf);
+ break;
+ }
+ }
+ } else {
+ buf.setLength(0);
+ break;
+ }
+ }
+ }
+ if (buf.length() == 0) {
+ while (ch != '/') {
+ if (ch == '!') {
+ ch = this.readChar();
+ if (ch != '-') {
+ throw this.expectedInput("Comment or Element");
+ }
+ ch = this.readChar();
+ if (ch != '-') {
+ throw this.expectedInput("Comment or Element");
+ }
+ this.skipComment();
+ } else {
+ this.unreadChar(ch);
+ XMLElement child = this.createAnotherElement();
+ this.scanElement(child);
+ elt.addChild(child);
+ }
+ ch = this.scanWhitespace();
+ if (ch != '<') {
+ throw this.expectedInput("<");
+ }
+ ch = this.readChar();
+ }
+ this.unreadChar(ch);
+ } else {
+ if (this.ignoreWhitespace) {
+ elt.setContent(buf.toString().trim());
+ } else {
+ elt.setContent(buf.toString());
+ }
+ }
+ ch = this.readChar();
+ if (ch != '/') {
+ throw this.expectedInput("/");
+ }
+ this.unreadChar(this.scanWhitespace());
+ if (! this.checkLiteral(name)) {
+ throw this.expectedInput(name);
+ }
+ if (this.scanWhitespace() != '>') {
+ throw this.expectedInput(">");
+ }
+ }
+
+
+ /**
+ * Resolves an entity. The name of the entity is read from the reader.
+ * The value of the entity is appended to <code>buf</code>.
+ *
+ * @param buf Where to put the entity value.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li>The first &amp; has already been read.
+ * <li><code>buf != null</code>
+ * </ul></dd></dl>
+ */
+ protected void resolveEntity(StringBuffer buf)
+ throws IOException
+ {
+ char ch = '\0';
+ StringBuffer keyBuf = new StringBuffer();
+ for (;;) {
+ ch = this.readChar();
+ if (ch == ';') {
+ break;
+ }
+ keyBuf.append(ch);
+ }
+ String key = keyBuf.toString();
+ if (key.charAt(0) == '#') {
+ try {
+ if (key.charAt(1) == 'x') {
+ ch = (char) Integer.parseInt(key.substring(2), 16);
+ } else {
+ ch = (char) Integer.parseInt(key.substring(1), 10);
+ }
+ } catch (NumberFormatException e) {
+ throw this.unknownEntity(key);
+ }
+ buf.append(ch);
+ } else {
+ char[] value = (char[]) this.entities.get(key);
+ if (value == null) {
+ throw this.unknownEntity(key);
+ }
+ buf.append(value);
+ }
+ }
+
+
+ /**
+ * Pushes a character back to the read-back buffer.
+ *
+ * @param ch The character to push back.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li>The read-back buffer is empty.
+ * <li><code>ch != '\0'</code>
+ * </ul></dd></dl>
+ */
+ protected void unreadChar(char ch)
+ {
+ this.charReadTooMuch = ch;
+ }
+
+
+ /**
+ * Creates a parse exception for when an invalid valueset is given to
+ * a method.
+ *
+ * @param name The name of the entity.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException invalidValueSet(String name)
+ {
+ String msg = "Invalid value set (entity name = \"" + name + "\")";
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+
+ /**
+ * Creates a parse exception for when an invalid value is given to a
+ * method.
+ *
+ * @param name The name of the entity.
+ * @param value The value of the entity.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * <li><code>value != null</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException invalidValue(String name,
+ String value)
+ {
+ String msg = "Attribute \"" + name + "\" does not contain a valid "
+ + "value (\"" + value + "\")";
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+
+ /**
+ * Creates a parse exception for when the end of the data input has been
+ * reached.
+ */
+ protected XMLParseException unexpectedEndOfData()
+ {
+ String msg = "Unexpected end of data reached";
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+
+ /**
+ * Creates a parse exception for when a syntax error occured.
+ *
+ * @param context The context in which the error occured.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>context != null</code>
+ * <li><code>context.length() &gt; 0</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException syntaxError(String context)
+ {
+ String msg = "Syntax error while parsing " + context;
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+
+ /**
+ * Creates a parse exception for when the next character read is not
+ * the character that was expected.
+ *
+ * @param charSet The set of characters (in human readable form) that was
+ * expected.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>charSet != null</code>
+ * <li><code>charSet.length() &gt; 0</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException expectedInput(String charSet)
+ {
+ String msg = "Expected: " + charSet;
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+ /**
+ * Creates a parse exception for when the next character read is not
+ * the character that was expected.
+ *
+ * @param charSet The set of characters (in human readable form) that was
+ * expected.
+ * @param ch The character that was received instead.
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>charSet != null</code>
+ * <li><code>charSet.length() &gt; 0</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException expectedInput(String charSet, char ch)
+ {
+ String msg = "Expected: '" + charSet +"'" + " but got: '" + ch + "'";
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+ /**
+ * Creates a parse exception for when an entity could not be resolved.
+ *
+ * @param name The name of the entity.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>name != null</code>
+ * <li><code>name.length() &gt; 0</code>
+ * </ul></dd></dl>
+ */
+ protected XMLParseException unknownEntity(String name)
+ {
+ String msg = "Unknown or invalid entity: &" + name + ";";
+ return new XMLParseException(this.getName(), this.parserLineNr, msg);
+ }
+
+ /**
+ * Reads an xml file and removes the comments, leaving only relevant
+ * xml code.
+ *
+ * @param isr The reader of the InputStream containing the xml.
+ * @param pout The PipedOutputStream that will be receiving the filtered
+ * xml file.
+ */
+ public void sanitizeInput(InputStreamReader isr, PipedOutputStream pout) {
+ try {
+ PrintStream out = new PrintStream(pout);
+
+ this.sanitizeCharReadTooMuch = '\0';
+ this.reader = isr;
+ this.parserLineNr = 0;
+ int newline = 2;
+ char prev = ' ';
+
+ while(true) {
+ char ch;
+ if (this.sanitizeCharReadTooMuch != '\0') {
+ ch = this.sanitizeCharReadTooMuch;
+ this.sanitizeCharReadTooMuch = '\0';
+ } else {
+
+ int i = this.reader.read();
+ if (i == -1) {
+ // no character in buffer, and nothing read
+ out.flush();
+ break;
+ } else if (i == 10) {
+ ch = '\n';
+ } else {
+ ch = (char) i;
+ }
+ }
+
+ char next;
+ int i = this.reader.read();
+ if (i == -1) {
+ // character in buffer and nothing read. write out
+ // what's in the buffer
+ out.print(ch);
+ out.flush();
+ if (JNLPRuntime.isDebug()) {
+ if (ch == 10) {
+ System.out.println();
+ System.out.print("line: " + newline + " ");
+ newline++;
+ } else {
+ System.out.print(ch);
+ }
+ }
+ break;
+ } else if (i == 10) {
+ next = '\n';
+ } else {
+ next = (char) i;
+ }
+
+ this.sanitizeCharReadTooMuch = next;
+
+ // If the next char is a ? or !, then we've hit a special tag,
+ // and should skip it.
+ if (prev == '<' && (next == '!' || next == '?')) {
+ this.skipSpecialTag(0);
+ this.sanitizeCharReadTooMuch = '\0';
+ }
+ // Otherwise we haven't hit a comment, and we should write ch.
+ else {
+ out.print(ch);
+ if (JNLPRuntime.isDebug()) {
+ if (ch == 10) {
+ System.out.println();
+ System.out.print("line: " + newline + " ");
+ newline++;
+ } else {
+ System.out.print(ch);
+ }
+ }
+ }
+ prev = next;
+ }
+
+ out.close();
+ isr.close();
+ } catch (Exception e) {
+ // Print the stack trace here -- xml.parseFromReader() will
+ // throw the ParseException if something goes wrong.
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/netx/net/sourceforge/nanoxml/XMLParseException.java b/netx/net/sourceforge/nanoxml/XMLParseException.java
new file mode 100644
index 0000000..dfbb637
--- /dev/null
+++ b/netx/net/sourceforge/nanoxml/XMLParseException.java
@@ -0,0 +1,130 @@
+/* XMLParseException.java
+ *
+ * $Revision: 1.1 $
+ * $Date: 2002/08/03 04:05:32 $
+ * $Name: $
+ *
+ * This file is part of NanoXML 2 Lite.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in
+ * a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source distribution.
+ *****************************************************************************/
+
+
+package net.sourceforge.nanoxml;
+
+
+/**
+ * An XMLParseException is thrown when an error occures while parsing an XML
+ * string.
+ * <P>
+ * $Revision: 1.1 $<BR>
+ * $Date: 2002/08/03 04:05:32 $<P>
+ *
+ * @see net.sourceforge.nanoxml.XMLElement
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: $, $Revision: 1.1 $
+ */
+public class XMLParseException
+ extends RuntimeException
+{
+
+ /**
+ * Indicates that no line number has been associated with this exception.
+ */
+ public static final int NO_LINE = -1;
+
+
+ /**
+ * The line number in the source code where the error occurred, or
+ * <code>NO_LINE</code> if the line number is unknown.
+ *
+ * <dl><dt><b>Invariants:</b></dt><dd>
+ * <ul><li><code>lineNr &gt 0 || lineNr == NO_LINE</code>
+ * </ul></dd></dl>
+ */
+ private int lineNr;
+
+
+ /**
+ * Creates an exception.
+ *
+ * @param name The name of the element where the error is located.
+ * @param message A message describing what went wrong.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>message != null</code>
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>getLineNr() => NO_LINE
+ * </ul></dd></dl><dl>
+ */
+ public XMLParseException(String name,
+ String message)
+ {
+ super("XML Parse Exception during parsing of "
+ + ((name == null) ? "the XML definition"
+ : ("a " + name + " element"))
+ + ": " + message);
+ this.lineNr = XMLParseException.NO_LINE;
+ }
+
+
+ /**
+ * Creates an exception.
+ *
+ * @param name The name of the element where the error is located.
+ * @param lineNr The number of the line in the input.
+ * @param message A message describing what went wrong.
+ *
+ * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+ * <ul><li><code>message != null</code>
+ * <li><code>lineNr &gt; 0</code>
+ * </ul></dd></dl>
+ *
+ * <dl><dt><b>Postconditions:</b></dt><dd>
+ * <ul><li>getLineNr() => lineNr
+ * </ul></dd></dl><dl>
+ */
+ public XMLParseException(String name,
+ int lineNr,
+ String message)
+ {
+ super("XML Parse Exception during parsing of "
+ + ((name == null) ? "the XML definition"
+ : ("a " + name + " element"))
+ + " at line " + lineNr + ": " + message);
+ this.lineNr = lineNr;
+ }
+
+
+ /**
+ * Where the error occurred, or <code>NO_LINE</code> if the line number is
+ * unknown.
+ *
+ * @see net.sourceforge.nanoxml.XMLParseException#NO_LINE
+ */
+ public int getLineNr()
+ {
+ return this.lineNr;
+ }
+
+}