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