# HG changeset patch # User Andrew John Hughes # Date 1242850622 -3600 # Node ID 8dc304404e01df747fd1a9e4b5f4640e6eb51ade # Parent bbac5c54022bc8840967cfe28e9eb93aa537bf2e Update plugin and javaws. 2009-05-13 Omair Majid * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPSplashScreen.java: New file. This new class is responsible for displaying the splash screen. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/Launcher.java (launchApplication): Show a splash screen if specified in the JNLP file while loading the remote jars. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java (getListener): Reposition the frame at the bottom right corner of the screen. 2009-05-13 Lillian Angel * plugin/icedtea/sun/applet/PluginAppletSecurityContext.java (PluginAppletSecurityContext): Added comment. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java (checkPermission): Fixed style. 2009-05-11 Lillian Angel * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java (getDefaultBaseDir): Added check for home dir. Return null if homeDir not found in system properties. 2009-05-11 Lillian Angel * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java (getDefaultBaseDir): Added null check. 2009-05-11 Lillian Angel * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java (getDefaultBaseDir): Set basedir for netx applications to ~/.netx, rather than asking. Now consistant with the plugin. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/InstallDialog.java: Removed. 2009-05-06 Lillian Angel Fixes bz#498108 * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java (runLoader): Pass false to JNLPRuntime.initialize, since this is an applet. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java (PluginAppletSecurityContext): Likewise. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/Boot.java (run): Pass true to JNLPRuntime.initialize, since this is a webstart app. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java (initialize): Added new parameter isApplication, which is used to set global static variable isWebstartApplication. (isWebstartApplication): New accessor function. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java: (checkPermission): Added call to JNLPRuntime.isWebstartApplication so the check is bypassed if the permissions are to be checked for a webstart application. 2009-04-30 Deepak Bhole * plugin/icedtea/sun/applet/PluginAppletSecurityContext.java: Forgo permission check if JS source is [System] (i.e. Mozilla extension/chrome). 2009-04-23 Deepak Bhole * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: Handle nested jars. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPPolicy.java: Use site address when checking for policy against CodeSource. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/tools/JarSigner.java: Handle nested jars. 2009-04-10 Deepak Bhole * IcedTeaPlugin.cc: Fix rhbz478561 (freeze with empty string return from Java side). * plugin/icedtea/sun/applet/PluginMessageConsumer.java: Bump max workers from 3 to 20, limiting initial to 3, and growing if/when needed. 2009-04-02 Deepak Bhole * IcedTeaPlugin.cc: Incremented timeout to 3 minutes. Added functions to process proxy and auth info requests (the latter is unused). * plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java: New file. Displays a username/password input dialog to users for sites and proxies requiring http auth. * plugin/icedtea/sun/applet/PluginAppletViewer.java: Fix wait mechanism to detect applet initialization failures correctly. Add support for requesting proxy information from the browser. * plugin/icedtea/sun/applet/PluginCallRequestFactory.java: Add support for PluginProxyInfoRequest objects. * plugin/icedtea/sun/applet/PluginMain.java: Wire in custom authenticator and proxy selector. * plugin/icedtea/sun/applet/PluginProxyInfoRequest.java: New file. Object representing proxy information request from browser. * plugin/icedtea/sun/applet/PluginProxySelector.java: Custom proxy selector that requests information from browser and uses it. * plugin/icedtea/sun/applet/PluginStreamHandler.java: Improve handling code for "plugin specific" messages on the wire. * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java: Add support for improved initialization failure detection. diff -r bbac5c54022b -r 8dc304404e01 ChangeLog --- a/ChangeLog Wed May 20 20:50:48 2009 +0100 +++ b/ChangeLog Wed May 20 21:17:02 2009 +0100 @@ -1,3 +1,102 @@ +2009-05-13 Omair Majid + + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPSplashScreen.java: New file. This new class + is responsible for displaying the splash screen. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/Launcher.java (launchApplication): Show a splash + screen if specified in the JNLP file while loading the remote jars. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java (getListener): + Reposition the frame at the bottom right corner of the screen. + +2009-05-13 Lillian Angel + + * plugin/icedtea/sun/applet/PluginAppletSecurityContext.java + (PluginAppletSecurityContext): Added comment. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java + (checkPermission): Fixed style. + +2009-05-11 Lillian Angel + + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java + (getDefaultBaseDir): Added check for home dir. Return null if homeDir + not found in system properties. + +2009-05-11 Lillian Angel + + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java + (getDefaultBaseDir): Added null check. + +2009-05-11 Lillian Angel + + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java + (getDefaultBaseDir): Set basedir for netx applications to ~/.netx, + rather than asking. Now consistant with the plugin. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/InstallDialog.java: Removed. + +2009-05-06 Lillian Angel + + Fixes bz#498108 + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java + (runLoader): Pass false to JNLPRuntime.initialize, since this is an + applet. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java + (PluginAppletSecurityContext): Likewise. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/Boot.java + (run): Pass true to JNLPRuntime.initialize, since this + is a webstart app. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java + (initialize): Added new parameter isApplication, which is used to set + global static variable isWebstartApplication. + (isWebstartApplication): New accessor function. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java: + (checkPermission): Added call to JNLPRuntime.isWebstartApplication so + the check is bypassed if the permissions are to be checked for + a webstart application. + +2009-04-30 Deepak Bhole + + * plugin/icedtea/sun/applet/PluginAppletSecurityContext.java: Forgo + permission check if JS source is [System] (i.e. Mozilla + extension/chrome). + +2009-04-23 Deepak Bhole + + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: Handle nested + jars. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPPolicy.java: Use site address when + checking for policy against CodeSource. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/tools/JarSigner.java: Handle nested + jars. + +2009-04-10 Deepak Bhole + + * IcedTeaPlugin.cc: Fix rhbz478561 (freeze with empty string return from + Java side). + * plugin/icedtea/sun/applet/PluginMessageConsumer.java: Bump max workers + from 3 to 20, limiting initial to 3, and growing if/when needed. + +2009-04-02 Deepak Bhole + + * IcedTeaPlugin.cc: Incremented timeout to 3 minutes. Added functions to + process proxy and auth info requests (the latter is unused). + * plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java: New file. + Displays a username/password input dialog to users for sites and proxies + requiring http auth. + * plugin/icedtea/sun/applet/PluginAppletViewer.java: Fix wait mechanism to + detect applet initialization failures correctly. Add support for + requesting proxy information from the browser. + * plugin/icedtea/sun/applet/PluginCallRequestFactory.java: Add support for + PluginProxyInfoRequest objects. + * plugin/icedtea/sun/applet/PluginMain.java: Wire in custom authenticator + and proxy selector. + * plugin/icedtea/sun/applet/PluginProxyInfoRequest.java: New file. Object + representing proxy information request from browser. + * plugin/icedtea/sun/applet/PluginProxySelector.java: Custom proxy + selector that requests information from browser and uses it. + * plugin/icedtea/sun/applet/PluginStreamHandler.java: Improve handling + code for "plugin specific" messages on the wire. + * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java: Add support for improved + initialization failure detection. + 2009-05-20 Andrew John Hughes * HACKING: Updated. diff -r bbac5c54022b -r 8dc304404e01 IcedTeaPlugin.cc --- a/IcedTeaPlugin.cc Wed May 20 20:50:48 2009 +0100 +++ b/IcedTeaPlugin.cc Wed May 20 21:17:02 2009 +0100 @@ -85,7 +85,7 @@ // #14 0x0153fdbf in ProxyJNIEnv::CallObjectMethod (env=0xa8b8040, obj=0x9dad690, methodID=0xa0ed070) at ProxyJNI.cpp:641 // timeout (in seconds) for various calls to java side -#define TIMEOUT 20 +#define TIMEOUT 180 #define NOT_IMPLEMENTED() \ PLUGIN_DEBUG_1ARG ("NOT IMPLEMENTED: %s\n", __PRETTY_FUNCTION__) @@ -588,7 +588,7 @@ PLUGIN_DEBUG_0ARG ("RECEIVE VALUE 1\n"); \ ResultContainer *resultC; \ factory->result_map.Get(reference, &resultC); \ - while (resultC->returnValue == "" && \ + while (resultC->returnValue.IsVoid() == PR_TRUE && \ resultC->errorOccurred == PR_FALSE) \ { \ PROCESS_PENDING_EVENTS_REF (reference); \ @@ -607,7 +607,7 @@ PLUGIN_DEBUG_0ARG("RECEIVE SIZE 1\n"); \ ResultContainer *resultC; \ factory->result_map.Get(reference, &resultC); \ - while (resultC->returnValue == "" && \ + while (resultC->returnValue.IsVoid() == PR_TRUE && \ resultC->errorOccurred == PR_FALSE) \ { \ PROCESS_PENDING_EVENTS_REF (reference); \ @@ -631,7 +631,7 @@ PLUGIN_DEBUG_0ARG("RECEIVE STRING 1\n"); \ ResultContainer *resultC; \ factory->result_map.Get(reference, &resultC); \ - while (resultC->returnValue == "" && \ + while (resultC->returnValue.IsVoid() == PR_TRUE && \ resultC->errorOccurred == PR_FALSE) \ { \ PROCESS_PENDING_EVENTS_REF (reference); \ @@ -655,7 +655,7 @@ PLUGIN_DEBUG_0ARG("RECEIVE STRING UCS 1\n"); \ ResultContainer *resultC; \ factory->result_map.Get(reference, &resultC); \ - while (resultC->returnValueUCS.IsEmpty() && \ + while (resultC->returnValueUCS.IsVoid() == PR_TRUE && \ resultC->errorOccurred == PR_FALSE) \ { \ PROCESS_PENDING_EVENTS_REF (reference); \ @@ -855,6 +855,8 @@ returnIdentifier = -1; returnValue.Truncate(); returnValueUCS.Truncate(); + returnValue.SetIsVoid(PR_TRUE); + returnValueUCS.SetIsVoid(PR_TRUE); errorMessage.Truncate(); errorOccurred = PR_FALSE; @@ -877,6 +879,8 @@ returnIdentifier = -1; returnValue.Truncate(); returnValueUCS.Truncate(); + returnValue.SetIsVoid(PR_TRUE); + returnValueUCS.SetIsVoid(PR_TRUE); errorMessage.Truncate(); errorOccurred = PR_FALSE; @@ -1007,6 +1011,7 @@ nsresult StartAppletviewer (); void ProcessMessage(); void ConsumeMsgFromJVM(); + nsresult GetProxyInfo(const char* siteAddr, char** proxyScheme, char** proxyHost, char** proxyPort); nsCOMPtr sink; nsCOMPtr transport; nsCOMPtr applet_viewer_process; @@ -1792,7 +1797,7 @@ NS_IMETHODIMP IcedTeaPluginFactory::Show (void) { - nsCString msg("showconsole"); + nsCString msg("plugin showconsole"); this->SendMessageToAppletViewer(msg); return NS_OK; } @@ -1800,7 +1805,7 @@ NS_IMETHODIMP IcedTeaPluginFactory::Hide (void) { - nsCString msg("hideconsole"); + nsCString msg("plugin hideconsole"); this->SendMessageToAppletViewer(msg); return NS_OK; } @@ -2658,6 +2663,96 @@ return factory->GetJavaObject (instance_identifier, object); } +#include +#include +#include +#include +#include +#include +#include + +/** + * + * Returns the proxy information for the given url + * + * The proxy query part of this function can be made much smaller by using + * nsIPluginManager2::FindProxyForURL() .. however, because we need to parse + * the return components in various ways, it is easier to query + * nsIProtocolProxyService directly + * + * @param siteAddr The URL to check + * @param proxyScheme Return parameter containing the proxy URI scheme (http/socks/etc.) + * @param proxyHost Return parameter containing the proxy host + * @param proxyPort Return parameter containing the proxy port + */ + +NS_IMETHODIMP +IcedTeaPluginFactory::GetProxyInfo(const char* siteAddr, char** proxyScheme, char** proxyHost, char** proxyPort) +{ + nsresult rv; + + // Initialize service variables + nsCOMPtr proxy_svc = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); + + if (!proxy_svc) { + printf("Cannot initialize proxy service\n"); + return rv; + } + + nsCOMPtr io_svc = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv) || !io_svc) { + printf("Cannot initialize io service\n"); + return NS_ERROR_FAILURE; + } + + // uri which needs to be accessed + nsCOMPtr uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + // find the proxy address if any + nsCOMPtr info; + proxy_svc->Resolve(uri, 0, getter_AddRefs(info)); + + // if there is no proxy found, return immediately + if (!info) { + PLUGIN_DEBUG_1ARG("%s does not need a proxy\n", siteAddr); + return NS_ERROR_FAILURE; + } + + // if proxy info is available, extract it + nsCString phost; + PRInt32 pport; + nsCString ptype; + + info->GetHost(phost); + info->GetPort(&pport); + info->GetType(ptype); + + // resolve the proxy address to an IP + nsCOMPtr dns_svc = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + + if (!dns_svc) { + printf("Cannot initialize DNS service\n"); + return rv; + } + + nsCOMPtr record; + dns_svc->Resolve(phost, 0U, getter_AddRefs(record)); + + // TODO: Add support for multiple ips + nsDependentCString ipAddr; + record->GetNextAddrAsString(ipAddr); + + // pack information in return variables + snprintf(*proxyScheme, sizeof(char)*32, "%s", ptype.get()); + snprintf(*proxyHost, sizeof(char)*64, "%s", ipAddr.get()); + snprintf(*proxyPort, sizeof(char)*8, "%d", pport); + + PLUGIN_DEBUG_4ARG("Proxy info for %s: %s %s %s\n", siteAddr, *proxyScheme, *proxyHost, *proxyPort); + + return NS_OK; +} NS_IMETHODIMP IcedTeaPluginInstance::GetCookie(const char* siteAddr, char** cookieString) @@ -2677,8 +2772,8 @@ return NS_ERROR_FAILURE; } - nsIURI *uri; - io_svc->NewURI(nsCString(siteAddr), NULL, NULL, &uri); + nsCOMPtr uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); nsCOMPtr cookie_svc = do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv); @@ -2902,6 +2997,13 @@ nsDependentCSubstring prefix(pch, strlen(pch)); pch = strtok (NULL, " "); PRUint32 identifier = nsDependentCSubstring(pch, strlen(pch)).ToInteger (&conversionResult); + + /* Certain prefixes may not have an identifier. if they don't. we have a command here */ + nsDependentCSubstring command; + if (NS_FAILED(conversionResult)) { + command.Rebind(pch, strlen(pch)); + } + PRUint32 reference = -1; if (strstr(message.get(), "reference") != NULL) { @@ -2910,8 +3012,11 @@ reference = nsDependentCSubstring(pch, strlen(pch)).ToInteger (&conversionResult); } - pch = strtok (NULL, " "); - nsDependentCSubstring command(pch, strlen(pch)); + if (command.Length() == 0) { + pch = strtok (NULL, " "); + command.Rebind(pch, strlen(pch)); + } + pch = strtok (NULL, " "); nsDependentCSubstring rest("", 0); @@ -3251,7 +3356,8 @@ ResultContainer *resultC; result_map.Get(reference, &resultC); - resultC->returnValue = rest; + resultC->returnValue = rest; + resultC->returnValue.SetIsVoid(PR_FALSE); PLUGIN_DEBUG_1ARG ("PLUGIN GOT RETURN VALUE: %s\n", resultC->returnValue.get()); } else if (command == "GetStringUTFChars") @@ -3339,11 +3445,55 @@ ResultContainer *resultC; result_map.Get(reference, &resultC); resultC->returnValueUCS = returnValueUCS; + resultC->returnValueUCS.SetIsVoid(PR_FALSE); } // Do nothing for: SetStaticField, SetField, ExceptionClear, // DeleteGlobalRef, DeleteLocalRef } + else if (prefix == "plugin") + { + + if (command == "PluginProxyInfo") { + + nsresult rv; + nsCOMPtr net_util = do_GetService(NS_NETUTIL_CONTRACTID, &rv); + + if (!net_util) + printf("Error instantiating NetUtil service.\n"); + + // decode the url + nsDependentCSubstring url; + net_util->UnescapeString(rest, 0, url); + + char* proxyScheme = (char*) malloc(sizeof(char)*32); + char* proxyHost = (char*) malloc(sizeof(char)*64); + char* proxyPort = (char*) malloc(sizeof(char)*8); + + nsCString proxyInfo("plugin PluginProxyInfo "); + + // get proxy info + if (GetProxyInfo(((nsCString) url).get(), &proxyScheme, &proxyHost, &proxyPort) == NS_OK) + { + proxyInfo += proxyScheme; + proxyInfo += " "; + proxyInfo += proxyHost; + proxyInfo += " "; + proxyInfo += proxyPort; + + PLUGIN_DEBUG_4ARG("Proxy for %s is %s %s %s\n", ((nsCString) url).get(), proxyScheme, proxyHost, proxyPort); + } else { + PLUGIN_DEBUG_1ARG("No suitable proxy found for %s\n", ((nsCString) url).get()); + } + + // send back what we found + SendMessageToAppletViewer (proxyInfo); + + // free allocated memory + delete proxyScheme, proxyHost, proxyPort; + + } + } } void IcedTeaPluginFactory::ProcessMessage () diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPFile.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPFile.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPFile.java Wed May 20 21:17:02 2009 +0100 @@ -106,7 +106,7 @@ // null values will still work, and app can set defaults later } } - + /** * Empty stub, allowing child classes to override the constructor */ diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/Launcher.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/Launcher.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/Launcher.java Wed May 20 21:17:02 2009 +0100 @@ -331,6 +331,22 @@ throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplication"), R("LNotApplicationInfo"))); try { + final int preferredWidth = 500; + final int preferredHeight = 400; + JNLPSplashScreen splashScreen = null; + URL splashImageURL = file.getInformation().getIconLocation( + IconDesc.SPLASH, preferredWidth, preferredHeight); + if (splashImageURL != null) { + ResourceTracker resourceTracker = new ResourceTracker(true); + resourceTracker.addResource(splashImageURL, "SPLASH", file.getFileVersion(), updatePolicy); + splashScreen = new JNLPSplashScreen(resourceTracker, null, null); + splashScreen.setSplashImageURL(splashImageURL); + if (splashScreen.isSplashScreenValid()) { + splashScreen.setVisible(true); + } + } + + ApplicationInstance app = createApplication(file); app.initialize(); @@ -361,6 +377,13 @@ // required to make some apps work right Thread.currentThread().setContextClassLoader(app.getClassLoader()); + if (splashScreen != null) { + if (splashScreen.isSplashScreenValid()) { + splashScreen.setVisible(false); + } + splashScreen.dispose(); + } + main.invoke(null, new Object[] { args } ); return app; diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/NetxPanel.java Wed May 20 21:17:02 2009 +0100 @@ -42,6 +42,7 @@ private boolean exitOnFailure = true; private AppletInstance appInst = null; private String cookieStr; + private boolean appletAlive; public NetxPanel(URL documentURL, Hashtable atts) { @@ -54,6 +55,7 @@ this(documentURL, atts); this.exitOnFailure = exitOnFailure; this.cookieStr = cookieStr; + this.appletAlive = true; } //Overriding to use Netx classloader. You might need to relax visibility @@ -76,7 +78,7 @@ if (JNLPRuntime.isDebug()) System.out.println("initializing JNLPRuntime..."); - JNLPRuntime.initialize(); + JNLPRuntime.initialize(false); } else { if (JNLPRuntime.isDebug()) System.out.println("JNLPRuntime already initialized"); @@ -121,10 +123,11 @@ validate(); } } catch (Exception e) { + this.appletAlive = false; e.printStackTrace(); } } - + // Reminder: Relax visibility in sun.applet.AppletPanel protected synchronized void createAppletThread() { handler = new Thread(this); @@ -139,5 +142,9 @@ public ClassLoader getAppletClassLoader() { return appInst.getClassLoader(); } + + public boolean isAlive() { + return handler.isAlive() && this.appletAlive; + } } diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java Wed May 20 21:17:02 2009 +0100 @@ -110,9 +110,12 @@ frame.pack(); if (!frame.isVisible()) { - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setLocation(screen.width/2-frame.getWidth()/2, - screen.height/2-frame.getHeight()/2); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(frame.getGraphicsConfiguration()); + Dimension screen = new Dimension(screenSize.width - insets.left , + screenSize.height - insets.top); + frame.setLocation(screen.width-frame.getWidth(), + screen.height-frame.getHeight()); } frame.show(); diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/Boot.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/Boot.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/Boot.java Wed May 20 21:17:02 2009 +0100 @@ -17,17 +17,32 @@ package net.sourceforge.jnlp.runtime; -import java.util.*; -import java.io.*; -import java.net.*; -import java.security.*; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; -import net.sourceforge.jnlp.*; -import net.sourceforge.jnlp.cache.*; -import net.sourceforge.jnlp.runtime.*; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import net.sourceforge.jnlp.AppletDesc; +import net.sourceforge.jnlp.ApplicationDesc; +import net.sourceforge.jnlp.JNLPFile; +import net.sourceforge.jnlp.LaunchException; +import net.sourceforge.jnlp.Launcher; +import net.sourceforge.jnlp.ParseException; +import net.sourceforge.jnlp.PropertyDesc; +import net.sourceforge.jnlp.ResourcesDesc; +import net.sourceforge.jnlp.cache.UpdatePolicy; +import net.sourceforge.jnlp.security.VariableX509TrustManager; import net.sourceforge.jnlp.security.viewer.CertificateViewer; -import net.sourceforge.jnlp.services.*; -import net.sourceforge.jnlp.util.*; +import net.sourceforge.jnlp.services.ServiceUtil; /** * This is the main entry point for the JNLP client. The main @@ -153,6 +168,20 @@ if (null != getOption("-noupdate")) JNLPRuntime.setDefaultUpdatePolicy(UpdatePolicy.NEVER); + + // wire in custom authenticator + try { + SSLSocketFactory sslSocketFactory; + SSLContext context = SSLContext.getInstance("SSL"); + TrustManager[] trust = new TrustManager[] { VariableX509TrustManager.getInstance() }; + context.init(null, trust, null); + sslSocketFactory = context.getSocketFactory(); + + HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory); + } catch (Exception e) { + System.err.println("Unable to set SSLSocketfactory (may _prevent_ access to sites that should be trusted)! Continuing anyway..."); + e.printStackTrace(); + } // do in a privileged action to clear the security context of // the Boot13 class, which doesn't have any privileges in @@ -168,7 +197,7 @@ public Object run() { JNLPRuntime.setBaseDir(getBaseDir()); JNLPRuntime.setSecurityEnabled(null == getOption("-nosecurity")); - JNLPRuntime.initialize(); + JNLPRuntime.initialize(true); try { new Launcher().launch(getFile()); diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/InstallDialog.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/InstallDialog.java Wed May 20 20:50:48 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -// Copyright (C) 2001-2003 Jon A. Maxwell (JAM) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -package net.sourceforge.jnlp.runtime; - -import java.awt.*; -import java.awt.event.*; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.List; -import javax.swing.*; -import javax.jnlp.*; - -import net.sourceforge.jnlp.*; - -/** - * Prompt for an install location. - * - * @author Jon A. Maxwell (JAM) - initial author - * @version $Revision: 1.7 $ - */ -class InstallDialog extends Dialog implements ActionListener { - - private static String R(String key) { return JNLPRuntime.getMessage(key); } - - private ImageIcon background; - private TextField installField = new TextField(); - private Button ok = new Button(R("ButOk")); - private Button cancel = new Button(R("ButCancel")); - private Button browse = new Button(R("ButBrowse")); - private boolean canceled = false; - - static Frame createFrame() { - Frame f = new Frame(); - f.setIconImage(JNLPRuntime.getWindowIcon()); - return f; - } - - InstallDialog() { - super(createFrame(), R("CChooseCache"), true); - - URL icon = (new sun.misc.Launcher()).getClassLoader().getResource("net/sourceforge/jnlp/resources/install.png"); - - background = new ImageIcon(icon); - setLayout(new BorderLayout()); - setBackground(Color.white); - - String home = System.getProperty("java.io.tmpdir"); - installField.setText((new File(home)).toString()); - - ok.addActionListener(this); - cancel.addActionListener(this); - browse.addActionListener(this); - - Font f = new Font("SansSerif", Font.BOLD, 14); - - installField.setFont(f); - ok.setFont(f); - cancel.setFont(f); - browse.setFont(f); - - Panel top = new Panel(new GridLayout(2, 1, 0, 10)); - Label l1 = new Label(R("CChooseCacheInfo")); - Label l2 = new Label(R("CChooseCacheDir")+":"); - l1.setFont(f); - l2.setFont(f); - top.add(l1); - top.add(l2); - - Panel field = new Panel(new BorderLayout()); - field.add(top, BorderLayout.NORTH); - field.add(installField, BorderLayout.CENTER); - field.add(browse, BorderLayout.EAST); - - Panel grid = new Panel(new GridLayout(1, 2, 8, 8)); - grid.add(ok); - grid.add(cancel); - - Panel choice = new Panel(new FlowLayout(FlowLayout.CENTER, 4, 0)); - choice.add(grid); - - add(field, BorderLayout.NORTH); - add(new Panel(), BorderLayout.CENTER); - add(choice, BorderLayout.SOUTH); - } - - public Insets getInsets() { - Insets s = super.getInsets(); - return new Insets(s.top + 55, s.left + 7, s.bottom + 6, s.right + 4); - //return new Insets(s.top + 115, s.left + 7, s.bottom + 6, s.right + 4); - } - - public Dimension getPreferredSize() { - Dimension d = super.getPreferredSize(); - if (background == null) - return d; - - Insets in = super.getInsets(); - d.width = background.getIconWidth() + in.left + in.right; - d.height = Math.max(d.height + 8, background.getIconHeight() + 8 + in.top + in.bottom); - - return d; - } - - public void paint(Graphics g) { - - Insets sin = super.getInsets(); - g.drawImage(background.getImage(), sin.left, sin.top, this); - - } - - public void actionPerformed(ActionEvent evt) { - if (evt.getSource() == cancel) { - canceled = true; - dispose(); - } - if (evt.getSource() == ok) { - File f = new File(installField.getText()); - - try { - f.mkdirs(); - dispose(); - } - catch (Exception ex) { - JOptionPane.showMessageDialog(this, "Install directory cannot be created.", - "ERROR", JOptionPane.ERROR_MESSAGE); - } - } - if (evt.getSource() == browse) { - JFileChooser fc = new JFileChooser(); - fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - int r = fc.showOpenDialog(this); - if (r == JFileChooser.APPROVE_OPTION) - installField.setText(fc.getSelectedFile().toString()); - } - } - - - static File getInstallDir() { - InstallDialog id = new InstallDialog(); - - //id.setResizable(false); - id.pack(); - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - id.setLocation(screen.width/2-id.getWidth()/2, - screen.height/2-id.getHeight()/2); - id.setVisible(true); - - if (id.canceled) - return null; - - return new File(id.installField.getText()); - } - -} - diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPClassLoader.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed May 20 21:17:02 2009 +0100 @@ -20,6 +20,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -344,24 +345,7 @@ //user does not trust this publisher if (!js.getAlreadyTrustPublisher()) { - if (!js.getRootInCacerts()) { //root cert is not in cacerts - boolean b = SecurityWarningDialog.showCertWarningDialog( - SecurityWarningDialog.AccessType.UNVERIFIED, file, js); - if (!b) - throw new LaunchException(null, null, R("LSFatal"), - R("LCLaunching"), R("LNotVerified"), ""); - } else if (js.getRootInCacerts()) { //root cert is in cacerts - boolean b = false; - if (js.noSigningIssues()) - b = SecurityWarningDialog.showCertWarningDialog( - SecurityWarningDialog.AccessType.VERIFIED, file, js); - else if (!js.noSigningIssues()) - b = SecurityWarningDialog.showCertWarningDialog( - SecurityWarningDialog.AccessType.SIGNING_ERROR, file, js); - if (!b) - throw new LaunchException(null, null, R("LSFatal"), - R("LCLaunching"), R("LCancelOnUserRequest"), ""); - } + checkTrustWithUser(js); } else { /** * If the user trusts this publisher (i.e. the publisher's certificate @@ -379,6 +363,27 @@ activateJars(initialJars); } + private void checkTrustWithUser(JarSigner js) throws LaunchException { + if (!js.getRootInCacerts()) { //root cert is not in cacerts + boolean b = SecurityWarningDialog.showCertWarningDialog( + SecurityWarningDialog.AccessType.UNVERIFIED, file, js); + if (!b) + throw new LaunchException(null, null, R("LSFatal"), + R("LCLaunching"), R("LNotVerified"), ""); + } else if (js.getRootInCacerts()) { //root cert is in cacerts + boolean b = false; + if (js.noSigningIssues()) + b = SecurityWarningDialog.showCertWarningDialog( + SecurityWarningDialog.AccessType.VERIFIED, file, js); + else if (!js.noSigningIssues()) + b = SecurityWarningDialog.showCertWarningDialog( + SecurityWarningDialog.AccessType.SIGNING_ERROR, file, js); + if (!b) + throw new LaunchException(null, null, R("LSFatal"), + R("LCLaunching"), R("LCancelOnUserRequest"), ""); + } + } + /** * Add applet's codebase URL. This allows compatibility with * applets that load resources from their codebase instead of @@ -505,8 +510,49 @@ JarFile jarFile = new JarFile(localFile); Enumeration e = jarFile.entries(); - while (e.hasMoreElements()) - jarEntries.add(((JarEntry) e.nextElement()).getName()); + while (e.hasMoreElements()) { + + JarEntry je = (JarEntry) e.nextElement(); + + // another jar in my jar? it is more likely than you think + if (je.getName().endsWith(".jar")) { + // We need to extract that jar so that it can be loaded + // (inline loading with "jar:..!/..." path will not work + // with standard classloader methods) + + String extractedJarLocation = localFile.getParent() + "/" + je.getName(); + FileOutputStream extractedJar = new FileOutputStream(extractedJarLocation); + InputStream is = jarFile.getInputStream(je); + + byte[] bytes = new byte[1024]; + int read = is.read(bytes); + while (read > 0) { + extractedJar.write(bytes, 0, read); + read = is.read(bytes); + } + + is.close(); + extractedJar.close(); + + JarSigner signer = new JarSigner(); + signer.verifyJar(extractedJarLocation); + + if (signer.anyJarsSigned() && !signer.getAlreadyTrustPublisher()) { + checkTrustWithUser(signer); + } + + try { + addURL(new URL("file://" + extractedJarLocation)); + } catch (MalformedURLException mfue) { + if (JNLPRuntime.isDebug()) + System.err.println("Unable to add extracted nested jar to classpath"); + + mfue.printStackTrace(); + } + } + + jarEntries.add(je.getName()); + } } diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPPolicy.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPPolicy.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPPolicy.java Wed May 20 21:17:02 2009 +0100 @@ -65,7 +65,8 @@ PermissionCollection clPermissions = cl.getPermissions(source); // systempolicy permissions need to be accounted for as well - Enumeration e = systemPolicy.getPermissions(source).elements(); + CodeSource appletCS = new CodeSource(JNLPRuntime.getApplication().getJNLPFile().getSourceLocation(), (java.security.cert.Certificate[]) null); + Enumeration e = systemPolicy.getPermissions(appletCS).elements(); while (e.hasMoreElements()) clPermissions.add((Permission) e.nextElement()); diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPRuntime.java Wed May 20 21:17:02 2009 +0100 @@ -94,6 +94,9 @@ /** mutex to wait on, for initialization */ public static Object initMutex = new Object(); + /** set to true if this is a webstart application. */ + private static boolean isWebstartApplication; + /** * Returns whether the JNLP runtime environment has been * initialized. Once initialized, some properties such as the @@ -112,10 +115,14 @@ * initialized, methods that alter the runtime can only be * called by the exit class.

