changeset 1462:300bff72109a

Proxy support for the icedtea plugin
author Deepak Bhole <dbhole@redhat.com>
date Thu, 02 Apr 2009 14:55:59 -0400
parents 5e6be75f137c
children 5b9c566ab513
files ChangeLog IcedTeaPlugin.cc plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java plugin/icedtea/sun/applet/PluginAppletViewer.java plugin/icedtea/sun/applet/PluginCallRequestFactory.java plugin/icedtea/sun/applet/PluginMain.java plugin/icedtea/sun/applet/PluginProxyInfoRequest.java plugin/icedtea/sun/applet/PluginProxySelector.java plugin/icedtea/sun/applet/PluginStreamHandler.java rt/net/sourceforge/jnlp/JNLPFile.java rt/net/sourceforge/jnlp/NetxPanel.java
diffstat 11 files changed, 808 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Apr 02 11:19:02 2009 -0400
+++ b/ChangeLog	Thu Apr 02 14:55:59 2009 -0400
@@ -1,3 +1,26 @@
+2009-04-02 Deepak Bhole <dbhole@redhat.com>
+
+	* 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.
+	* rt/net/sourceforge/jnlp/NetxPanel.java: Add support for improved
+	initialization failure detection.
+
 2009-04-02  Lillian Angel  <langel@redhat.com>
 
 	* patches/icedtea-lcms.patch: Updated with most recent security fixes.
--- a/IcedTeaPlugin.cc	Thu Apr 02 11:19:02 2009 -0400
+++ b/IcedTeaPlugin.cc	Thu Apr 02 14:55:59 2009 -0400
@@ -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__)
@@ -1007,6 +1007,7 @@
   nsresult StartAppletviewer ();
   void ProcessMessage();
   void ConsumeMsgFromJVM();
+  nsresult GetProxyInfo(const char* siteAddr, char** proxyScheme, char** proxyHost, char** proxyPort);
   nsCOMPtr<IcedTeaEventSink> sink;
   nsCOMPtr<nsISocketTransport> transport;
   nsCOMPtr<nsIProcess> applet_viewer_process;
@@ -1792,7 +1793,7 @@
 NS_IMETHODIMP
 IcedTeaPluginFactory::Show (void)
 {
-  nsCString msg("showconsole");
+  nsCString msg("plugin showconsole");
   this->SendMessageToAppletViewer(msg);
   return NS_OK;
 }
@@ -1800,7 +1801,7 @@
 NS_IMETHODIMP
 IcedTeaPluginFactory::Hide (void)
 {
-  nsCString msg("hideconsole");
+  nsCString msg("plugin hideconsole");
   this->SendMessageToAppletViewer(msg);
   return NS_OK;
 }
@@ -2658,6 +2659,96 @@
   return factory->GetJavaObject (instance_identifier, object);
 }
 
+#include <nsIDNSRecord.h>
+#include <nsIDNSService.h>
+#include <nsIHttpAuthManager.h>
+#include <nsIProxyInfo.h>
+#include <nsIProtocolProxyService.h>
+#include <nsILoginManager.h>
+#include <nsILoginInfo.h>
+
+/** 
+ *
+ * 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<nsIProtocolProxyService> proxy_svc = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+
+  if (!proxy_svc) {
+	  printf("Cannot initialize proxy service\n");
+	  return rv;
+  }
+
+  nsCOMPtr<nsIIOService> 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<nsIURI> uri;
+  io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
+
+  // find the proxy address if any
+  nsCOMPtr<nsIProxyInfo> 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<nsIDNSService> dns_svc = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+
+  if (!dns_svc) {
+      printf("Cannot initialize DNS service\n");
+      return rv;
+  }
+
+  nsCOMPtr<nsIDNSRecord> 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 +2768,8 @@
     return NS_ERROR_FAILURE;
   }
 
-  nsIURI *uri;
-  io_svc->NewURI(nsCString(siteAddr), NULL, NULL, &uri);
+  nsCOMPtr<nsIURI> uri;
+  io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
 
   nsCOMPtr<nsICookieService> cookie_svc = do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
 
@@ -2902,6 +2993,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 +3008,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);
@@ -3344,6 +3445,49 @@
       // Do nothing for: SetStaticField, SetField, ExceptionClear,
       // DeleteGlobalRef, DeleteLocalRef
     }
+	else if (prefix == "plugin")
+    {
+
+        if (command == "PluginProxyInfo") {
+
+          nsresult rv;
+          nsCOMPtr<nsINetUtil> 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 ()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedtea/sun/applet/PasswordAuthenticationDialog.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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("<html>The " + type + " server at " + host + " is requesting authentication. It says \"" + prompt + "\"</html>");
+
+        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);
+    }
+}
--- a/plugin/icedtea/sun/applet/PluginAppletViewer.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/plugin/icedtea/sun/applet/PluginAppletViewer.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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<Integer, String> siteCookies = new HashMap<Integer,String>();
-
+     
      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.
--- a/plugin/icedtea/sun/applet/PluginCallRequestFactory.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/plugin/icedtea/sun/applet/PluginCallRequestFactory.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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");
 		}
 		
--- a/plugin/icedtea/sun/applet/PluginMain.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/plugin/icedtea/sun/applet/PluginMain.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedtea/sun/applet/PluginProxyInfoRequest.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedtea/sun/applet/PluginProxySelector.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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<String, Proxy> proxyCache = new TimedHashMap<String, Proxy>(); 
+
+
+    @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<Proxy> select(URI uri) {
+
+        List<Proxy> proxyList = new ArrayList<Proxy>();
+
+        // 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 <K> The key type
+     * @param <V> The Object type
+     */
+
+    private class TimedHashMap<K,V> extends HashMap<K,V> {
+
+        HashMap<K, Long> timeStamps = new HashMap<K, Long>();
+        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;
+        }
+    }
+    
+}
--- a/plugin/icedtea/sun/applet/PluginStreamHandler.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/plugin/icedtea/sun/applet/PluginStreamHandler.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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();
--- a/rt/net/sourceforge/jnlp/JNLPFile.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/rt/net/sourceforge/jnlp/JNLPFile.java	Thu Apr 02 14:55:59 2009 -0400
@@ -106,7 +106,7 @@
             // null values will still work, and app can set defaults later
         }
     }
-
+    
     /**
      * Empty stub, allowing child classes to override the constructor
      */
--- a/rt/net/sourceforge/jnlp/NetxPanel.java	Thu Apr 02 11:19:02 2009 -0400
+++ b/rt/net/sourceforge/jnlp/NetxPanel.java	Thu Apr 02 14:55:59 2009 -0400
@@ -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
@@ -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;
+    }
 }