001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.preferences.server; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 import static org.openstreetmap.josm.tools.I18n.trc; 006 007 import java.awt.Component; 008 import java.awt.Dimension; 009 import java.awt.GridBagConstraints; 010 import java.awt.GridBagLayout; 011 import java.awt.Insets; 012 import java.awt.event.ItemEvent; 013 import java.awt.event.ItemListener; 014 import java.net.PasswordAuthentication; 015 import java.net.ProxySelector; 016 import java.net.Authenticator.RequestorType; 017 import java.util.HashMap; 018 import java.util.Map; 019 020 import javax.swing.BorderFactory; 021 import javax.swing.ButtonGroup; 022 import javax.swing.JLabel; 023 import javax.swing.JPanel; 024 import javax.swing.JPasswordField; 025 import javax.swing.JRadioButton; 026 import javax.swing.JTextField; 027 028 import org.openstreetmap.josm.Main; 029 import org.openstreetmap.josm.gui.JMultilineLabel; 030 import org.openstreetmap.josm.gui.help.HelpUtil; 031 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 032 import org.openstreetmap.josm.io.DefaultProxySelector; 033 import org.openstreetmap.josm.io.auth.CredentialsAgent; 034 import org.openstreetmap.josm.io.auth.CredentialsAgentException; 035 import org.openstreetmap.josm.io.auth.CredentialsManager; 036 import org.openstreetmap.josm.tools.GBC; 037 038 public class ProxyPreferencesPanel extends VerticallyScrollablePanel { 039 040 public enum ProxyPolicy { 041 NO_PROXY("no-proxy"), 042 USE_SYSTEM_SETTINGS("use-system-settings"), 043 USE_HTTP_PROXY("use-http-proxy"), 044 USE_SOCKS_PROXY("use-socks-proxy"); 045 046 private String policyName; 047 ProxyPolicy(String policyName) { 048 this.policyName = policyName; 049 } 050 051 public String getName() { 052 return policyName; 053 } 054 055 static public ProxyPolicy fromName(String policyName) { 056 if (policyName == null) return null; 057 policyName = policyName.trim().toLowerCase(); 058 for(ProxyPolicy pp: values()) { 059 if (pp.getName().equals(policyName)) 060 return pp; 061 } 062 return null; 063 } 064 } 065 066 public static final String PROXY_POLICY = "proxy.policy"; 067 public static final String PROXY_HTTP_HOST = "proxy.http.host"; 068 public static final String PROXY_HTTP_PORT = "proxy.http.port"; 069 public static final String PROXY_SOCKS_HOST = "proxy.socks.host"; 070 public static final String PROXY_SOCKS_PORT = "proxy.socks.port"; 071 public static final String PROXY_USER = "proxy.user"; 072 public static final String PROXY_PASS = "proxy.pass"; 073 074 private ButtonGroup bgProxyPolicy; 075 private Map<ProxyPolicy, JRadioButton> rbProxyPolicy; 076 private JTextField tfProxyHttpHost; 077 private JTextField tfProxyHttpPort; 078 private JTextField tfProxySocksHost; 079 private JTextField tfProxySocksPort; 080 private JTextField tfProxyHttpUser; 081 private JPasswordField tfProxyHttpPassword; 082 083 private JPanel pnlHttpProxyConfigurationPanel; 084 private JPanel pnlSocksProxyConfigurationPanel; 085 086 /** 087 * Builds the panel for the HTTP proxy configuration 088 * 089 * @return 090 */ 091 protected JPanel buildHttpProxyConfigurationPanel() { 092 JPanel pnl = new JPanel(new GridBagLayout()) { 093 @Override 094 public Dimension getMinimumSize() { 095 return getPreferredSize(); 096 } 097 }; 098 GridBagConstraints gc = new GridBagConstraints(); 099 100 gc.anchor = GridBagConstraints.WEST; 101 gc.insets = new Insets(5,5,0,0); 102 gc.fill = GridBagConstraints.HORIZONTAL; 103 gc.weightx = 0.0; 104 pnl.add(new JLabel(tr("Host:")), gc); 105 106 gc.gridx = 1; 107 gc.weightx = 1.0; 108 pnl.add(tfProxyHttpHost = new JTextField(),gc); 109 110 gc.gridy = 1; 111 gc.gridx = 0; 112 gc.fill = GridBagConstraints.NONE; 113 gc.weightx = 0.0; 114 pnl.add(new JLabel(trc("server", "Port:")), gc); 115 116 gc.gridx = 1; 117 gc.weightx = 1.0; 118 pnl.add(tfProxyHttpPort = new JTextField(5),gc); 119 tfProxyHttpPort.setMinimumSize(tfProxyHttpPort.getPreferredSize()); 120 121 gc.gridy = 2; 122 gc.gridx = 0; 123 gc.gridwidth = 2; 124 gc.fill = GridBagConstraints.HORIZONTAL; 125 gc.weightx = 1.0; 126 pnl.add(new JMultilineLabel(tr("Please enter a username and a password if your proxy requires authentication.")), gc); 127 128 gc.gridy = 3; 129 gc.gridx = 0; 130 gc.gridwidth = 1; 131 gc.fill = GridBagConstraints.NONE; 132 gc.weightx = 0.0; 133 pnl.add(new JLabel(tr("User:")), gc); 134 135 gc.gridy = 3; 136 gc.gridx = 1; 137 gc.weightx = 1.0; 138 pnl.add(tfProxyHttpUser = new JTextField(20),gc); 139 tfProxyHttpUser.setMinimumSize(tfProxyHttpUser.getPreferredSize()); 140 141 gc.gridy = 4; 142 gc.gridx = 0; 143 gc.weightx = 0.0; 144 pnl.add(new JLabel(tr("Password:")), gc); 145 146 gc.gridx = 1; 147 gc.weightx = 1.0; 148 pnl.add(tfProxyHttpPassword = new JPasswordField(20),gc); 149 tfProxyHttpPassword.setMinimumSize(tfProxyHttpPassword.getPreferredSize()); 150 151 // add an extra spacer, otherwise the layout is broken 152 gc.gridy = 5; 153 gc.gridx = 0; 154 gc.gridwidth = 2; 155 gc.fill = GridBagConstraints.BOTH; 156 gc.weightx = 1.0; 157 gc.weighty = 1.0; 158 pnl.add(new JPanel(), gc); 159 return pnl; 160 } 161 162 /** 163 * Builds the panel for the SOCKS proxy configuration 164 * 165 * @return 166 */ 167 protected JPanel buildSocksProxyConfigurationPanel() { 168 JPanel pnl = new JPanel(new GridBagLayout()) { 169 @Override 170 public Dimension getMinimumSize() { 171 return getPreferredSize(); 172 } 173 }; 174 GridBagConstraints gc = new GridBagConstraints(); 175 gc.anchor = GridBagConstraints.WEST; 176 gc.insets = new Insets(5,5,0,0); 177 gc.fill = GridBagConstraints.HORIZONTAL; 178 gc.weightx = 0.0; 179 pnl.add(new JLabel(tr("Host:")), gc); 180 181 gc.gridx = 1; 182 gc.weightx = 1.0; 183 pnl.add(tfProxySocksHost = new JTextField(20),gc); 184 185 gc.gridy = 1; 186 gc.gridx = 0; 187 gc.weightx = 0.0; 188 gc.fill = GridBagConstraints.NONE; 189 pnl.add(new JLabel(trc("server", "Port:")), gc); 190 191 gc.gridx = 1; 192 gc.weightx = 1.0; 193 pnl.add(tfProxySocksPort = new JTextField(5), gc); 194 tfProxySocksPort.setMinimumSize(tfProxySocksPort.getPreferredSize()); 195 196 // add an extra spacer, otherwise the layout is broken 197 gc.gridy = 2; 198 gc.gridx = 0; 199 gc.gridwidth = 2; 200 gc.fill = GridBagConstraints.BOTH; 201 gc.weightx = 1.0; 202 gc.weighty = 1.0; 203 pnl.add(new JPanel(), gc); 204 return pnl; 205 } 206 207 protected JPanel buildProxySettingsPanel() { 208 JPanel pnl = new JPanel(new GridBagLayout()); 209 GridBagConstraints gc = new GridBagConstraints(); 210 211 bgProxyPolicy = new ButtonGroup(); 212 rbProxyPolicy = new HashMap<ProxyPolicy, JRadioButton>(); 213 ProxyPolicyChangeListener policyChangeListener = new ProxyPolicyChangeListener(); 214 for (ProxyPolicy pp: ProxyPolicy.values()) { 215 rbProxyPolicy.put(pp, new JRadioButton()); 216 bgProxyPolicy.add(rbProxyPolicy.get(pp)); 217 rbProxyPolicy.get(pp).addItemListener(policyChangeListener); 218 } 219 220 // radio button "No proxy" 221 gc.gridx = 0; 222 gc.gridy = 0; 223 gc.fill = GridBagConstraints.HORIZONTAL; 224 gc.anchor = GridBagConstraints.NORTHWEST; 225 gc.weightx = 0.0; 226 pnl.add(rbProxyPolicy.get(ProxyPolicy.NO_PROXY),gc); 227 228 gc.gridx = 1; 229 gc.weightx = 1.0; 230 pnl.add(new JLabel(tr("No proxy")), gc); 231 232 // radio button "System settings" 233 gc.gridx = 0; 234 gc.gridy = 1; 235 gc.weightx = 0.0; 236 pnl.add(rbProxyPolicy.get(ProxyPolicy.USE_SYSTEM_SETTINGS),gc); 237 238 gc.gridx = 1; 239 gc.weightx = 1.0; 240 String msg; 241 if (DefaultProxySelector.willJvmRetrieveSystemProxies()) { 242 msg = tr("Use standard system settings"); 243 } else { 244 msg = tr("Use standard system settings (disabled. Start JOSM with <tt>-Djava.net.useSystemProxies=true</tt> to enable)"); 245 } 246 pnl.add(new JMultilineLabel("<html>" + msg + "</html>"), gc); 247 248 // radio button http proxy 249 gc.gridx = 0; 250 gc.gridy = 2; 251 gc.weightx = 0.0; 252 pnl.add(rbProxyPolicy.get(ProxyPolicy.USE_HTTP_PROXY),gc); 253 254 gc.gridx = 1; 255 gc.weightx = 1.0; 256 pnl.add(new JLabel(tr("Manually configure a HTTP proxy")),gc); 257 258 // the panel with the http proxy configuration parameters 259 gc.gridx = 1; 260 gc.gridy = 3; 261 gc.fill = GridBagConstraints.HORIZONTAL; 262 gc.weightx = 1.0; 263 gc.weighty = 0.0; 264 pnl.add(pnlHttpProxyConfigurationPanel = buildHttpProxyConfigurationPanel(),gc); 265 266 // radio button SOCKS proxy 267 gc.gridx = 0; 268 gc.gridy = 4; 269 gc.weightx = 0.0; 270 pnl.add(rbProxyPolicy.get(ProxyPolicy.USE_SOCKS_PROXY),gc); 271 272 gc.gridx = 1; 273 gc.weightx = 1.0; 274 pnl.add(new JLabel(tr("Use a SOCKS proxy")),gc); 275 276 // the panel with the SOCKS configuration parameters 277 gc.gridx = 1; 278 gc.gridy = 5; 279 gc.fill = GridBagConstraints.BOTH; 280 gc.anchor = GridBagConstraints.WEST; 281 gc.weightx = 1.0; 282 gc.weighty = 0.0; 283 pnl.add(pnlSocksProxyConfigurationPanel = buildSocksProxyConfigurationPanel(),gc); 284 285 return pnl; 286 } 287 288 /** 289 * Initializes the panel with the values from the preferences 290 */ 291 public void initFromPreferences() { 292 String policy = Main.pref.get(PROXY_POLICY, null); 293 ProxyPolicy pp = ProxyPolicy.fromName(policy); 294 if (pp == null) { 295 pp = ProxyPolicy.NO_PROXY; 296 } 297 rbProxyPolicy.get(pp).setSelected(true); 298 String value = Main.pref.get("proxy.host", null); 299 if (value != null) { 300 // legacy support 301 tfProxyHttpHost.setText(value); 302 Main.pref.put("proxy.host", null); 303 } else { 304 tfProxyHttpHost.setText(Main.pref.get(PROXY_HTTP_HOST, "")); 305 } 306 value = Main.pref.get("proxy.port", null); 307 if (value != null) { 308 // legacy support 309 tfProxyHttpPort.setText(value); 310 Main.pref.put("proxy.port", null); 311 } else { 312 tfProxyHttpPort.setText(Main.pref.get(PROXY_HTTP_PORT, "")); 313 } 314 tfProxySocksHost.setText(Main.pref.get(PROXY_SOCKS_HOST, "")); 315 tfProxySocksPort.setText(Main.pref.get(PROXY_SOCKS_PORT, "")); 316 317 if (pp.equals(ProxyPolicy.USE_SYSTEM_SETTINGS) && ! DefaultProxySelector.willJvmRetrieveSystemProxies()) { 318 System.err.println(tr("Warning: JOSM is configured to use proxies from the system setting, but the JVM is not configured to retrieve them. Resetting preferences to ''No proxy''")); 319 pp = ProxyPolicy.NO_PROXY; 320 rbProxyPolicy.get(pp).setSelected(true); 321 } 322 323 // save the proxy user and the proxy password to a credentials store managed by 324 // the credentials manager 325 CredentialsAgent cm = CredentialsManager.getInstance(); 326 try { 327 PasswordAuthentication pa = cm.lookup(RequestorType.PROXY, tfProxyHttpHost.getText()); 328 if (pa == null) { 329 tfProxyHttpUser.setText(""); 330 tfProxyHttpPassword.setText(""); 331 } else { 332 tfProxyHttpUser.setText(pa.getUserName() == null ? "" : pa.getUserName()); 333 tfProxyHttpPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword())); 334 } 335 } catch(CredentialsAgentException e) { 336 e.printStackTrace(); 337 tfProxyHttpUser.setText(""); 338 tfProxyHttpPassword.setText(""); 339 } 340 } 341 342 protected void updateEnabledState() { 343 boolean isHttpProxy = rbProxyPolicy.get(ProxyPolicy.USE_HTTP_PROXY).isSelected(); 344 for (Component c: pnlHttpProxyConfigurationPanel.getComponents()) { 345 c.setEnabled(isHttpProxy); 346 } 347 348 boolean isSocksProxy = rbProxyPolicy.get(ProxyPolicy.USE_SOCKS_PROXY).isSelected(); 349 for (Component c: pnlSocksProxyConfigurationPanel.getComponents()) { 350 c.setEnabled(isSocksProxy); 351 } 352 353 rbProxyPolicy.get(ProxyPolicy.USE_SYSTEM_SETTINGS).setEnabled(DefaultProxySelector.willJvmRetrieveSystemProxies()); 354 } 355 356 class ProxyPolicyChangeListener implements ItemListener { 357 @Override 358 public void itemStateChanged(ItemEvent arg0) { 359 updateEnabledState(); 360 } 361 } 362 363 public ProxyPreferencesPanel() { 364 setLayout(new GridBagLayout()); 365 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 366 add(buildProxySettingsPanel(), GBC.eop().anchor(GridBagConstraints.NORTHWEST).fill(GridBagConstraints.BOTH)); 367 368 initFromPreferences(); 369 updateEnabledState(); 370 371 HelpUtil.setHelpContext(this, HelpUtil.ht("/Preferences/Connection#ProxySettings")); 372 } 373 374 /** 375 * Saves the current values to the preferences 376 */ 377 public void saveToPreferences() { 378 ProxyPolicy policy = null; 379 for (ProxyPolicy pp: ProxyPolicy.values()) { 380 if (rbProxyPolicy.get(pp).isSelected()) { 381 policy = pp; 382 break; 383 } 384 } 385 if (policy == null) { 386 policy = ProxyPolicy.NO_PROXY; 387 } 388 Main.pref.put(PROXY_POLICY, policy.getName()); 389 Main.pref.put(PROXY_HTTP_HOST, tfProxyHttpHost.getText()); 390 Main.pref.put(PROXY_HTTP_PORT, tfProxyHttpPort.getText()); 391 Main.pref.put(PROXY_SOCKS_HOST, tfProxySocksHost.getText()); 392 Main.pref.put(PROXY_SOCKS_PORT, tfProxySocksPort.getText()); 393 394 // update the proxy selector 395 ProxySelector selector = ProxySelector.getDefault(); 396 if (selector instanceof DefaultProxySelector) { 397 ((DefaultProxySelector)selector).initFromPreferences(); 398 } 399 400 CredentialsAgent cm = CredentialsManager.getInstance(); 401 try { 402 PasswordAuthentication pa = new PasswordAuthentication( 403 tfProxyHttpUser.getText().trim(), 404 tfProxyHttpPassword.getPassword() 405 ); 406 cm.store(RequestorType.PROXY, tfProxyHttpHost.getText(), pa); 407 } catch(CredentialsAgentException e) { 408 e.printStackTrace(); 409 } 410 } 411 }