001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Johannes Lehtinen 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package com.izforge.izpack.installer; 023 024import java.awt.BorderLayout; 025import java.awt.Component; 026import java.awt.Cursor; 027import java.awt.GridLayout; 028import java.awt.Toolkit; 029import java.io.InputStream; 030import java.net.Authenticator; 031import java.net.ConnectException; 032import java.net.InetAddress; 033import java.net.PasswordAuthentication; 034import java.net.URL; 035import java.net.URLConnection; 036import java.util.Locale; 037 038import javax.swing.JDialog; 039import javax.swing.JLabel; 040import javax.swing.JOptionPane; 041import javax.swing.JPanel; 042import javax.swing.JPasswordField; 043import javax.swing.JTextField; 044import javax.swing.UIManager; 045 046/** 047 * Dialogs for password authentication and firewall specification, when needed, during web 048 * installation. 049 * 050 * @author Chadwick McHenry 051 * @version 1.0 052 */ 053public class WebAccessor 054{ 055 056 private Thread openerThread = null; 057 058 private InputStream iStream = null; 059 060 private Exception exception = null; 061 062 private Object soloCancelOption = null; 063 064 private Component parent = null; 065 066 private JDialog dialog = null; 067 068 private boolean tryProxy = false; 069 070 private JPanel passwordPanel = null; 071 072 private JLabel promptLabel; 073 074 private JTextField nameField; 075 076 private JPasswordField passField; 077 078 private JPanel proxyPanel = null; 079 080 private JLabel errorLabel; 081 082 private JTextField hostField; 083 084 private JTextField portField; 085 086 /** 087 * Not yet Implemented: placeholder for headless installs. 088 * 089 * @throws UnsupportedOperationException 090 */ 091 public WebAccessor() 092 { 093 // the class should probably be rearranged to do this. 094 throw new UnsupportedOperationException(); 095 } 096 097 /** 098 * Create a WebAccessor that prompts for proxies and passwords using a JDialog. 099 * 100 * @param parent determines the frame in which the dialog is displayed; if the parentComponent 101 * has no Frame, a default Frame is used 102 */ 103 public WebAccessor(Component parent) 104 { 105 this.parent = parent; 106 Locale l = null; 107 if (parent != null) parent.getLocale(); 108 soloCancelOption = UIManager.get("OptionPane.cancelButtonText", l);// TODO: 109 // i18n? 110 Authenticator.setDefault(new MyDialogAuthenticator()); 111 } 112 113 /** 114 * Opens a URL connection and returns it's InputStream for the specified URL. 115 * 116 * @param url the url to open the stream to. 117 * @return an input stream ready to read, or null on failure 118 */ 119 public InputStream openInputStream(URL url) 120 { 121 // TODO: i18n everything 122 Object[] options = { soloCancelOption}; 123 JOptionPane pane = new JOptionPane("Connecting to the Internet", 124 JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, 125 options[0]); 126 dialog = pane.createDialog(parent, "Accessing Install Files"); 127 pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 128 129 Object value = null; 130 OPEN_URL: while (true) 131 { 132 startOpening(url); // this starts a thread that may dismiss the 133 // dialog before user 134 dialog.setVisible(true); 135 value = pane.getValue(); 136 137 // dialog closed or canceled (by widget) 138 if (value == null || value == soloCancelOption) 139 { 140 try 141 { 142 openerThread.interrupt();// stop the connection 143 } 144 catch (Exception e) 145 {} 146 iStream = null; // even if connection was made just after cancel 147 break; 148 } 149 150 // dialog closed by thread so either a connection error or success! 151 else if (value == JOptionPane.UNINITIALIZED_VALUE) 152 { 153 // success! 154 if (iStream != null) break; 155 156 // System.err.println(exception); 157 158 // an exception we don't expect setting a proxy to fix 159 if (!tryProxy) break; 160 161 // else (exception != null) 162 // show proxy dialog until valid values or cancel 163 JPanel panel = getProxyPanel(); 164 errorLabel.setText("Unable to connect: " + exception.getMessage()); 165 while (true) 166 { 167 int result = JOptionPane.showConfirmDialog(parent, panel, 168 "Proxy Configuration", JOptionPane.OK_CANCEL_OPTION, 169 JOptionPane.QUESTION_MESSAGE); 170 if (result != JOptionPane.OK_OPTION) // canceled 171 break OPEN_URL; 172 173 String host = null; 174 String port = null; 175 176 try 177 { 178 InetAddress addr = InetAddress.getByName(hostField.getText()); 179 host = addr.getHostName(); 180 } 181 catch (Exception x) 182 { 183 errorLabel.setText("Unable to resolve Host"); 184 Toolkit.getDefaultToolkit().beep(); 185 } 186 187 try 188 { 189 if (host != null) port = Integer.valueOf(portField.getText()).toString(); 190 } 191 catch (NumberFormatException x) 192 { 193 errorLabel.setText("Invalid Port"); 194 Toolkit.getDefaultToolkit().beep(); 195 } 196 197 if (host != null && port != null) 198 { 199 // System.err.println ("Setting http proxy: "+ host 200 // +":"+ port); 201 System.getProperties().put("proxySet", "true"); 202 System.getProperties().put("proxyHost", host); 203 System.getProperties().put("proxyPort", port); 204 break; 205 } 206 } 207 } 208 } 209 return iStream; 210 } 211 212 private void startOpening(final URL url) 213 { 214 openerThread = new Thread() { 215 216 public void run() 217 { 218 iStream = null; 219 try 220 { 221 tryProxy = false; 222 URLConnection connection = url.openConnection(); 223 iStream = connection.getInputStream(); // just to make 224 // connection 225 226 } 227 catch (ConnectException x) 228 { // could be an incorrect proxy 229 tryProxy = true; 230 exception = x; 231 232 } 233 catch (Exception x) 234 { 235 // Exceptions that get here are considered cancels or 236 // missing 237 // pages, eg 401 if user finally cancels auth 238 exception = x; 239 240 } 241 finally 242 { 243 // if dialog is in use, allow it to become visible /before/ 244 // closing 245 // it, else on /fast/ connectinos, it may open later and 246 // hang! 247 if (dialog != null) 248 { 249 Thread.yield(); 250 dialog.setVisible(false); 251 } 252 } 253 } 254 }; 255 openerThread.start(); 256 } 257 258 /** 259 * Only to be called after an initial error has indicated a connection problem 260 */ 261 private JPanel getProxyPanel() 262 { 263 if (proxyPanel == null) 264 { 265 proxyPanel = new JPanel(new BorderLayout(5, 5)); 266 267 errorLabel = new JLabel(); 268 269 JPanel fields = new JPanel(new GridLayout(2, 2)); 270 String h = (String) System.getProperties().get("proxyHost"); 271 String p = (String) System.getProperties().get("proxyPort"); 272 hostField = new JTextField(h != null ? h : ""); 273 portField = new JTextField(p != null ? p : ""); 274 JLabel host = new JLabel("Host: "); // TODO: i18n 275 JLabel port = new JLabel("Port: "); // TODO: i18n 276 fields.add(host); 277 fields.add(hostField); 278 fields.add(port); 279 fields.add(portField); 280 281 JLabel exampleLabel = new JLabel("e.g. host=\"gatekeeper.example.com\" port=\"80\""); 282 283 proxyPanel.add(errorLabel, BorderLayout.NORTH); 284 proxyPanel.add(fields, BorderLayout.CENTER); 285 proxyPanel.add(exampleLabel, BorderLayout.SOUTH); 286 } 287 proxyPanel.validate(); 288 289 return proxyPanel; 290 } 291 292 private JPanel getPasswordPanel() 293 { 294 if (passwordPanel == null) 295 { 296 passwordPanel = new JPanel(new BorderLayout(5, 5)); 297 298 promptLabel = new JLabel(); 299 300 JPanel fields = new JPanel(new GridLayout(2, 2)); 301 nameField = new JTextField(); 302 passField = new JPasswordField(); 303 JLabel name = new JLabel("Name: "); // TODO: i18n 304 JLabel pass = new JLabel("Password: "); // TODO: i18n 305 fields.add(name); 306 fields.add(nameField); 307 fields.add(pass); 308 fields.add(passField); 309 310 passwordPanel.add(promptLabel, BorderLayout.NORTH); 311 passwordPanel.add(fields, BorderLayout.CENTER); 312 } 313 passField.setText(""); 314 315 return passwordPanel; 316 } 317 318 /** 319 * Authenticates via dialog when needed. 320 */ 321 private class MyDialogAuthenticator extends Authenticator 322 { 323 324 public PasswordAuthentication getPasswordAuthentication() 325 { 326 // TODO: i18n 327 JPanel p = getPasswordPanel(); 328 String prompt = getRequestingPrompt(); 329 InetAddress addr = getRequestingSite(); 330 if (addr != null) prompt += " (" + addr.getHostName() + ")"; 331 promptLabel.setText(prompt); 332 int result = JOptionPane.showConfirmDialog(parent, p, "Enter Password", 333 JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); 334 if (result != JOptionPane.OK_OPTION) return null; 335 336 return new PasswordAuthentication(nameField.getText(), passField.getPassword()); 337 } 338 }; 339}