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