changeset 689:a2497d56461b cacao

2008-02-05 Joshua Sumali <jsumali@redhat.com> * tools/netx/jnlp/Launcher.java: Removed unused line. * tools/netx/jnlp/resources/Messages.properties: Added security messages for security dialogs. * tools/netx/jnlp/runtime/ApplicationInstance.java: Added signing field. * tools/netx/jnlp/runtime/Boot.java: Use jar root default.jnlp * tools/netx/jnlp/runtime/JNLPClassLoader.java: Added security warning dialogs when running signed code. * tools/netx/jnlp/runtime/JNLPRuntime.java: Enable code signing verification by default. * tools/netx/jnlp/services/ServiceUtil.java: Added enum for different access types that may need permissions. (checkAccess(AccessType)): New method. * tools/netx/jnlp/services/XDownloadService.java: Fixed typo. * tools/netx/jnlp/services/XExtensionInstallerService.java: Likewise. * tools/netx/jnlp/services/XServiceManagerStub.java: Added support for new services. * tools/netx/jnlp/tools/JarSigner.java: (verifyJars): New method. (allVerified): Likewise. (anyJarsSigned): Likewise. (getDetails): Likewise. (getCerts): Likewise. (verifyJar): Keep track of CertPaths used for CertsInfoPane * tools/netx/jnlp/security/CertsInfoPane.java: New file. * tools/netx/jnlp/security/MoreInfoPane.java: Likewise. * tools/netx/jnlp/security/SecurityDialogUI.java: Likewise. * tools/netx/jnlp/security/SecurityWarningDialog.java: Likewise. * tools/netx/jnlp/security/SecurityWarningOptionPane.java: Likewise. * tools/netx/jnlp/services/XClipboardService.java: Likewise. * tools/netx/jnlp/services/XFileOpenService.java: Likewise. * tools/netx/jnlp/services/XFileSaveService.java: Likewise. * tools/netx/jnlp/tools/CharacterEncoder.java: Likewise. * tools/netx/jnlp/tools/HexDumpEncoder.java: Likewise. * ChangeLog: Added fkung's 2 latest ChangeLog entries.
author Joshua Sumali <jsumali@redhat.com>
date Tue, 05 Feb 2008 15:58:12 -0500
parents 217c9bffb37f
children fc2725fed5d1
files ChangeLog tools/netx/jnlp/Launcher.java tools/netx/jnlp/resources/Messages.properties tools/netx/jnlp/runtime/ApplicationInstance.java tools/netx/jnlp/runtime/Boot.java tools/netx/jnlp/runtime/JNLPClassLoader.java tools/netx/jnlp/runtime/JNLPRuntime.java tools/netx/jnlp/security/CertsInfoPane.java tools/netx/jnlp/security/MoreInfoPane.java tools/netx/jnlp/security/SecurityDialogUI.java tools/netx/jnlp/security/SecurityWarningDialog.java tools/netx/jnlp/security/SecurityWarningOptionPane.java tools/netx/jnlp/services/ServiceUtil.java tools/netx/jnlp/services/XClipboardService.java tools/netx/jnlp/services/XDownloadService.java tools/netx/jnlp/services/XExtensionInstallerService.java tools/netx/jnlp/services/XFileOpenService.java tools/netx/jnlp/services/XFileSaveService.java tools/netx/jnlp/services/XServiceManagerStub.java tools/netx/jnlp/tools/CharacterEncoder.java tools/netx/jnlp/tools/HexDumpEncoder.java tools/netx/jnlp/tools/JarSigner.java
diffstat 22 files changed, 2281 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Feb 05 12:26:45 2008 -0500
+++ b/ChangeLog	Tue Feb 05 15:58:12 2008 -0500
@@ -1,3 +1,40 @@
+2008-02-05  Joshua Sumali  <jsumali@redhat.com>
+
+	* tools/netx/jnlp/Launcher.java: Removed unused line.
+	* tools/netx/jnlp/resources/Messages.properties: Added security messages
+	for security dialogs.
+	* tools/netx/jnlp/runtime/ApplicationInstance.java: Added signing field.
+	* tools/netx/jnlp/runtime/Boot.java: Use jar root default.jnlp
+	* tools/netx/jnlp/runtime/JNLPClassLoader.java: Added security warning
+	dialogs when running signed code.
+	* tools/netx/jnlp/runtime/JNLPRuntime.java: Enable code signing
+	verification by default.
+	* tools/netx/jnlp/services/ServiceUtil.java: Added enum for different
+	access types that may need permissions.
+	(checkAccess(AccessType)): New method.
+	* tools/netx/jnlp/services/XDownloadService.java: Fixed typo.
+	* tools/netx/jnlp/services/XExtensionInstallerService.java: Likewise.
+	* tools/netx/jnlp/services/XServiceManagerStub.java: Added support for new
+	services.
+	* tools/netx/jnlp/tools/JarSigner.java:
+	(verifyJars): New method.
+	(allVerified): Likewise.
+	(anyJarsSigned): Likewise.
+	(getDetails): Likewise.
+	(getCerts): Likewise.
+	(verifyJar): Keep track of CertPaths used for CertsInfoPane
+	* tools/netx/jnlp/security/CertsInfoPane.java: New file.
+	* tools/netx/jnlp/security/MoreInfoPane.java: Likewise.
+	* tools/netx/jnlp/security/SecurityDialogUI.java: Likewise.
+	* tools/netx/jnlp/security/SecurityWarningDialog.java: Likewise.
+	* tools/netx/jnlp/security/SecurityWarningOptionPane.java: Likewise.
+	* tools/netx/jnlp/services/XClipboardService.java: Likewise.
+	* tools/netx/jnlp/services/XFileOpenService.java: Likewise.
+	* tools/netx/jnlp/services/XFileSaveService.java: Likewise.
+	* tools/netx/jnlp/tools/CharacterEncoder.java: Likewise.
+	* tools/netx/jnlp/tools/HexDumpEncoder.java: Likewise.
+	* ChangeLog: Added fkung's 2 latest ChangeLog entries.
+
 2008-02-05  Lillian Angel  <langel@redhat.com>
 
 	* Makefile.am