* + * @param isApplication is true if a webstart application is being initialized + * * @throws IllegalStateException if the runtime was previously initialized */ - public static void initialize() throws IllegalStateException { + public static void initialize(boolean isApplication) throws IllegalStateException { checkInitialized(); + + isWebstartApplication = isApplication; if (headless == false) checkHeadless(); @@ -149,6 +156,14 @@ } /** + * Returns true if a webstart application has been initialized, and false + * for a plugin applet. + */ + public static boolean isWebstartApplication() { + return isWebstartApplication; + } + + /** * Returns the window icon. */ public static Image getWindowIcon() { @@ -265,8 +280,9 @@ if (isHeadless()) return null; - File baseDir = InstallDialog.getInstallDir(); - if (baseDir == null) + String homeDir = System.getProperty("user.home"); + File baseDir = new File(homeDir + "/.netx/"); + if (homeDir == null || !baseDir.mkdir()) return null; props.setProperty("basedir", baseDir.toString()); @@ -285,14 +301,14 @@ checkExitClass(); security.setExitClass(exitClass); } - + /** * Disables applets from calling exit. * * Once disabled, exit cannot be re-enabled for the duration of the JVM instance */ public static void disableExit() { - security.disableExit(); + security.disableExit(); } /** diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java Wed May 20 21:17:02 2009 +0100 @@ -103,7 +103,7 @@ /** listener installs the app's classloader on the event dispatch thread */ private ContextUpdater contextListener = new ContextUpdater(); - + /** Sets whether or not exit is allowed (in the context of the plugin, this is always false) */ private boolean exitAllowed = true; @@ -255,13 +255,14 @@ */ public void checkPermission(Permission perm) { String name = perm.getName(); - + // Enable this manually -- it'll produce too much output for -verbose // otherwise. -// if (true) -// System.out.println("Checking permission: " + perm.toString()); - if ("setPolicy".equals(name) || - "setSecurityManager".equals(name)) + // if (true) + // System.out.println("Checking permission: " + perm.toString()); + + if (!JNLPRuntime.isWebstartApplication() && + ("setPolicy".equals(name) || "setSecurityManager".equals(name))) throw new SecurityException(R("RCantReplaceSM")); try { @@ -279,7 +280,7 @@ try { super.checkPermission(perm); } catch (SecurityException se) { - + //This section is a special case for dealing with SocketPermissions. if (JNLPRuntime.isDebug()) System.err.println("Requesting permission: " + perm.toString()); @@ -440,12 +441,13 @@ * behave normally, and the exit class can always exit the JVM. */ public void checkExit(int status) { + // applets are not allowed to exit, but the plugin main class (primordial loader) is Class stack[] = getClassContext(); if (!exitAllowed) { - for (int i=0; i < stack.length; i++) - if (stack[i].getClassLoader() != null) - throw new AccessControlException("Applets may not call System.exit()"); + for (int i=0; i < stack.length; i++) + if (stack[i].getClassLoader() != null) + throw new AccessControlException("Applets may not call System.exit()"); } super.checkExit(status); @@ -482,6 +484,7 @@ protected void disableExit() { exitAllowed = false; } + } diff -r bbac5c54022b -r 8dc304404e01 overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/tools/JarSigner.java --- a/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/tools/JarSigner.java Wed May 20 20:50:48 2009 +0100 +++ b/overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/tools/JarSigner.java Wed May 20 21:17:02 2009 +0100 @@ -218,7 +218,6 @@ String localFile = jarFile.getAbsolutePath(); boolean result = verifyJar(localFile); - checkTrustedCerts(); if (!result) { //allVerified is true until we encounter a problem @@ -241,6 +240,10 @@ boolean hasUnsignedEntry = false; JarInputStream jis = null; + // certs could be uninitialized if one calls this method directly + if (certs == null) + certs = new ArrayList(); + try { jis = new JarInputStream(new FileInputStream(jarName), true); Vector entriesVec = new Vector(); @@ -352,6 +355,9 @@ } } + // check if the certs added above are in the trusted path + checkTrustedCerts(); + //anySigned does not guarantee that all files were signed. return anySigned && !(hasUnsignedEntry || hasExpiredCert || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java Wed May 20 21:17:02 2009 +0100 @@ -0,0 +1,241 @@ +/* PasswordAuthenticationDialog -- requests authentication information from users + Copyright (C) 2009 Red Hat + +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; either version 2, or (at your option) +any later version. + +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 sun.applet; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.PasswordAuthentication; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +/** + * Modal non-minimizable dialog to request http authentication credentials + */ + +public class PasswordAuthenticationDialog extends JDialog { + + private JLabel jlInfo = new JLabel(""); + private JTextField jtfUserName = new JTextField(); + private JPasswordField jpfPassword = new JPasswordField(); + private boolean userCancelled; + + public PasswordAuthenticationDialog() { + initialize(); + } + + /** + * Initialized the dialog components + */ + + public void initialize() { + + setTitle("IcedTea Java Plugin - Authorization needed to proceed"); + + setLayout(new GridBagLayout()); + + JLabel jlUserName = new JLabel("Username: "); + JLabel jlPassword = new JLabel("Password: "); + JButton jbOK = new JButton("OK"); + JButton jbCancel = new JButton("Cancel"); + + jtfUserName.setSize(20, 10); + jpfPassword.setSize(20, 10); + + GridBagConstraints c; + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = new Insets(10, 5, 3, 3); + add(jlInfo, c); + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + add(jlUserName, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + c.weightx = 1.0; + add(jtfUserName, c); + + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + add(jlPassword, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 1.0; + add(jpfPassword, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 70); + c.weightx = 0.0; + add(jbCancel, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 0.0; + add(jbOK, c); + + setMinimumSize(new Dimension(400,150)); + setMaximumSize(new Dimension(1024,150)); + setAlwaysOnTop(true); + + setSize(400,150); + setLocationRelativeTo(null); + + // OK => read supplied info and pass it on + jbOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + // Cancel => discard supplied info and pass on an empty auth + jbCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = true; + dispose(); + } + }); + + // "return" key in either user or password field => OK + + jtfUserName.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + jpfPassword.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + } + + /** + * Present a dialog to the user asking them for authentication information + * + * @param hostThe host for with authentication is needed + * @param port The port being accessed + * @param prompt The prompt (realm) as presented by the server + * @param type The type of server (proxy/web) + * @return PasswordAuthentication containing the credentials (empty credentials if user cancelled) + */ + protected PasswordAuthentication askUser(String host, int port, String prompt, String type) { + PasswordAuthentication auth = null; + + host += port != -1 ? ":" + port : ""; + + // This frame is reusable. So reset everything first. + userCancelled = true; + jlInfo.setText("The " + type + " server at " + host + " is requesting authentication. It says \"" + prompt + "\""); + + try { + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + // show dialog to user + setVisible(true); + } + }); + + PluginDebug.debug("password dialog shown"); + + // wait until dialog is gone + while (this.isShowing()) { + try { + Thread.sleep(200); + } catch (InterruptedException ie) { + } + } + + PluginDebug.debug("password dialog closed"); + + if (!userCancelled) { + auth = new PasswordAuthentication(jtfUserName.getText(), jpfPassword.getText().toCharArray()); + } + } catch (Exception e) { + e.printStackTrace(); + + // Nothing else we can do. Empty auth will be returned + } + + return auth; + } + + public static void main(String[] args) { + PasswordAuthenticationDialog frame = new PasswordAuthenticationDialog(); + + PasswordAuthentication auth = frame.askUser("127.0.0.1", 3128, "Password for local proxy", "proxy"); + + System.err.println("Auth info: " + auth.getUserName() + ":" + new String(auth.getPassword())); + System.exit(0); + } +} diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginAppletSecurityContext.java --- a/plugin/icedtea/sun/applet/PluginAppletSecurityContext.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginAppletSecurityContext.java Wed May 20 21:17:02 2009 +0100 @@ -248,7 +248,7 @@ // an applet will be loaded at some point, we should make it the SM // that JNLPRuntime will try to install if (System.getSecurityManager() == null) { - JNLPRuntime.initialize(); + JNLPRuntime.initialize(/* isApplication */ false); } JNLPRuntime.disableExit(); @@ -1001,7 +1001,7 @@ PluginDebug.debug("target = " + target + " jsSrc=" + jsSrc + " classSrc=" + classSrc); // if src is not a file and class loader does not map to the same base, UniversalBrowserRead (BrowserReadPermission) must be set - if (jsSrc != "file://" && !classSrc.equals(jsSrc)) { + if (!jsSrc.equals("file://") && !jsSrc.equals("[System]") && !classSrc.equals(jsSrc)) { acc.checkPermission(new BrowserReadPermission()); } } diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginAppletViewer.java --- a/plugin/icedtea/sun/applet/PluginAppletViewer.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginAppletViewer.java Wed May 20 21:17:02 2009 +0100 @@ -87,6 +87,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.SocketPermission; +import java.net.URI; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; @@ -99,14 +100,14 @@ import javax.swing.SwingUtilities; -import com.sun.jndi.toolkit.url.UrlUtil; - import net.sourceforge.jnlp.NetxPanel; import net.sourceforge.jnlp.runtime.JNLPClassLoader; import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.X11.XEmbeddedFrame; import sun.misc.Ref; + +import com.sun.jndi.toolkit.url.UrlUtil; /** * Lets us construct one using unix-style one shot behaviors @@ -177,7 +178,7 @@ private static PluginCallRequestFactory requestFactory; private static HashMap siteCookies = new HashMap(); - + private double proposedHeightFactor; private double proposedWidthFactor; @@ -309,7 +310,7 @@ // Wait for the panel to initialize // (happens in a separate thread) Applet a; - while ((a = panel.getApplet()) == null && panel.getAppletHandlerThread().isAlive()) { + while ((a = panel.getApplet()) == null && ((NetxPanel) panel).isAlive()) { try { Thread.sleep(2000); PluginDebug.debug("Waiting for applet to initialize... "); @@ -493,7 +494,7 @@ // Wait for the panel to initialize // (happens in a separate thread) - while ((o = panel.getApplet()) == null && panel.getAppletHandlerThread().isAlive()) { + while ((o = panel.getApplet()) == null && ((NetxPanel) panel).isAlive()) { try { Thread.sleep(2000); PluginDebug.debug("Waiting for applet to initialize..."); @@ -1005,6 +1006,49 @@ return request.getObject(); } + public static Object requestPluginProxyInfo(URI uri) { + + String requestURI = null; + + try { + + // there is no easy way to get SOCKS proxy info. So, we tell mozilla that we want proxy for + // an HTTP uri in case of non http/ftp protocols. If we get back a SOCKS proxy, we can + // use that, if we get back an http proxy, we fallback to DIRECT connect + + String scheme = uri.getScheme(); + String port = uri.getPort() != -1 ? ":" + uri.getPort() : ""; + if (!uri.getScheme().startsWith("http") && !uri.getScheme().equals("ftp")) + scheme = "http"; + + requestURI = UrlUtil.encode(scheme + "://" + uri.getHost() + port + "/" + uri.getPath(), "UTF-8"); + } catch (Exception e) { + PluginDebug.debug("Cannot construct URL from " + uri.toString() + " ... falling back to DIRECT proxy"); + e.printStackTrace(); + return null; + } + + PluginCallRequest request = requestFactory.getPluginCallRequest("proxyinfo", + "plugin PluginProxyInfo " + requestURI, + "plugin"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait call request 1"); + synchronized(request) { + PluginDebug.debug ("wait call request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait call request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" Call DONE"); + return request.getObject(); + } + public static void JavaScriptFinalize(long internal) { // Prefix with dummy instance for convenience. @@ -1591,12 +1635,14 @@ att = att.replace("&", "&"); att = att.replace(" ", "\n"); att = att.replace(" ", "\r"); + att = att.replace(""", "\""); val = val.replace(">", ">"); val = val.replace("<", "<"); val = val.replace("&", "&"); val = val.replace(" ", "\n"); val = val.replace(" ", "\r"); + val = val.replace(""", "\""); PluginDebug.debug("PUT " + att + " = " + val); atts.put(att.toLowerCase(), val); } else { @@ -1677,13 +1723,6 @@ atts.put("type", ((String) atts.get("java_type"))); } - // The attribute codebase isn't what - // we want when not dealing with jars. If its - // defined, remove it in that case. - if(atts.get("archive") == null && atts.get("codebase") != null) { - atts.remove("codebase"); - } - if (atts.get("width") == null || !isInt(atts.get("width"))) { atts.put("width", "1000"); atts.put("widthPercentage", 100); diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginCallRequestFactory.java --- a/plugin/icedtea/sun/applet/PluginCallRequestFactory.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginCallRequestFactory.java Wed May 20 21:17:02 2009 +0100 @@ -49,7 +49,9 @@ return new VoidPluginCallRequest(message, returnString); } else if (id == "window") { return new GetWindowPluginCallRequest(message, returnString); - } else { + } else if (id == "proxyinfo") { + return new PluginProxyInfoRequest(message, returnString); + } else { throw new RuntimeException ("Unknown plugin call request type requested from factory"); } diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginMain.java --- a/plugin/icedtea/sun/applet/PluginMain.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginMain.java Wed May 20 21:17:02 2009 +0100 @@ -67,16 +67,18 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; -import java.net.Socket; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.ProxySelector; import java.util.Enumeration; +import java.util.HashMap; import java.util.Properties; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; -import javax.net.ssl.HttpsURLConnection; - import net.sourceforge.jnlp.security.VariableX509TrustManager; /** @@ -212,6 +214,10 @@ System.err.println("Unable to set SSLSocketfactory (may _prevent_ access to sites that should be trusted)! Continuing anyway..."); e.printStackTrace(); } + + // plug in a custom authenticator and proxy selector + Authenticator.setDefault(new CustomAuthenticator()); + ProxySelector.setDefault(new PluginProxySelector()); } static boolean messageAvailable() { @@ -221,6 +227,26 @@ static String getMessage() { return streamHandler.getMessage(); } + + static class CustomAuthenticator extends Authenticator { + + public PasswordAuthentication getPasswordAuthentication() { + + // No security check is required here, because the only way to + // set parameters for which auth info is needed + // (Authenticator:requestPasswordAuthentication()), has a security + // check + + String type = this.getRequestorType() == RequestorType.PROXY ? "proxy" : "web"; + + // request auth info from user + PasswordAuthenticationDialog pwDialog = new PasswordAuthenticationDialog(); + PasswordAuthentication auth = pwDialog.askUser(this.getRequestingHost(), this.getRequestingPort(), this.getRequestingPrompt(), type); + + // send it along + return auth; + } + } /** * Behaves like the 'tee' command, sends output to both actual std stream and a @@ -264,7 +290,9 @@ @Override public void write(byte[] buf, int off, int len) { logFile.write(buf, off, len); - super.write(buf, off, len); + + if (!redirectStreams) + super.write(buf, off, len); } @Override diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginMessageConsumer.java --- a/plugin/icedtea/sun/applet/PluginMessageConsumer.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginMessageConsumer.java Wed May 20 21:17:02 2009 +0100 @@ -35,7 +35,6 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ - package sun.applet; import java.util.ArrayList; @@ -45,17 +44,19 @@ class PluginMessageConsumer { - int MAX_WORKERS = 3; + int MAX_WORKERS = 20; LinkedList readQueue = new LinkedList(); ArrayList workers = new ArrayList(); PluginStreamHandler streamHandler = null; + AppletSecurity as; public PluginMessageConsumer(PluginStreamHandler streamHandler) { - AppletSecurity as = new AppletSecurity(); + as = new AppletSecurity(); this.streamHandler = streamHandler; - for (int i=0; i < MAX_WORKERS; i++) { + // create some workers at the start... + for (int i=0; i < 3; i++) { PluginDebug.debug("Creating worker " + i); PluginMessageHandlerWorker worker = new PluginMessageHandlerWorker(streamHandler, i, as); worker.start(); @@ -96,6 +97,19 @@ return worker; } } + + // If we have less than MAX_WORKERS, create a new worker + if (workers.size() < MAX_WORKERS) { + PluginDebug.debug("Cannot find free worker, creating worker " + workers.size()); + PluginMessageHandlerWorker worker = new PluginMessageHandlerWorker(streamHandler, workers.size(), as); + worker.start(); + workers.add(worker); + worker.busy(); + return worker; + } else { + // else wait + } + Thread.yield(); } diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginProxyInfoRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedtea/sun/applet/PluginProxyInfoRequest.java Wed May 20 21:17:02 2009 +0100 @@ -0,0 +1,85 @@ +/* PluginProxyInfoRequest -- Object representing a request for proxy information from the browser + Copyright (C) 2009 Red Hat + +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; either version 2, or (at your option) +any later version. + +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 sun.applet; + +import java.net.MalformedURLException; +import java.net.URI; + +/** + * This class represents a request object for proxy information for a given URI + */ + +public class PluginProxyInfoRequest extends PluginCallRequest { + + URI internal = null; + + public PluginProxyInfoRequest(String message, String returnString) { + super(message, returnString); + } + + public void parseReturn(String proxyInfo) { + + // try to parse the proxy information. If things go wrong, do nothing .. + // this will keep internal = null which forces a direct connection + + PluginDebug.debug ("PluginProxyInfoRequest GOT: " + proxyInfo); + String[] messageComponents = proxyInfo.split(" "); + + try { + internal = new URI(messageComponents[2], null, messageComponents[3], Integer.parseInt(messageComponents[4]), null, null, null); + } catch (Exception e) { + // do nothing + } + + setDone(true); + } + + /** + * Returns whether the given message is serviceable by this object + * + * @param message The message to service + * @return boolean indicating if message is serviceable + */ + public boolean serviceable(String message) { + return message.startsWith("plugin PluginProxyInfo"); + } + + public URI getObject() { + return this.internal; + } +} diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginProxySelector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedtea/sun/applet/PluginProxySelector.java Wed May 20 21:17:02 2009 +0100 @@ -0,0 +1,195 @@ +/* PluginProxySelector -- proxy selector for all connections from applets and the plugin + Copyright (C) 2009 Red Hat + +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; either version 2, or (at your option) +any later version. + +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 sun.applet; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.Date; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Proxy selector implementation for plugin network functions. + * + * This class fetches proxy information from the web browser and + * uses that information in the context of all network connection + * (plugin specific and applet connections) as applicable + * + */ + +public class PluginProxySelector extends ProxySelector { + + private TimedHashMap proxyCache = new TimedHashMap(); + + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + // If the connection fails, there is little we can do here. Just print the exception + ioe.printStackTrace(); + } + + /** + * Selects the appropriate proxy (or DIRECT connection method) for the given URI + * + * @param uri The URI being accessed + * @return A list of Proxy objects that are usable for this URI + */ + @Override + public List select(URI uri) { + + List proxyList = new ArrayList(); + + // check cache first + Proxy cachedProxy = checkCache(uri); + if (cachedProxy != null) { + proxyList.add(cachedProxy); + return proxyList; + } + + // Nothing usable in cache. Fetch info from browser + Proxy proxy = Proxy.NO_PROXY; + Object o = PluginAppletViewer.requestPluginProxyInfo(uri); + + // If the browser returned anything, try to parse it. If anything in the try block fails, the fallback is direct connection + try { + if (o != null) { + PluginDebug.debug("Proxy URI = " + o); + URI proxyURI = (URI) o; + + // If origin uri is http/ftp, we're good. If origin uri is not that, the proxy _must_ be socks, else we fallback to direct + if (uri.getScheme().startsWith("http") || uri.getScheme().equals("ftp") || proxyURI.getScheme().startsWith("socks")) { + + Proxy.Type type = proxyURI.getScheme().equals("http") ? Proxy.Type.HTTP : Proxy.Type.SOCKS; + InetSocketAddress socketAddr = new InetSocketAddress(proxyURI.getHost(), proxyURI.getPort()); + + proxy = new Proxy(type, socketAddr); + + String uriKey = uri.getScheme() + "://" + uri.getHost(); + proxyCache.put(uriKey, proxy); + } else { + PluginDebug.debug("Proxy " + proxyURI + " cannot be used for " + uri + ". Falling back to DIRECT"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + proxyList.add(proxy); + + PluginDebug.debug("Proxy for " + uri.toString() + " is " + proxy); + + return proxyList; + } + + /** + * Checks to see if proxy information is already cached. + * + * @param uri The URI to check + * @return The cached Proxy. null if there is no suitable cached proxy. + */ + private Proxy checkCache(URI uri) { + + String uriKey = uri.getScheme() + "://" + uri.getHost(); + if (proxyCache.get(uriKey) != null) { + return proxyCache.get(uriKey); + } + + return null; + } + + /** + * Simple utility class that extends HashMap by adding an expiry to the entries. + * + * This map stores entries, and returns them only if the entries were last accessed within time t=10 seconds + * + * @param The key type + * @param The Object type + */ + + private class TimedHashMap extends HashMap { + + HashMap timeStamps = new HashMap(); + Long expiry = 10000L; + + /** + * Store the item in the map and associate a timestamp with it + * + * @param key The key + * @param value The value to store + */ + public V put(K key, V value) { + timeStamps.put(key, new Date().getTime()); + return super.put(key, value); + } + + /** + * Return cached item if it has not already expired. + * + * Before returning, this method also resets the "last accessed" + * time for this entry, so it is good for another 10 seconds + * + * @param key The key + */ + public V get(Object key) { + + Long now = new Date().getTime(); + + if (super.containsKey(key)) { + Long age = now - timeStamps.get(key); + + // Item exists. If it has not expired, renew its access time and return it + if (age <= expiry) { + PluginDebug.debug("Returning proxy " + super.get(key) + " from cache for " + key); + timeStamps.put((K) key, (new Date()).getTime()); + return super.get(key); + } else { + PluginDebug.debug("Proxy cache for " + key + " has expired (age=" + age/1000.0 + " seconds)"); + } + } + + return null; + } + } + +} diff -r bbac5c54022b -r 8dc304404e01 plugin/icedtea/sun/applet/PluginStreamHandler.java --- a/plugin/icedtea/sun/applet/PluginStreamHandler.java Wed May 20 20:50:48 2009 +0100 +++ b/plugin/icedtea/sun/applet/PluginStreamHandler.java Wed May 20 21:17:02 2009 +0100 @@ -134,7 +134,7 @@ long b4 = new Date().getTime(); String s = read(); - + long after = new Date().getTime(); totalWait += (after - b4); @@ -228,7 +228,12 @@ if (msgComponents.length < 2) return; - + + if (msgComponents[0].startsWith("plugin")) { + handlePluginMessage(message); + return; + } + // type and identifier are guaranteed to be there String type = msgComponents[0]; final int identifier = Integer.parseInt(msgComponents[1]); @@ -297,6 +302,17 @@ } } + private void handlePluginMessage(String message) { + if (message.equals("plugin showconsole")) { + showConsole(); + } else if (message.equals("plugin hideconsole")) { + hideConsole(); + } else { + // else this is something that was specifically requested + finishCallRequest(message); + } + } + public void postCallRequest(PluginCallRequest request) { synchronized(queue) { queue.post(request); @@ -321,6 +337,8 @@ while (!request.serviceable(message)) { + PluginDebug.debug(request + " cannot service " + message); + // something is very wrong.. we have a message to // process, but no one to service it if (count >= size) { @@ -382,10 +400,6 @@ AppletSecurityContextManager.dumpStore(0); PluginDebug.debug("APPLETVIEWER: exiting appletviewer"); System.exit(0); - } else if (message.equals("showconsole")) { - showConsole(); - } else if (message.equals("hideconsole")) { - hideConsole(); } } catch (IOException e) { e.printStackTrace();