source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/server/ProxyPreferencesPanel.java

Last change on this file was 18663, checked in by taylor.smock, 14 months ago

Fix #22497: Add setting to only proxy specific hosts (patch by TrickyFoxy)

  • Property svn:eol-style set to native
File size: 15.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.server;
3
4import static java.awt.GridBagConstraints.HORIZONTAL;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trc;
7
8import java.awt.Component;
9import java.awt.Dimension;
10import java.awt.GridBagConstraints;
11import java.awt.GridBagLayout;
12import java.awt.Insets;
13import java.awt.event.ItemEvent;
14import java.awt.event.ItemListener;
15import java.net.Authenticator.RequestorType;
16import java.net.PasswordAuthentication;
17import java.net.ProxySelector;
18import java.util.Arrays;
19import java.util.EnumMap;
20import java.util.Map;
21import java.util.Optional;
22import java.util.ArrayList;
23
24import javax.swing.BorderFactory;
25import javax.swing.Box;
26import javax.swing.ButtonGroup;
27import javax.swing.JLabel;
28import javax.swing.JPanel;
29import javax.swing.JRadioButton;
30
31import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
32import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
33import org.openstreetmap.josm.gui.widgets.JosmTextField;
34import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
35import org.openstreetmap.josm.gui.widgets.EditableList;
36import org.openstreetmap.josm.io.DefaultProxySelector;
37import org.openstreetmap.josm.io.ProxyPolicy;
38import org.openstreetmap.josm.io.auth.CredentialsAgent;
39import org.openstreetmap.josm.io.auth.CredentialsAgentException;
40import org.openstreetmap.josm.io.auth.CredentialsManager;
41import org.openstreetmap.josm.spi.preferences.Config;
42import org.openstreetmap.josm.spi.preferences.IPreferences;
43import org.openstreetmap.josm.tools.GBC;
44import org.openstreetmap.josm.tools.Logging;
45
46/**
47 * Component allowing input of proxy settings.
48 */
49public class ProxyPreferencesPanel extends VerticallyScrollablePanel {
50
51 private static final long serialVersionUID = -2479852374189976764L;
52
53 static final class AutoSizePanel extends JPanel {
54 private static final long serialVersionUID = 7469560761925020366L;
55
56 AutoSizePanel() {
57 super(new GridBagLayout());
58 }
59
60 @Override
61 public Dimension getMinimumSize() {
62 return getPreferredSize();
63 }
64 }
65
66 private transient Map<ProxyPolicy, JRadioButton> rbProxyPolicy;
67 private final JosmTextField tfProxyHttpHost = new JosmTextField();
68 private final JosmTextField tfProxyHttpPort = new JosmTextField(5);
69 private final JosmTextField tfProxySocksHost = new JosmTextField(20);
70 private final JosmTextField tfProxySocksPort = new JosmTextField(5);
71 private final JosmTextField tfProxyHttpUser = new JosmTextField(20);
72 private final JosmPasswordField tfProxyHttpPassword = new JosmPasswordField(20);
73 private final EditableList tfExceptionHosts = new EditableList(tr("No proxy for"));
74 private final EditableList tfIncludeHosts = new EditableList(tr("Proxy only for"));
75
76 private JPanel pnlHttpProxyConfigurationPanel;
77 private JPanel pnlSocksProxyConfigurationPanel;
78 private JPanel pnlIncludeOrExcludeHostsPanel;
79
80 /**
81 * Builds the panel for the HTTP proxy configuration
82 *
83 * @return panel with HTTP proxy configuration
84 */
85 protected final JPanel buildHttpProxyConfigurationPanel() {
86 JPanel pnl = new AutoSizePanel();
87 GridBagConstraints gc = new GridBagConstraints();
88
89 gc.anchor = GridBagConstraints.LINE_START;
90 gc.insets = new Insets(5, 5, 0, 0);
91 gc.fill = HORIZONTAL;
92 gc.weightx = 0.0;
93 pnl.add(new JLabel(tr("Host:")), gc);
94
95 gc.gridx = 1;
96 gc.weightx = 1.0;
97 pnl.add(tfProxyHttpHost, gc);
98
99 gc.gridy = 1;
100 gc.gridx = 0;
101 gc.fill = GridBagConstraints.NONE;
102 gc.weightx = 0.0;
103 pnl.add(new JLabel(trc("server", "Port:")), gc);
104
105 gc.gridx = 1;
106 gc.weightx = 1.0;
107 pnl.add(tfProxyHttpPort, gc);
108 tfProxyHttpPort.setMinimumSize(tfProxyHttpPort.getPreferredSize());
109
110 gc.gridy = 2;
111 gc.gridx = 0;
112 gc.gridwidth = 2;
113 gc.fill = HORIZONTAL;
114 gc.weightx = 1.0;
115 pnl.add(new JMultilineLabel(tr("Please enter a username and a password if your proxy requires authentication.")), gc);
116
117 gc.gridy = 3;
118 gc.gridx = 0;
119 gc.gridwidth = 1;
120 gc.fill = GridBagConstraints.NONE;
121 gc.weightx = 0.0;
122 pnl.add(new JLabel(tr("User:")), gc);
123
124 gc.gridy = 3;
125 gc.gridx = 1;
126 gc.weightx = 1.0;
127 pnl.add(tfProxyHttpUser, gc);
128 tfProxyHttpUser.setMinimumSize(tfProxyHttpUser.getPreferredSize());
129
130 gc.gridy = 4;
131 gc.gridx = 0;
132 gc.weightx = 0.0;
133 pnl.add(new JLabel(tr("Password:")), gc);
134
135 gc.gridx = 1;
136 gc.weightx = 1.0;
137 pnl.add(tfProxyHttpPassword, gc);
138 tfProxyHttpPassword.setMinimumSize(tfProxyHttpPassword.getPreferredSize());
139
140 // add an extra spacer, otherwise the layout is broken
141 gc.gridy = 5;
142 gc.gridx = 0;
143 gc.gridwidth = 2;
144 gc.fill = GridBagConstraints.BOTH;
145 gc.weightx = 1.0;
146 gc.weighty = 1.0;
147 pnl.add(new JPanel(), gc);
148 return pnl;
149 }
150
151 /**
152 * Builds the panel for the SOCKS proxy configuration
153 *
154 * @return panel with SOCKS proxy configuration
155 */
156 protected final JPanel buildSocksProxyConfigurationPanel() {
157 JPanel pnl = new AutoSizePanel();
158 GridBagConstraints gc = new GridBagConstraints();
159 gc.anchor = GridBagConstraints.LINE_START;
160 gc.insets = new Insets(5, 5, 0, 0);
161 gc.fill = HORIZONTAL;
162 gc.weightx = 0.0;
163 pnl.add(new JLabel(tr("Host:")), gc);
164
165 gc.gridx = 1;
166 gc.weightx = 1.0;
167 pnl.add(tfProxySocksHost, gc);
168
169 gc.gridy = 1;
170 gc.gridx = 0;
171 gc.weightx = 0.0;
172 gc.fill = GridBagConstraints.NONE;
173 pnl.add(new JLabel(trc("server", "Port:")), gc);
174
175 gc.gridx = 1;
176 gc.weightx = 1.0;
177 pnl.add(tfProxySocksPort, gc);
178 tfProxySocksPort.setMinimumSize(tfProxySocksPort.getPreferredSize());
179
180 // add an extra spacer, otherwise the layout is broken
181 gc.gridy = 2;
182 gc.gridx = 0;
183 gc.gridwidth = 2;
184 gc.fill = GridBagConstraints.BOTH;
185 gc.weightx = 1.0;
186 gc.weighty = 1.0;
187 pnl.add(new JPanel(), gc);
188 return pnl;
189 }
190
191 protected final JPanel buildExceptionIncludesHostsProxyConfigurationPanel() {
192 JPanel pnl = new AutoSizePanel();
193 GridBagConstraints gc = new GridBagConstraints();
194 gc.anchor = GridBagConstraints.LINE_START;
195 gc.insets = new Insets(5, 5, 0, 0);
196 gc.weightx = 0.0;
197 pnl.add(new JLabel(tr("No proxy for (hosts):")), gc);
198
199 gc.gridx = 1;
200 gc.weightx = 0.0;
201 gc.fill = GridBagConstraints.NONE;
202 pnl.add(new JLabel(tr("Proxy only for (hosts):")), gc);
203
204 gc.gridy = 1;
205 gc.gridx = 0;
206 gc.weightx = 1.0;
207 gc.fill = HORIZONTAL;
208 tfExceptionHosts.setMinimumSize(tfExceptionHosts.getPreferredSize());
209 pnl.add(tfExceptionHosts, gc);
210
211 gc.gridx = 1;
212 gc.weightx = 1.0;
213 gc.fill = HORIZONTAL;
214 tfIncludeHosts.setMinimumSize(tfIncludeHosts.getPreferredSize());
215 pnl.add(tfIncludeHosts, gc);
216
217 // add an extra spacer, otherwise the layout is broken
218 gc.gridy = 2;
219 gc.gridx = 0;
220 gc.gridwidth = 2;
221 gc.fill = GridBagConstraints.BOTH;
222 gc.weightx = 1.0;
223 gc.weighty = 1.0;
224 pnl.add(new JPanel(), gc);
225 return pnl;
226 }
227
228 protected final JPanel buildProxySettingsPanel() {
229 JPanel pnl = new JPanel(new GridBagLayout());
230
231 ButtonGroup bgProxyPolicy = new ButtonGroup();
232 rbProxyPolicy = new EnumMap<>(ProxyPolicy.class);
233 ProxyPolicyChangeListener policyChangeListener = new ProxyPolicyChangeListener();
234 for (ProxyPolicy pp: ProxyPolicy.values()) {
235 rbProxyPolicy.put(pp, new JRadioButton());
236 bgProxyPolicy.add(rbProxyPolicy.get(pp));
237 rbProxyPolicy.get(pp).addItemListener(policyChangeListener);
238 }
239
240 // radio button "No proxy"
241 pnl.add(newRadioButton(ProxyPolicy.NO_PROXY, tr("No proxy")), GBC.eop().anchor(GBC.NORTHWEST));
242
243 // radio button "System settings"
244 pnl.add(newRadioButton(ProxyPolicy.USE_SYSTEM_SETTINGS, tr("Use standard system settings")), GBC.eol());
245 if (!DefaultProxySelector.willJvmRetrieveSystemProxies()) {
246 String msg = tr("Use standard system settings (disabled. Start JOSM with <tt>-Djava.net.useSystemProxies=true</tt> to enable)");
247 pnl.add(new JMultilineLabel("<html>" + msg + "</html>"), GBC.eop().fill(HORIZONTAL));
248 }
249
250 // radio button http proxy
251 pnl.add(newRadioButton(ProxyPolicy.USE_HTTP_PROXY, tr("Manually configure a HTTP proxy")), GBC.eol());
252
253 // the panel with the http proxy configuration parameters
254 pnlHttpProxyConfigurationPanel = buildHttpProxyConfigurationPanel();
255 pnl.add(pnlHttpProxyConfigurationPanel, GBC.eop().fill(HORIZONTAL));
256
257 // radio button SOCKS proxy
258 pnl.add(newRadioButton(ProxyPolicy.USE_SOCKS_PROXY, tr("Use a SOCKS proxy")), GBC.eol());
259
260 // the panel with the SOCKS configuration parameters
261 pnlSocksProxyConfigurationPanel = buildSocksProxyConfigurationPanel();
262 pnl.add(pnlSocksProxyConfigurationPanel, GBC.eop().fill(HORIZONTAL));
263
264 pnl.add(Box.createVerticalGlue(), GBC.eol().fill());
265
266 // the panel with the exception and includes hosts
267 pnlIncludeOrExcludeHostsPanel = buildExceptionIncludesHostsProxyConfigurationPanel();
268 pnl.add(pnlIncludeOrExcludeHostsPanel, GBC.eop().fill(HORIZONTAL));
269
270 pnl.add(Box.createVerticalGlue(), GBC.eol().fill());
271
272 return pnl;
273 }
274
275 private JRadioButton newRadioButton(ProxyPolicy policy, String text) {
276 JRadioButton radioButton = rbProxyPolicy.get(policy);
277 radioButton.setText(text);
278 return radioButton;
279 }
280
281 /**
282 * Initializes the panel with the values from the preferences
283 */
284 public final void initFromPreferences() {
285 IPreferences pref = Config.getPref();
286 ProxyPolicy pp = Optional.ofNullable(ProxyPolicy.fromName(pref.get(DefaultProxySelector.PROXY_POLICY, null)))
287 .orElse(ProxyPolicy.NO_PROXY);
288 rbProxyPolicy.get(pp).setSelected(true);
289 tfProxyHttpHost.setText(pref.get(DefaultProxySelector.PROXY_HTTP_HOST, ""));
290 tfProxyHttpPort.setText(pref.get(DefaultProxySelector.PROXY_HTTP_PORT, ""));
291 tfProxySocksHost.setText(pref.get(DefaultProxySelector.PROXY_SOCKS_HOST, ""));
292 tfProxySocksPort.setText(pref.get(DefaultProxySelector.PROXY_SOCKS_PORT, ""));
293 tfExceptionHosts.setItems(pref.getList(DefaultProxySelector.PROXY_EXCEPTIONS, new ArrayList<>()));
294 tfIncludeHosts.setItems(pref.getList(DefaultProxySelector.PROXY_INCLUDES, new ArrayList<>()));
295
296 if (pp == ProxyPolicy.USE_SYSTEM_SETTINGS && !DefaultProxySelector.willJvmRetrieveSystemProxies()) {
297 Logging.warn(tr("JOSM is configured to use proxies from the system setting, but the JVM is not configured to retrieve them. " +
298 "Resetting preferences to ''No proxy''"));
299 pp = ProxyPolicy.NO_PROXY;
300 rbProxyPolicy.get(pp).setSelected(true);
301 }
302
303 // save the proxy user and the proxy password to a credentials store managed by
304 // the credentials manager
305 CredentialsAgent cm = CredentialsManager.getInstance();
306 try {
307 PasswordAuthentication pa = cm.lookup(RequestorType.PROXY, tfProxyHttpHost.getText());
308 if (pa == null) {
309 tfProxyHttpUser.setText("");
310 tfProxyHttpPassword.setText("");
311 } else {
312 tfProxyHttpUser.setText(pa.getUserName() == null ? "" : pa.getUserName());
313 tfProxyHttpPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
314 }
315 } catch (CredentialsAgentException e) {
316 Logging.error(e);
317 tfProxyHttpUser.setText("");
318 tfProxyHttpPassword.setText("");
319 }
320 }
321
322 protected final void updateEnabledState() {
323 boolean isHttpProxy = rbProxyPolicy.get(ProxyPolicy.USE_HTTP_PROXY).isSelected();
324 for (Component c: pnlHttpProxyConfigurationPanel.getComponents()) {
325 c.setEnabled(isHttpProxy);
326 }
327
328 boolean isSocksProxy = rbProxyPolicy.get(ProxyPolicy.USE_SOCKS_PROXY).isSelected();
329 for (Component c: pnlSocksProxyConfigurationPanel.getComponents()) {
330 c.setEnabled(isSocksProxy);
331 }
332
333 rbProxyPolicy.get(ProxyPolicy.USE_SYSTEM_SETTINGS).setEnabled(DefaultProxySelector.willJvmRetrieveSystemProxies());
334
335 boolean proxyEnabled = !rbProxyPolicy.get(ProxyPolicy.NO_PROXY).isSelected();
336 for (Component c : pnlIncludeOrExcludeHostsPanel.getComponents()) {
337 c.setEnabled(proxyEnabled);
338 }
339 }
340
341 class ProxyPolicyChangeListener implements ItemListener {
342 @Override
343 public void itemStateChanged(ItemEvent e) {
344 updateEnabledState();
345 }
346 }
347
348 /**
349 * Constructs a new {@code ProxyPreferencesPanel}.
350 */
351 public ProxyPreferencesPanel() {
352 setLayout(new GridBagLayout());
353 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
354 add(buildProxySettingsPanel(), GBC.eop().anchor(GridBagConstraints.NORTHWEST).fill(GridBagConstraints.BOTH));
355
356 initFromPreferences();
357 updateEnabledState();
358 }
359
360 /**
361 * Saves the current values to the preferences
362 */
363 public void saveToPreferences() {
364 ProxyPolicy policy = Arrays.stream(ProxyPolicy.values())
365 .filter(pp -> rbProxyPolicy.get(pp).isSelected())
366 .findFirst().orElse(null);
367 IPreferences pref = Config.getPref();
368 pref.put(DefaultProxySelector.PROXY_POLICY, Optional.ofNullable(policy).orElse(ProxyPolicy.NO_PROXY).getName());
369 pref.put(DefaultProxySelector.PROXY_HTTP_HOST, tfProxyHttpHost.getText());
370 pref.put(DefaultProxySelector.PROXY_HTTP_PORT, tfProxyHttpPort.getText());
371 pref.put(DefaultProxySelector.PROXY_SOCKS_HOST, tfProxySocksHost.getText());
372 pref.put(DefaultProxySelector.PROXY_SOCKS_PORT, tfProxySocksPort.getText());
373 pref.putList(DefaultProxySelector.PROXY_EXCEPTIONS, tfExceptionHosts.getItems());
374 pref.putList(DefaultProxySelector.PROXY_INCLUDES, tfIncludeHosts.getItems());
375
376
377 // update the proxy selector
378 ProxySelector selector = ProxySelector.getDefault();
379 if (selector instanceof DefaultProxySelector) {
380 ((DefaultProxySelector) selector).initFromPreferences();
381 }
382
383 CredentialsAgent cm = CredentialsManager.getInstance();
384 try {
385 PasswordAuthentication pa = new PasswordAuthentication(
386 tfProxyHttpUser.getText().trim(),
387 tfProxyHttpPassword.getPassword()
388 );
389 cm.store(RequestorType.PROXY, tfProxyHttpHost.getText(), pa);
390 } catch (CredentialsAgentException e) {
391 Logging.error(e);
392 }
393 }
394}
Note: See TracBrowser for help on using the repository browser.