@@ -38,6 +75,38 @@
 	* acinclude.m4: Updated for libgcj-4.3.0.jar.
 	* AUTHORS: Added Bernhard Rosenkränzer.
 
+2008-02-02  Francis Kung  <fkung@redhat.com>
+
+	* tools/netx/jnlp/Parser.java: Re-added Node class.
+	* tools/netx/jnlp/runtime/AppletEnvironment.java: Removed unused import.
+
+2008-02-02  Francis Kung  <fkung@redhat.com>
+
+	* tools/netx/jnlp/JNLPFile.java: Added empty protected constructor.
+	* tools/netx/jnlp/Launcher.java
+	(launch(JNLPFile)): Delegate to new method.
+	(launch(JNLPFile, Container)): New method.
+	(launchApplet): Added Container parameter.
+	(createApplet): Likewise.
+	(TgThread): Added private Container field.
+	(TgThread.constructor(JNLPFile)): Delegate to new constructor.
+	(TgThread.constructor(JNLPFile, Container)): New method.
+	(TgThread.run): Launch applet with container argument.
+	* tools/netx/jnlp/runtime/AppletEnvironment.java
+	(Frame): Renamed field to...
+	(Container): New field.
+	(AppletEnvironment(JNLPFile, AppletInstance, Container)): New method.
+	(AppletEnvironment(JNLPFile, AppletInstance)): Delegate to new method.
+	(getAppletFrame): Return Container instead of Frame.
+	(startApplet): Replace Frame with Container.
+	(appletResize): Likewise.
+	(getParameter): Add lower-case check.
+	* tools/netx/jnlp/runtime/AppletInstance.java
+	(AppletInstance(JNLPFile, ThreadGroup, ClassLoader, Applet, Container)):
+	New method.
+	(setResizable): Only resize if the container is a Frame.
+	(isResizable): Return false if container is not a Frame.
+
 2008-02-01  Gary Benson  <gbenson@redhat.com>
 
 	* patches/icedtea-lib64.patch: Fixed to silence warning.
--- a/tools/netx/jnlp/Launcher.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/Launcher.java	Tue Feb 05 15:58:12 2008 -0500
@@ -429,7 +429,6 @@
             return app;
         }
         catch (Exception ex) {
-            //throw launchError(new LaunchException(file, ex, R("LSFatal"), R("CLInit"), R("LInitApplet"), R("LInitAppletInfo")));
             throw new LaunchException(file, ex, R("LSFatal"), R("CLInit"), R("LInitApplet"), R("LInitAppletInfo"));
         }
     }
--- a/tools/netx/jnlp/resources/Messages.properties	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/resources/Messages.properties	Tue Feb 05 15:58:12 2008 -0500
@@ -1,5 +1,5 @@
 # Default (English) UI messages for netx
-# L=Launcher, B=Boot, P=Parser, C=cache
+# L=Launcher, B=Boot, P=Parser, C=cache S=security
 #
 # General
 NullParameter=Null parameter
@@ -56,7 +56,7 @@
 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=
 
@@ -130,3 +130,25 @@
 CChooseCacheInfo=Netx needs a location for storing cache files.
 CChooseCacheDir=Cache directory
 
+# Security
+SFileReadAccess=The application has requested read access to a file on the machine. Do you want to allow this action?
+SFileWriteAccess=The application has requested write access to a file on the machine. 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?
+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?
+
+# 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.
+SRunWithoutRestrictions=This application will be run without the security restrictions normally provided by java.
+SRunWithUntrustedCertificate=The digital signature was generated with an untrusted certificate.
+
--- a/tools/netx/jnlp/runtime/ApplicationInstance.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/runtime/ApplicationInstance.java	Tue Feb 05 15:58:12 2008 -0500
@@ -59,6 +59,8 @@
     /** 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.
@@ -67,6 +69,7 @@
         this.file = file;
         this.group = group;
         this.loader = loader;
+        this.isSigned = ((JNLPClassLoader) loader).getSigning();
     }
 
     /**
@@ -235,5 +238,11 @@
         weakWindows.trimToSize();
     }
 
+	/**
+	 * Returns whether or not this jar is signed.
+	 */
+	public boolean isSigned() {
+		return isSigned;
+	}
 }
 
--- a/tools/netx/jnlp/runtime/Boot.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/runtime/Boot.java	Tue Feb 05 15:58:12 2008 -0500
@@ -64,7 +64,7 @@
     /** the JNLP file to open if -jnlp not specified (null for no default) */
     private static final String defaultFile = "jar:"
         + Boot.class.getProtectionDomain().getCodeSource().getLocation()
-        + "!/netx/jnlp/resources/default.jnlp";
+        + "!/default.jnlp";
 
     private static final String miniLicense = "\n"
         + "   netx - an open-source JNLP client.\n"
--- a/tools/netx/jnlp/runtime/JNLPClassLoader.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/runtime/JNLPClassLoader.java	Tue Feb 05 15:58:12 2008 -0500
@@ -29,6 +29,8 @@
 import netx.jnlp.cache.*;
 import netx.jnlp.*;
 import netx.jnlp.tools.JarSigner;
