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