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