+import netx.jnlp.services.*;
+import netx.jnlp.security.*;
 
 /**
  * Classloader that takes it's resources from a JNLP file.  If the
@@ -96,6 +98,11 @@
 	/** 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;
+
     /**
      * Create a new JNLPClassLoader from the specified file.
      *
@@ -229,49 +236,49 @@
 		//Verify jars if the -verify option is passed.
 		if (JNLPRuntime.isVerifying()) {
 
-			boolean allVerified;
-
+			JarSigner js;
 			waitForJars(initialJars); //download the jars first.
 
 			try {
-				allVerified = verifyJars(initialJars);
+				js = verifyJars(initialJars);
 			} catch (Exception e) {
-
 				//we caught an Exception from the JarSigner class.
 				e.printStackTrace();
 				throw new LaunchException(null, null, R("LSFatal"),
 					R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo"));
 			}
 
-			if (!allVerified) {
-
-				String listOfVerifiedJars = "The following jars were verified:\n";
-				String listOfUnverifiedJars = "The following jars were unverified:\n";
-
-				if (verifiedJars.size() != 0)
-					for (int i = 0; i < verifiedJars.size(); i++)
-						listOfVerifiedJars += verifiedJars.get(i) + "\n";
-
-				if (unverifiedJars.size() != 0)
-					for (int i = 0; i < unverifiedJars.size(); i++)
-						listOfUnverifiedJars += unverifiedJars.get(i) + "\n";
+			//Case when at least one jar has some signing
+			if (js.anyJarsSigned()){
+				signing = true;
+				//if there was some problem with the signing...
+				if (!js.allVerified()) {
 
-				//Open dialog, ask user if they still want to run the applet.
-				int i = JOptionPane.showConfirmDialog(null, 
-						R("LNotVerifiedDialog")+"\n\n"
-						+listOfVerifiedJars+"\n"
-						+listOfUnverifiedJars+"\n"
-						+R("LAskToContinue"),
-						"Warning", JOptionPane.YES_NO_OPTION);
-				
-				if (i == 1)
-					throw new LaunchException(null, null, R("LSFatal"), 
-						R("LCLaunching"), R("LNotVerified"), "");
+					boolean b = SecurityWarningDialog.showWarningDialog(
+						SecurityWarningDialog.AccessType.UNVERIFIED, file,
+						js.getCerts(), js.getDetails());
+					if (!b)
+						throw new LaunchException(null, null, R("LSFatal"), 
+							R("LCLaunching"), R("LNotVerified"), "");
+				} else {
+					//jar is completely verified, but we still need to show
+					//a dialog
+
+					boolean b = SecurityWarningDialog.showWarningDialog(
+						SecurityWarningDialog.AccessType.VERIFIED, file,
+						js.getCerts(), js.getDetails());
+					if (!b)
+						throw new LaunchException(null, null, R("LSFatal"),
+							R("LCLaunching"), R("LCancelOnUserRequest"), "");
+				}
+			} else {
+
+				signing = false;
+				//otherwise this jar is simply unsigned -- make sure to ask
+				//for permission on certain actions
 			}
-
 		}
 
-
         activateJars(initialJars);
     }
 
@@ -518,36 +525,11 @@
 	 *
 	 * @param jars the jars to be verified.
 	 */
-	private boolean verifyJars(List<JARDesc> jars) throws Exception {
+	private JarSigner verifyJars(List<JARDesc> jars) throws Exception {
 	
-		boolean allVerified = true;
-		
-		JarSigner js = new JarSigner();
-		verifiedJars = new ArrayList<String>();
-		unverifiedJars = new ArrayList<String>();
-
-		for (int i = 0; i < jars.size(); i++) {
-			
-			JARDesc jar = (JARDesc) jars.get(i);
-			
-			try {
-				String localFile = tracker.getCacheFile(jar.getLocation()).getAbsolutePath();
-				boolean result = js.verifyJar(localFile);
-
-				if (!result) {
-					allVerified = false;
-					unverifiedJars.add(localFile);
-				} else {
-					verifiedJars.add(localFile);
-				}
-			} catch (Exception e){
-				//We may catch exceptions from using js.verifyJar(localFile).
-				e.printStackTrace();
-				throw e;
-			}
-		}
-		
-		return allVerified;
+		js = new JarSigner();
+		js.verifyJars(jars, tracker);
+		return js;
 	}
 
     /**
@@ -751,6 +733,10 @@
         return file.getFileLocation().toString();
     }
 
+	public boolean getSigning() {
+		return signing;
+	}
+
 }
 
 
--- a/tools/netx/jnlp/runtime/JNLPRuntime.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/runtime/JNLPRuntime.java	Tue Feb 05 15:58:12 2008 -0500
@@ -81,7 +81,7 @@
     private static boolean headless = false;
 
 	/** whether we'll be checking for jar signing */
-	private static boolean verify = false;
+	private static boolean verify = true;
 
     /** whether the runtime uses security */
     private static boolean securityEnabled = true;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/security/CertsInfoPane.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,312 @@
+/* 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 netx.jnlp.security;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.security.cert.CertPath;
+import java.security.cert.X509Certificate;
+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 netx.jnlp.tools.*;
+
+/**
+ * Provides the UI for the Certificate Info dialog. This dialog displays data from
+ * X509Certificate(s) used in jar signing.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a>
+ */
+public class CertsInfoPane extends SecurityDialogUI {
+	
+	private ArrayList<CertPath> certs;
+    private JList list;
+	private JTree tree;
+    private JTable table;
+    private JTextArea output;
+    private ListSelectionModel listSelectionModel;
+    private ListSelectionModel tableSelectionModel;
+    private String[] certNames;
+    private String[] columnNames = { "Field", "Value" };
+	private ArrayList<String[][]> certsData;
+
+	public CertsInfoPane(JComponent x) {
+		super(x);
+	}
+
+	/**
+	 * Builds the JTree out of CertPaths.
+	 */
+	private void buildTree() {
+		//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 = 
+			getCN(firstCert.getSubjectX500Principal().getName());
+		String issuerString = 
+			getCN(firstCert.getIssuerX500Principal().getName());
+
+		DefaultMutableTreeNode top =
+			new DefaultMutableTreeNode(subjectString 
+				+ " (" + issuerString + ")");
+
+		//not self signed
+		if (firstPath.getCertificates().size() > 1) {
+			X509Certificate secondCert = 
+				((X509Certificate)firstPath.getCertificates().get(1));
+			subjectString = 
+				getCN(secondCert.getSubjectX500Principal().getName());
+			issuerString = 
+				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());
+	}
+
+	/**
+	 * Constructs the GUI components of this UI
+	 */
+	protected void installComponents() {
+		certs = ((SecurityWarningDialog)optionPane).getCerts();
+		buildTree();
+		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);
+
+            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[][] cert = { {"Version", version},
+                                {"Serial", serialNumber},
+                                {"Signature Algorithm", signatureAlg},
+                                {"Issuer", issuer},
+                                {"Validity", validity},
+                                {"Subject", subject},
+                                {"Signature", signature} };
+            certsData.add(cert);
+            certNames[i] = getCN(c.getSubjectX500Principal().getName())
+				+ " (" + getCN(c.getIssuerX500Principal().getName()) + ")";
+        }
+
+		/**
+		//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(createButtonActionListener(0));
+		copyToClipboard.addActionListener(new CopyToClipboardHandler());
+		buttonPane.add(close, BorderLayout.EAST);
+		buttonPane.add(copyToClipboard, BorderLayout.WEST);
+		buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+		optionPane.add(mainPane, BorderLayout.CENTER);
+		optionPane.add(buttonPane, BorderLayout.SOUTH);
+	}
+
+	/**
+	 * Extracts the CN field from a Certificate principal string.
+	 */
+	private String getCN(String principal) {
+        int start = principal.indexOf("CN=");
+        int end = principal.indexOf(",", start);
+
+		if (end == -1) {
+			end = principal.length();
+		}
+
+        if (start >= 0)
+            return principal.substring(start+3, end);
+        else
+            return principal;
+    }
+
+	/**
+	 * 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.
+	 */
+	private 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));
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/security/MoreInfoPane.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,106 @@
+/* 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 netx.jnlp.security;
+
+import java.awt.*;
+import javax.swing.*;
+import netx.jnlp.runtime.*;
+import java.util.ArrayList;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import javax.swing.border.Border;
+
+/**
+ * Provides the UI for the More Info dialog. This dialog shows details about an
+ * application's signing status.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a>
+ */
+public class MoreInfoPane extends SecurityDialogUI {
+
+	public MoreInfoPane(JComponent x) {
+		super(x);
+	}
+
+	/**
+	 * Constructs the GUI components of this UI
+	 */
+	protected void installComponents() {
+		ArrayList<String> details = 
+			((SecurityWarningDialog)optionPane).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++) {
+			errorPanel.add(new JLabel(htmlWrap(details.get(i))));
+		}
+
+		JPanel buttonsPanel = new JPanel(new BorderLayout());
+		JButton certDetails = new JButton("Cerificate Details");
+		certDetails.addActionListener(new CertInfoButtonListener());
+		JButton close = new JButton("Close");
+		close.addActionListener(createButtonActionListener(0));
+        buttonsPanel.add(certDetails, BorderLayout.WEST);
+        buttonsPanel.add(close, BorderLayout.EAST);
+		buttonsPanel.setBorder(BorderFactory.createEmptyBorder(15,15,15,15));
+
+        JPanel main = new JPanel(new BorderLayout());
+        main.add(errorPanel, BorderLayout.NORTH);
+        main.add(buttonsPanel, BorderLayout.SOUTH);
+
+        optionPane.add(main);
+	}
+
+	/**
+	 * Needed to get word-wrap working in JLabels.
+	 */
+	private String htmlWrap (String s) {
+        return "<html>"+s+"</html>";
+    }
+
+	private class CertInfoButtonListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            SecurityWarningDialog.showCertInfoDialog(
+				((SecurityWarningDialog)optionPane).getCerts(),
+				optionPane);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/security/SecurityDialogUI.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,196 @@
+/* SecurityDialogUI.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 netx.jnlp.security;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.plaf.OptionPaneUI;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.ComponentAdapter;
+import netx.jnlp.runtime.JNLPRuntime;
+import netx.jnlp.JNLPFile;
+
+/**
+ * Provides a base for JNLP warning dialogs.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a>
+ */
+public abstract class SecurityDialogUI extends OptionPaneUI {
+
+	/** The JOptionPane that we're providing the L&F for */
+	protected JOptionPane optionPane;
+
+	/** Component to receive focus when messaged with selectInitialValue. */
+	Component initialFocusComponent;
+
+	/** PropertyChangeListener for <code>optionPane</code> */
+	private PropertyChangeListener propertyChangeListener;
+	private Handler handler;
+
+	public SecurityDialogUI(JComponent x){
+		optionPane = (JOptionPane)x;
+	}
+
+	/**
+	 * Installs the user interface for the SecurityWarningDialog.
+	 */
+	public void installUI(JComponent c) {
+
+		//Only install the UI when type and file in SecurityWarningDialog
+		//have been set.
+		if (((SecurityWarningDialog)c).isInitialized()) {
+			setSystemLookAndFeel();
+			optionPane = (JOptionPane)c;
+			optionPane.setLayout(new BorderLayout());
+			installComponents();
+			installListeners();
+		}
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	protected void installListeners() {
+		if ((propertyChangeListener = getHandler()) != null)
+			optionPane.addPropertyChangeListener(propertyChangeListener);
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	protected void uninstallComponents() {
+		initialFocusComponent = null;
+		optionPane.removeAll();
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	private Handler getHandler() {
+		if (handler == null)
+			handler = new Handler();
+		return handler;
+	}
+
+	//Inherited from OptionPaneUI
+	//Modified from javax.swing.plaf.basic.BasicOptionPaneUI
+	public void selectInitialValue(JOptionPane op) {
+		if (initialFocusComponent != null)
+			initialFocusComponent.requestFocus();
+
+		if (initialFocusComponent instanceof JButton) {
+			JRootPane root = SwingUtilities.getRootPane(initialFocusComponent);
+			if (root != null)
+				root.setDefaultButton((JButton) initialFocusComponent);
+		}
+	}
+
+	//Inherited from OptionPaneUI
+	public boolean containsCustomComponents(JOptionPane op) {
+		return false;
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	protected ActionListener createButtonActionListener(int buttonIndex) {
+		return new ButtonActionListener(buttonIndex);
+	}
+
+	private static String R(String key) {
+		return JNLPRuntime.getMessage(key);
+	}
+
+	/**
+	 * Needed to get word wrap working in JLabels.
+	 */
+	private String htmlWrap (String s) {
+		return "<html>"+s+"</html>";
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	private class Handler implements PropertyChangeListener {
+		public void propertyChange(PropertyChangeEvent e) {
+			if (e.getSource() == optionPane) {
+				String changeName = e.getPropertyName();
+				if (changeName == JOptionPane.OPTIONS_PROPERTY ||
+				        changeName == JOptionPane.INITIAL_VALUE_PROPERTY ||
+				        changeName == JOptionPane.ICON_PROPERTY ||
+				        changeName == JOptionPane.MESSAGE_TYPE_PROPERTY ||
+				        changeName == JOptionPane.OPTION_TYPE_PROPERTY ||
+				        changeName == JOptionPane.MESSAGE_PROPERTY ||
+				        changeName == JOptionPane.SELECTION_VALUES_PROPERTY ||
+				        changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY ||
+				        changeName == JOptionPane.WANTS_INPUT_PROPERTY) {
+					uninstallComponents();
+					installComponents();
+					optionPane.validate();
+				} else if (changeName == "componentOrientation") {
+					ComponentOrientation o = (ComponentOrientation)e.getNewValue();
+					JOptionPane op = (JOptionPane)e.getSource();
+					if (o != (ComponentOrientation)e.getOldValue()) {
+						op.applyComponentOrientation(o);
+					}
+				}
+			}
+		}
+	}
+
+	//Taken from javax.swing.plaf.basic.BasicOptionPaneUI
+	public class ButtonActionListener implements ActionListener {
+		protected int buttonIndex;
+
+		public ButtonActionListener(int buttonIndex) {
+			this.buttonIndex = buttonIndex;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (optionPane != null) {
+				optionPane.setValue(new Integer(buttonIndex));
+			}
+		}
+	}
+
+	private void setSystemLookAndFeel() {
+		try {
+			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+		} catch (Exception e) {
+			//don't worry if we can't.
+		}
+	}
+
+	//this is for the different dialogs to fill in.
+	protected abstract void installComponents();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/security/SecurityWarningDialog.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,286 @@
+/* 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 netx.jnlp.security;
+
+import netx.jnlp.JNLPFile;
+import java.awt.*;
+import javax.swing.*;
+import java.awt.event.*;
+import javax.swing.plaf.OptionPaneUI;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.security.cert.CertPath;
+
+/**
+ * Provides methods for showing security warning dialogs
+ * for a wide range of JNLP security issues.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a>
+ */
+public class SecurityWarningDialog extends JOptionPane {
+
+	/** Types of dialogs we can create */
+	public static enum DialogType {
+		WARNING,
+		MORE_INFO,
+		CERT_INFO
+	}
+	/** The types of access which may need user permission. */
+	public static enum AccessType {
+        READ_FILE,
+        WRITE_FILE,
+        CLIPBOARD_READ,
+        CLIPBOARD_WRITE,
+        PRINTER,
+        VERIFIED,
+        UNVERIFIED
+    }
+
+	/** The type of dialog we want to show */
+	private DialogType dialogType;
+
+	/** The type of access that this dialog is for */
+	private AccessType accessType;
+
+	/** The application file assocated with this security warning */
+	private JNLPFile file;
+
+	/** The Certificates associated with this application */
+	private ArrayList<CertPath> certs;
+
+	/** Details of the signing */
+	private ArrayList<String> details;
+
+	/** Whether or not this object has been fully initialized */
+	private boolean initialized = false;
+
+	public SecurityWarningDialog(DialogType dialogType, AccessType accessType,
+		JNLPFile file, ArrayList<CertPath> certs, 
+		ArrayList<String> details) {
+		this.dialogType = dialogType;
+		this.accessType = accessType;
+		this.file = file;
+		this.certs = certs;
+		this.details = details;
+		initialized = true;
+		updateUI();
+	}
+
+	public boolean isInitialized(){
+		return initialized;
+	}
+	
+	public static boolean showWarningDialog(AccessType accessType, 
+		JNLPFile file) {
+	 	return showWarningDialog(accessType, file, null, null);
+	}
+
+	/**
+	 * 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 certs the signing certificates assocated with this warning, 
+	 * if any. Can be null.
+	 * @param details the extra details associated with this warning. Can be null.
+	 */
+	public static boolean showWarningDialog(AccessType accessType, 
+		JNLPFile file, ArrayList<CertPath> certs, 
+		ArrayList<String> details) {
+		SecurityWarningDialog swd = 
+			new SecurityWarningDialog(DialogType.WARNING, accessType, file,
+			certs, details);
+		JDialog dialog = swd.createDialog();
+		swd.selectInitialValue();
+		dialog.setResizable(false);
+		centerDialog(dialog);
+		dialog.setVisible(true);
+		dialog.dispose();
+
+		Object selectedValue = swd.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 certs the certificates used in this signing
+	 * @param details the extra details regarding this signing
+	 */
+	public static void showMoreInfoDialog(
+		ArrayList<CertPath> certs,
+		ArrayList<String> details, JOptionPane parent) {
+
+		SecurityWarningDialog swd =
+			new SecurityWarningDialog(DialogType.MORE_INFO, null, null,
+			certs, details);
+		JDialog dialog = swd.createDialog();
+		dialog.setLocationRelativeTo(parent);
+		swd.selectInitialValue();
+		dialog.setResizable(false);
+		dialog.setVisible(true);
+		dialog.dispose();
+	}
+
+	/**
+	 * Displays CertPath information in a readable table format.
+	 *
+	 * @param certs the certificates used in signing.
+	 */
+	public static void showCertInfoDialog(ArrayList<CertPath> certs,
+		JOptionPane parent) {
+		SecurityWarningDialog swd = new SecurityWarningDialog(DialogType.CERT_INFO,
+			null, null, certs, null);
+		JDialog dialog = swd.createDialog();
+		dialog.setLocationRelativeTo(parent);
+		swd.selectInitialValue();
+		dialog.setResizable(true);
+		dialog.setVisible(true);
+		dialog.dispose();
+	}
+
+	//Modified from javax.swing.JOptionPane
+	private JDialog createDialog() {
+		String dialogTitle;
+		if (dialogType == DialogType.WARNING)
+			dialogTitle = "Warning - Security";
+		else if (dialogType == DialogType.MORE_INFO)
+			dialogTitle = "More Information";
+		else
+			dialogTitle = "Details - Certificate";
+
+		final JDialog dialog = new JDialog((Frame)null, dialogTitle, true);
+		
+		Container contentPane = dialog.getContentPane();
+		contentPane.setLayout(new BorderLayout());
+		contentPane.add(this, BorderLayout.CENTER);
+		dialog.pack();
+
+		WindowAdapter adapter = new WindowAdapter() {
+            private boolean gotFocus = false;
+            public void windowClosing(WindowEvent we) {
+                setValue(null);
+            }
+            public void windowGainedFocus(WindowEvent we) {
+                // Once window gets focus, set initial focus
+                if (!gotFocus) {
+                    selectInitialValue();
+                    gotFocus = true;
+                }
+            }
+        };
+		dialog.addWindowListener(adapter);
+		dialog.addWindowFocusListener(adapter);
+
+		dialog.addComponentListener(new ComponentAdapter() {
+            public void componentShown(ComponentEvent ce) {
+                // reset value to ensure closing works properly
+                setValue(JOptionPane.UNINITIALIZED_VALUE);
+            }
+        });
+
+		addPropertyChangeListener( new PropertyChangeListener() {
+            public void propertyChange(PropertyChangeEvent event) {
+                // Let the defaultCloseOperation handle the closing
+                // if the user closed the window without selecting a button
+                // (newValue = null in that case).  Otherwise, close the dialog.
+                if (dialog.isVisible() && 
+                	event.getSource() == SecurityWarningDialog.this &&
+                	(event.getPropertyName().equals(VALUE_PROPERTY) ||
+                	event.getPropertyName().equals(INPUT_VALUE_PROPERTY)) &&
+                	event.getNewValue() != null &&
+                	event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) {
+                    dialog.setVisible(false);
+                }
+            }
+        });
+
+		return dialog;
+	}
+
+	public AccessType getType() {
+		return accessType;
+	}
+
+	public JNLPFile getFile() {
+		return file;
+	}
+
+	public ArrayList<CertPath> getCerts() {
+		return certs;
+	}
+
+	public ArrayList<String> getDetails() {
+		return details;
+	}
+
+	/**
+	 * Updates the UI using SecurityWarningOptionPane, instead of the
+	 * basic dialog box.
+	 */
+	public void updateUI() {
+
+		if (dialogType == DialogType.WARNING)
+			setUI((OptionPaneUI) new SecurityWarningOptionPane(this));
+		else if (dialogType == DialogType.MORE_INFO)
+			setUI((OptionPaneUI) new MoreInfoPane(this));
+		else if (dialogType == DialogType.CERT_INFO)
+			setUI((OptionPaneUI) new CertsInfoPane(this));
+	}
+
+	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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/security/SecurityWarningOptionPane.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,237 @@
+/* SecurityWarningOptionPane.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 netx.jnlp.security;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.plaf.OptionPaneUI;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.ComponentAdapter;
+import sun.swing.DefaultLookup;
+import netx.jnlp.runtime.JNLPRuntime;
+import netx.jnlp.JNLPFile;
+
+/**
+ * Provides the look and feel for 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:jsumali@redhat.com">Joshua Sumali</a>
+ */
+public class SecurityWarningOptionPane extends SecurityDialogUI {
+
+	public SecurityWarningOptionPane(JComponent x) {
+		super(x);
+	}
+
+	/**
+	 * Creates the actual GUI components, and adds it to <code>optionPane</code>
+	 */
+	protected void installComponents() {
+		SecurityWarningDialog.AccessType type =
+		    ((SecurityWarningDialog)optionPane).getType();
+		JNLPFile file =
+		    ((SecurityWarningDialog)optionPane).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();
+		} catch (Exception e) {
+		}
+
+		try {
+			publisher = file.getInformation().getVendor();
+		} catch (Exception e) {
+		}
+
+		try {
+			from = file.getInformation().getHomepage().toString();
+		} catch (Exception e) {
+		}
+
+		//Used to determine whether we need another JLabel on the bottom
+		//of our dialog.
+		boolean signingRelated =
+		    (type == SecurityWarningDialog.AccessType.VERIFIED
+		     || type == SecurityWarningDialog.AccessType.UNVERIFIED);
+
+		//Top label
+		String topLabelText = "";
+		String propertyName = "";
+		switch (type) {
+		case VERIFIED:
+			topLabelText = R("SSigVerified");
+			propertyName = "OptionPane.informationIcon";
+			break;
+		case UNVERIFIED:
+			topLabelText = R("SSigUnverified");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		case READ_FILE:
+			topLabelText = R("SFileReadAccess");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		case WRITE_FILE:
+			topLabelText = R("SFileWriteAccess");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		case CLIPBOARD_READ:
+			topLabelText = R("SClipboardReadAccess");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		case CLIPBOARD_WRITE:
+			topLabelText = R("SClipboardWriteAccess");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		case PRINTER:
+			topLabelText = R("SPrinterAccess");
+			propertyName = "OptionPane.warningIcon";
+			break;
+		}
+		//TODO: Get system icons and add them to our dialogs.
+		//Icon icon = (Icon)DefaultLookup.get(optionPane,this,propertyName);
+		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,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));
+
+		//TODO: enable this checkbox once we have a way to
+		//keep track of our trusted signers.
+		JCheckBox alwaysTrust;
+		if (signingRelated)   
+			alwaysTrust = new JCheckBox(
+				"Always trust content from this publisher");
+		else
+			alwaysTrust = new JCheckBox("Always allow this action.");
+		alwaysTrust.setEnabled(false);
+
+		JPanel infoPanel = new JPanel(new GridLayout(4,1));
+		infoPanel.add(nameLabel);
+		infoPanel.add(publisherLabel);
+		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));
+
+		String runButtonText = "Run";
+		if (!signingRelated)
+			runButtonText = "Allow";
+
+		JButton run = new JButton(runButtonText);
+		JButton cancel = new JButton("Cancel");
+		run.addActionListener(createButtonActionListener(0));
+		cancel.addActionListener(createButtonActionListener(1));
+		initialFocusComponent = cancel;
+		buttonPanel.add(run);
+		buttonPanel.add(cancel);
+		buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+		//all of the above
+		JPanel main = new JPanel();
+		main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
+		main.add(topPanel);
+		main.add(infoPanel);
+		main.add(buttonPanel);
+
+		//optional bottom panel, for when we're dealing with signing
+		//(as opposed to file/clipboard/printer access).
+		if (signingRelated) {
+
+			JLabel bottomLabel;
+			JButton moreInfo = new JButton("More information...");
+			moreInfo.addActionListener(new MoreInfoButtonListener());
+			if (type == SecurityWarningDialog.AccessType.VERIFIED)
+				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,50));
+			bottomPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+			main.add(bottomPanel);
+		}
+
+		optionPane.add(main, BorderLayout.CENTER);
+	}
+
+	private static String R(String key) {
+        return JNLPRuntime.getMessage(key);
+    }
+
+	protected String htmlWrap (String s) {
+        return "<html>"+s+"</html>";
+    }
+
+	private class MoreInfoButtonListener implements ActionListener {
+		public void actionPerformed(ActionEvent e) {
+			SecurityWarningDialog.showMoreInfoDialog(
+				((SecurityWarningDialog)optionPane).getCerts(),
+				((SecurityWarningDialog)optionPane).getDetails(), 
+				optionPane);
+		}
+	}
+
+}
--- a/tools/netx/jnlp/services/ServiceUtil.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/services/ServiceUtil.java	Tue Feb 05 15:58:12 2008 -0500
@@ -1,15 +1,15 @@
 // 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.
@@ -25,16 +25,32 @@
 import javax.jnlp.*;
 import netx.jnlp.*;
 import netx.jnlp.runtime.*;
+import netx.jnlp.security.SecurityWarningDialog;
+
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
 
 /**
  * Provides static methods to interact useful for using the JNLP
  * services.<p>
  *
  * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author
- * @version $Revision: 1.8 $ 
+ * @author <a href="mailto:jsumali@redhat.com">Joshua Sumali</a>
+ * @version $Revision: 1.8 $
  */
 public class ServiceUtil {
 
+    private static String R(String key) {
+        return JNLPRuntime.getMessage(key);
+    }
+
+    /**
+     * Types of system access that may need permissions.
+     */
+    public static enum AccessType {
+        READ_FILE, WRITE_FILE, CLIPBOARD_READ, CLIPBOARD_WRITE, PRINTER
+    }
+
     /**
      * Returns the BasicService reference, or null if the service is
      * unavailable.
@@ -120,12 +136,12 @@
      */
     static Object createPrivilegedProxy(Class iface, final Object receiver) {
         return java.lang.reflect.Proxy.newProxyInstance(XServiceManagerStub.class.getClassLoader(),
-                                      new Class[] { iface },
-                                      new PrivilegedHandler(receiver));
+                new Class[] { iface },
+                new PrivilegedHandler(receiver));
     }
 
-    /** 
-     * calls the object's method using privileged access 
+    /**
+     * calls the object's method using privileged access
      */
     private static class PrivilegedHandler implements InvocationHandler {
         private final Object receiver;
@@ -157,6 +173,27 @@
         }
     };
 
+    /**
+     * 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.
+     */
+    static boolean checkAccess(SecurityWarningDialog.AccessType type) {
+
+        ApplicationInstance app = JNLPRuntime.getApplication();
+        if (app != null) {
+            if (!app.isSigned()) {
+                return SecurityWarningDialog.showWarningDialog(type,
+                        app.getJNLPFile());
+            } else if (app.isSigned()) {
+
+                //just return true here regardless if the app
+                //has signing issues -- at this point the user would've
+                //already decided to run the app anyways.
+                return true;
+            }
+        }
+        return false; //deny
+    }
 }
 
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/services/XClipboardService.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,80 @@
+/* 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 netx.jnlp.services;
+
+import javax.jnlp.*;
+import netx.jnlp.*;
+import java.awt.datatransfer.Transferable;
+import java.awt.Toolkit;
+import netx.jnlp.security.SecurityWarningDialog;
+
+/**
+ * The ClipboardService JNLP service.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">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);
+		}
+	}
+
+}
--- a/tools/netx/jnlp/services/XDownloadService.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/services/XDownloadService.java	Tue Feb 05 15:58:12 2008 -0500
@@ -25,7 +25,7 @@
 import netx.jnlp.*;
 
 /**
- * The BasicService JNLP service.
+ * The DownloadService JNLP service.
  *
  * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author
  * @version $Revision: 1.7 $ 
--- a/tools/netx/jnlp/services/XExtensionInstallerService.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/services/XExtensionInstallerService.java	Tue Feb 05 15:58:12 2008 -0500
@@ -25,7 +25,7 @@
 import netx.jnlp.*;
 
 /**
- * The BasicService JNLP service.
+ * The ExtensionInstallerService JNLP service.
  *
  * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell (JAM)</a> - initial author
  * @version $Revision: 1.6 $ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/services/XFileOpenService.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,112 @@
+/* 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 netx.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+import netx.jnlp.*;
+import netx.jnlp.runtime.*;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import java.security.*;
+import netx.jnlp.security.SecurityWarningDialog;
+
+/**
+ * The FileOpenService JNLP service.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">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;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/services/XFileSaveService.java	Tue Feb 05 15:58:12 2008 -0500
@@ -0,0 +1,139 @@
+/* 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 netx.jnlp.services;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.lang.ref.*;
+import javax.jnlp.*;
+import netx.jnlp.*;
+import netx.jnlp.security.SecurityWarningDialog;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import java.security.*;
+
+/**
+ * The FileSaveService JNLP service.
+ *
+ * @author <a href="mailto:jsumali@redhat.com">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");
+        }
+    }
+}
--- a/tools/netx/jnlp/services/XServiceManagerStub.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/services/XServiceManagerStub.java	Tue Feb 05 15:58:12 2008 -0500
@@ -41,11 +41,14 @@
     // run less code in the secure environment (or avoid privileged
     // actions by giving permission to the code source).
 
-    private static String serviceNames[] = {
+	private static String serviceNames[] = {
         "javax.jnlp.BasicService", // required
         "javax.jnlp.DownloadService", // required
         "javax.jnlp.ExtensionInstallerService", // required
         "javax.jnlp.PersistenceService",
+        "javax.jnlp.FileOpenService",
+        "javax.jnlp.FileSaveService",
+        "javax.jnlp.ClipboardService"
     };
 
     private static Object services[] = {
@@ -53,6 +56,9 @@
         ServiceUtil.createPrivilegedProxy(DownloadService.class, new XDownloadService()),
         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()),
     };
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/tools/CharacterEncoder.java	Tue Feb 05 15:58:12 2008 -0500
@@ -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 netx.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);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/netx/jnlp/tools/HexDumpEncoder.java	Tue Feb 05 15:58:12 2008 -0500
@@ -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 netx.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;
+    }
+
+}
--- a/tools/netx/jnlp/tools/JarSigner.java	Tue Feb 05 12:26:45 2008 -0500
+++ b/tools/netx/jnlp/tools/JarSigner.java	Tue Feb 05 15:58:12 2008 -0500
@@ -33,10 +33,16 @@
 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 netx.jnlp.cache.*;
+import netx.jnlp.*;
+import netx.jnlp.security.*;
+import netx.jnlp.runtime.JNLPRuntime;
+
 /**
  * <p>The jarsigner utility.
  *
@@ -46,6 +52,10 @@
 
 public class JarSigner {
 
+    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
@@ -108,10 +118,11 @@
     // read zip entry raw bytes
     private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
     private byte[] buffer = new byte[8192];
- //   private ContentSigner signingMechanism = null;
+//   private ContentSigner signingMechanism = null;
     private String altSignerClass = null;
     private String altSignerClasspath = null;
     private ZipFile zipFile = null;
+    private boolean hasUnsignedEntry = false;
     private boolean hasExpiredCert = false;
     private boolean hasExpiringCert = false;
     private boolean notYetValidCert = false;
@@ -120,6 +131,73 @@
     private boolean badExtendedKeyUsage = false;
     private boolean badNetscapeCertType = false;
 
+    private boolean allVerified = 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>();
+
+    public boolean hasSigningIssues() {
+        return hasExpiredCert || notYetValidCert || badKeyUsage
+               || badExtendedKeyUsage || badNetscapeCertType;
+    }
+
+    public boolean allVerified() {
+        return allVerified;
+    }
+
+    public boolean anyJarsSigned() {
+        return anyJarsSigned;
+    }
+
+    public ArrayList<String> getDetails() {
+        return details;
+    }
+
+    public ArrayList<CertPath> getCerts() {
+        return certs;
+    }
+
+    public void verifyJars(List<JARDesc> jars, ResourceTracker tracker)
+    throws Exception {
+
+        for (int i = 0; i < jars.size(); i++) {
+
+            JARDesc jar = (JARDesc) jars.get(i);
+            verifiedJars = new ArrayList<String>();
+            unverifiedJars = new ArrayList<String>();
+            certs = new ArrayList<CertPath>();
+
+            try {
+                String localFile = tracker.getCacheFile(jar.getLocation()).getAbsolutePath();
+                boolean result = verifyJar(localFile);
+
+                if (!result) {
+                    //allVerified is true until we encounter a problem
+                    //with one or more jars
+                    allVerified = false;
+                    unverifiedJars.add(localFile);
+                } else {
+                    verifiedJars.add(localFile);
+                }
+            } catch (Exception e){
+                //We may catch exceptions from using js.verifyJar(localFile).
+                e.printStackTrace();
+                throw e;
+            }
+        }
+    }
+
     public boolean verifyJar(String jarName) throws Exception {
         boolean anySigned = false;
         boolean hasUnsignedEntry = false;
@@ -167,14 +245,16 @@
                                         && !signatureRelated(name);
                     if (isSigned) {
                         for (int i = 0; i < signers.length; i++) {
-                            Certificate cert =
-                                signers[i].getSignerCertPath()
-                                    .getCertificates().get(0);
+                            CertPath certPath = signers[i].getSignerCertPath();
+                            if (!certs.contains(certPath))
+                                certs.add(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();
+                                                    .getNotAfter().getTime();
 
                                     if (notAfter < now) {
                                         hasExpiredCert = true;
@@ -185,10 +265,39 @@
                             }
                         }
                     }
-
                 } //while e has more elements
             } //if man not null
-            
+
+            //Alert the user if any of the following are true.
+            if (!anySigned) {
+
+            } 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;
@@ -198,9 +307,17 @@
             }
         }
 
-        return anySigned;
+        //anySigned does not guarantee that all files were signed.
+        return anySigned && !(hasUnsignedEntry || hasExpiredCert
+                              || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType
+                              || notYetValidCert);
     }
 
+	private void addToDetails(String detail) {
+		if (!details.contains(detail))
+			details.add(detail);
+	}
+
     private static MessageFormat validityTimeForm = null;
     private static MessageFormat notYetTimeForm = null;
     private static MessageFormat expiredTimeForm = null;
@@ -210,7 +327,7 @@
 
 
     Hashtable<Certificate, String> storeHash =
-                                new Hashtable<Certificate, String>();
+        new Hashtable<Certificate, String>();
 
     /**
      * signature-related files include:
@@ -219,20 +336,20 @@
      * . 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("/"))) {
+                ucName.equals(META_INF) ||
+                (ucName.startsWith(SIG_PREFIX) &&
+                 ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
             return true;
         }
 
         if (ucName.startsWith(META_INF) &&
-            SignatureFileVerifier.isBlockOrSF(ucName)) {
+                SignatureFileVerifier.isBlockOrSF(ucName)) {
             // .SF/.DSA/.RSA files in META-INF subdirs
             // are not considered signature-related
             return (ucName.indexOf("/") == ucName.lastIndexOf("/"));
@@ -248,7 +365,7 @@
      *            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) {
@@ -293,18 +410,18 @@
         try {
             // OID_NETSCAPE_CERT_TYPE
             byte[] netscapeEx = userCert.getExtensionValue
-                    ("2.16.840.1.113730.1.1");
+                                ("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();
+                .toByteArray();
 
                 NetscapeCertTypeExtension extn =
-                        new NetscapeCertTypeExtension(encoded);
+                    new NetscapeCertTypeExtension(encoded);
 
                 Boolean val = (Boolean)extn.get(
-                        NetscapeCertTypeExtension.OBJECT_SIGNING);
+                                  NetscapeCertTypeExtension.OBJECT_SIGNING);
                 if (!val) {
                     if (bad != null) {
                         bad[2] = true;