source: josm/trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java@ 13331

Last change on this file since 13331 was 13204, checked in by Don-vip, 6 years ago

enable new PMD rule AvoidFileStream - see https://pmd.github.io/pmd-6.0.0/pmd_rules_java_performance.html#avoidfilestream / https://bugs.openjdk.java.net/browse/JDK-8080225 for details

  • Property svn:eol-style set to native
File size: 39.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static java.awt.event.InputEvent.ALT_DOWN_MASK;
5import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
6import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
7import static java.awt.event.KeyEvent.VK_A;
8import static java.awt.event.KeyEvent.VK_C;
9import static java.awt.event.KeyEvent.VK_D;
10import static java.awt.event.KeyEvent.VK_DELETE;
11import static java.awt.event.KeyEvent.VK_DOWN;
12import static java.awt.event.KeyEvent.VK_ENTER;
13import static java.awt.event.KeyEvent.VK_ESCAPE;
14import static java.awt.event.KeyEvent.VK_F10;
15import static java.awt.event.KeyEvent.VK_F4;
16import static java.awt.event.KeyEvent.VK_LEFT;
17import static java.awt.event.KeyEvent.VK_NUM_LOCK;
18import static java.awt.event.KeyEvent.VK_PRINTSCREEN;
19import static java.awt.event.KeyEvent.VK_RIGHT;
20import static java.awt.event.KeyEvent.VK_SHIFT;
21import static java.awt.event.KeyEvent.VK_SPACE;
22import static java.awt.event.KeyEvent.VK_TAB;
23import static java.awt.event.KeyEvent.VK_UP;
24import static java.awt.event.KeyEvent.VK_V;
25import static java.awt.event.KeyEvent.VK_X;
26import static java.awt.event.KeyEvent.VK_Y;
27import static java.awt.event.KeyEvent.VK_Z;
28import static org.openstreetmap.josm.tools.I18n.tr;
29import static org.openstreetmap.josm.tools.WinRegistry.HKEY_LOCAL_MACHINE;
30
31import java.awt.GraphicsEnvironment;
32import java.io.BufferedWriter;
33import java.io.File;
34import java.io.IOException;
35import java.io.InputStream;
36import java.io.OutputStream;
37import java.io.OutputStreamWriter;
38import java.io.Writer;
39import java.lang.reflect.InvocationTargetException;
40import java.nio.charset.StandardCharsets;
41import java.nio.file.DirectoryStream;
42import java.nio.file.FileSystems;
43import java.nio.file.Files;
44import java.nio.file.InvalidPathException;
45import java.nio.file.Path;
46import java.security.InvalidKeyException;
47import java.security.KeyFactory;
48import java.security.KeyStore;
49import java.security.KeyStoreException;
50import java.security.MessageDigest;
51import java.security.NoSuchAlgorithmException;
52import java.security.NoSuchProviderException;
53import java.security.PublicKey;
54import java.security.SignatureException;
55import java.security.cert.Certificate;
56import java.security.cert.CertificateException;
57import java.security.cert.X509Certificate;
58import java.security.spec.InvalidKeySpecException;
59import java.security.spec.X509EncodedKeySpec;
60import java.util.ArrayList;
61import java.util.Collection;
62import java.util.Enumeration;
63import java.util.List;
64import java.util.Locale;
65import java.util.Properties;
66
67import javax.swing.JOptionPane;
68
69import org.openstreetmap.josm.Main;
70import org.openstreetmap.josm.data.StructUtils;
71import org.openstreetmap.josm.data.StructUtils.StructEntry;
72import org.openstreetmap.josm.data.StructUtils.WriteExplicitly;
73import org.openstreetmap.josm.io.CertificateAmendment.CertAmend;
74import org.openstreetmap.josm.spi.preferences.Config;
75
76/**
77 * {@code PlatformHook} implementation for Microsoft Windows systems.
78 * @since 1023
79 */
80public class PlatformHookWindows implements PlatformHook {
81
82 /**
83 * Simple data class to hold information about a font.
84 *
85 * Used for fontconfig.properties files.
86 */
87 public static class FontEntry {
88 /**
89 * The character subset. Basically a free identifier, but should be unique.
90 */
91 @StructEntry
92 public String charset;
93
94 /**
95 * Platform font name.
96 */
97 @StructEntry
98 @WriteExplicitly
99 public String name = "";
100
101 /**
102 * File name.
103 */
104 @StructEntry
105 @WriteExplicitly
106 public String file = "";
107
108 /**
109 * Constructs a new {@code FontEntry}.
110 */
111 public FontEntry() {
112 // Default constructor needed for construction by reflection
113 }
114
115 /**
116 * Constructs a new {@code FontEntry}.
117 * @param charset The character subset. Basically a free identifier, but should be unique
118 * @param name Platform font name
119 * @param file File name
120 */
121 public FontEntry(String charset, String name, String file) {
122 this.charset = charset;
123 this.name = name;
124 this.file = file;
125 }
126 }
127
128 private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
129 0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
130 (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
131 0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
132 (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
133 0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
134 (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
135 (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
136 (byte) 0xc7, (byte) 0xc3, 0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
137 (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
138 (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
139 (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
140 0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
141 (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
142 0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
143 (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
144 (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
145 0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
146 (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
147 (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
148 (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
149 0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
150 (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
151 (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
152 0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
153 };
154
155 private static final String WINDOWS_ROOT = "Windows-ROOT";
156
157 private static final String CURRENT_VERSION = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
158
159 private String oSBuildNumber;
160
161 @Override
162 public Platform getPlatform() {
163 return Platform.WINDOWS;
164 }
165
166 @Override
167 public void afterPrefStartupHook() {
168 extendFontconfig("fontconfig.properties.src");
169 }
170
171 @Override
172 public void startupHook(JavaExpirationCallback callback) {
173 checkExpiredJava(callback);
174 }
175
176 @Override
177 public void openUrl(String url) throws IOException {
178 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
179 }
180
181 @Override
182 public void initSystemShortcuts() {
183 // CHECKSTYLE.OFF: LineLength
184 //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK);
185 Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results
186
187 // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
188
189 // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all
190
191 // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page
192 Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic();
193
194 // Ease of Access keyboard shortcuts
195 Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off
196 Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off
197 //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?)
198
199 // General keyboard shortcuts
200 //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0); // Display Help
201 Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK); // Copy the selected item
202 Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK); // Cut the selected item
203 Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK); // Paste the selected item
204 Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK); // Undo an action
205 Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK); // Redo an action
206 //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0); // Delete the selected item and move it to the Recycle Bin
207 //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK); // Delete the selected item without moving it to the Recycle Bin first
208 //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0); // Rename the selected item
209 Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next word
210 Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous word
211 Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next paragraph
212 Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous paragraph
213 //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
214 //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
215 //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
216 //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
217 //Shortcut.registerSystemCut("microsoft-reserved-21", tr("reserved"), VK_RIGHT, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
218 //Shortcut.registerSystemCut("microsoft-reserved-22", tr("reserved"), VK_LEFT, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
219 //Shortcut.registerSystemCut("microsoft-reserved-23", tr("reserved"), VK_DOWN, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
220 //Shortcut.registerSystemCut("microsoft-reserved-24", tr("reserved"), VK_UP, SHIFT_DOWN_MASK); // Select more than one item in a window or on the desktop, or select text within a document
221 //Shortcut.registerSystemCut("microsoft-reserved-25", tr("reserved"), VK_RIGHT+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
222 //Shortcut.registerSystemCut("microsoft-reserved-26", tr("reserved"), VK_LEFT+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
223 //Shortcut.registerSystemCut("microsoft-reserved-27", tr("reserved"), VK_DOWN+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
224 //Shortcut.registerSystemCut("microsoft-reserved-28", tr("reserved"), VK_UP+, CTRL_DOWN_MASK); // Select multiple individual items in a window or on the desktop (TODO: ctrl+arrow+spacebar, how to handle it in Java ?)
225 Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK); // Select all items in a document or window
226 //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0); // Search for a file or folder
227 Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic(); // Display properties for the selected item
228 Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program
229 Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic(); // Open the shortcut menu for the active window
230 //Shortcut.registerSystemCut("microsoft-reserved-34", tr("reserved"), VK_F4, CTRL_DOWN_MASK); // Close the active document (in programs that allow you to have multiple documents open simultaneously)
231 Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic(); // Switch between open items
232 Shortcut.registerSystemShortcut("microsoft-reserved-36", tr("reserved"), VK_TAB, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic(); // Use the arrow keys to switch between open items
233 //Shortcut.registerSystemCut("microsoft-reserved-37", tr("reserved"), VK_TAB, ); // Cycle through programs on the taskbar by using Aero Flip 3-D (TODO: Windows-Tab, how to handle it in Java ?)
234 //Shortcut.registerSystemCut("microsoft-reserved-38", tr("reserved"), VK_TAB, CTRL_DOWN_MASK | ); // Use the arrow keys to cycle through programs on the taskbar by using Aero Flip 3-D (TODO: Ctrl-Windows-Tab, how to handle it in Java ?)
235 Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic(); // Cycle through items in the order in which they were opened
236 //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0); // Cycle through screen elements in a window or on the desktop
237 //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0); // Display the address bar list in Windows Explorer
238 Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK); // Display the shortcut menu for the selected item
239 Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu
240 //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0); // Activate the menu bar in the active program
241 //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0); // Open the next menu to the right, or open a submenu
242 //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0); // Open the next menu to the left, or close a submenu
243 //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0); // Refresh the active window
244 //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK); // View the folder one level up in Windows Explorer
245 //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0); // Cancel the current task
246 Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager
247 Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic(); // Switch the input language when multiple input languages are enabled
248 Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic(); // Switch the keyboard layout when multiple keyboard layouts are enabled
249 //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear)
250 // CHECKSTYLE.ON: LineLength
251 }
252
253 @Override
254 public String getDefaultStyle() {
255 return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
256 }
257
258 @Override
259 public boolean rename(File from, File to) {
260 if (to.exists())
261 Utils.deleteFile(to);
262 return from.renameTo(to);
263 }
264
265 @Override
266 public String getOSDescription() {
267 return Utils.strip(System.getProperty("os.name")) + ' ' +
268 ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit";
269 }
270
271 /**
272 * Returns the Windows product name from registry (example: "Windows 10 Pro")
273 * @return the Windows product name from registry
274 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
275 * @throws InvocationTargetException if the underlying method throws an exception
276 * @since 12744
277 */
278 public static String getProductName() throws IllegalAccessException, InvocationTargetException {
279 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ProductName");
280 }
281
282 /**
283 * Returns the Windows release identifier from registry (example: "1703")
284 * @return the Windows release identifier from registry
285 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
286 * @throws InvocationTargetException if the underlying method throws an exception
287 * @since 12744
288 */
289 public static String getReleaseId() throws IllegalAccessException, InvocationTargetException {
290 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "ReleaseId");
291 }
292
293 /**
294 * Returns the Windows current build number from registry (example: "15063")
295 * @return the Windows current build number from registry
296 * @throws IllegalAccessException if Java language access control is enforced and the underlying method is inaccessible
297 * @throws InvocationTargetException if the underlying method throws an exception
298 * @since 12744
299 */
300 public static String getCurrentBuild() throws IllegalAccessException, InvocationTargetException {
301 return WinRegistry.readString(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "CurrentBuild");
302 }
303
304 private static String buildOSBuildNumber() {
305 StringBuilder sb = new StringBuilder();
306 try {
307 sb.append(getProductName());
308 String releaseId = getReleaseId();
309 if (releaseId != null) {
310 sb.append(' ').append(releaseId);
311 }
312 sb.append(" (").append(getCurrentBuild()).append(')');
313 } catch (ReflectiveOperationException | JosmRuntimeException e) {
314 Logging.error(e);
315 }
316 return sb.toString();
317 }
318
319 @Override
320 public String getOSBuildNumber() {
321 if (oSBuildNumber == null) {
322 oSBuildNumber = buildOSBuildNumber();
323 }
324 return oSBuildNumber;
325 }
326
327 /**
328 * Loads Windows-ROOT keystore.
329 * @return Windows-ROOT keystore
330 * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
331 * @throws CertificateException if any of the certificates in the keystore could not be loaded
332 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
333 * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT"
334 * @since 7343
335 */
336 public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
337 KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT);
338 ks.load(null, null);
339 return ks;
340 }
341
342 /**
343 * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
344 * @throws NoSuchAlgorithmException on unsupported signature algorithms
345 * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
346 * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
347 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
348 * @since 7335
349 */
350 public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
351 // We offered before a public private key we need now to remove from Windows PCs as it might be a huge security risk (see #10230)
352 PublicKey insecurePubKey = null;
353 try {
354 insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
355 } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
356 Logging.error(e);
357 return;
358 }
359 KeyStore ks = getRootKeystore();
360 Enumeration<String> en = ks.aliases();
361 Collection<String> insecureCertificates = new ArrayList<>();
362 while (en.hasMoreElements()) {
363 String alias = en.nextElement();
364 // Look for certificates associated with a private key
365 if (ks.isKeyEntry(alias)) {
366 try {
367 ks.getCertificate(alias).verify(insecurePubKey);
368 // If no exception, this is a certificate signed with the insecure key -> remove it
369 insecureCertificates.add(alias);
370 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
371 // If exception this is not a certificate related to JOSM, just trace it
372 Logging.trace(alias + " --> " + e.getClass().getName());
373 Logging.trace(e);
374 }
375 }
376 }
377 // Remove insecure certificates
378 if (!insecureCertificates.isEmpty()) {
379 StringBuilder message = new StringBuilder("<html>");
380 message.append(tr("A previous version of JOSM has installed a custom certificate "+
381 "in order to provide HTTPS support for Remote Control:"))
382 .append("<br><ul>");
383 for (String alias : insecureCertificates) {
384 message.append("<li>")
385 .append(alias)
386 .append("</li>");
387 }
388 message.append("</ul>")
389 .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
390 "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
391 "For your own safety, <b>please click Yes</b> in next dialog."))
392 .append("</html>");
393 JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
394 for (String alias : insecureCertificates) {
395 Logging.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
396 try {
397 ks.deleteEntry(alias);
398 } catch (KeyStoreException e) {
399 Logging.log(Logging.LEVEL_ERROR, tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()), e);
400 }
401 }
402 }
403 }
404
405 @Override
406 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
407 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
408 KeyStore ks = getRootKeystore();
409 // Look for certificate to install
410 try {
411 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
412 if (alias != null) {
413 // JOSM certificate found, return
414 Logging.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
415 return false;
416 }
417 } catch (ArrayIndexOutOfBoundsException e) {
418 // catch error of JDK-8172244 as bug seems to not be fixed anytime soon
419 Logging.log(Logging.LEVEL_ERROR, "JDK-8172244 occured. Abort HTTPS setup", e);
420 return false;
421 }
422 if (!GraphicsEnvironment.isHeadless()) {
423 // JOSM certificate not found, warn user
424 StringBuilder message = new StringBuilder("<html>");
425 message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
426 "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
427 "You are now going to be prompted by Windows to confirm this operation.<br>"+
428 "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
429 "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."))
430 .append("</html>");
431 JOptionPane.showMessageDialog(Main.parent, message.toString(),
432 tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
433 }
434 // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
435 Logging.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
436 ks.setEntry(entryAlias, trustedCert, null);
437 return true;
438 }
439
440 @Override
441 public X509Certificate getX509Certificate(CertAmend certAmend)
442 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
443 KeyStore ks = getRootKeystore();
444 // Search by alias (fast)
445 Certificate result = ks.getCertificate(certAmend.getId());
446 if (result instanceof X509Certificate) {
447 return (X509Certificate) result;
448 }
449 // If not found, search by SHA-256 (slower)
450 MessageDigest md = MessageDigest.getInstance("SHA-256");
451 for (Enumeration<String> aliases = ks.aliases(); aliases.hasMoreElements();) {
452 result = ks.getCertificate(aliases.nextElement());
453 if (result instanceof X509Certificate
454 && certAmend.getSha256().equalsIgnoreCase(Utils.toHexString(md.digest(result.getEncoded())))) {
455 return (X509Certificate) result;
456 }
457 }
458 // Not found
459 return null;
460 }
461
462 @Override
463 public File getDefaultCacheDirectory() {
464 String p = System.getenv("LOCALAPPDATA");
465 if (p == null || p.isEmpty()) {
466 // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined
467 p = System.getenv("APPDATA");
468 }
469 return new File(new File(p, Main.pref.getJOSMDirectoryBaseName()), "cache");
470 }
471
472 @Override
473 public File getDefaultPrefDirectory() {
474 return new File(System.getenv("APPDATA"), Main.pref.getJOSMDirectoryBaseName());
475 }
476
477 @Override
478 public File getDefaultUserDataDirectory() {
479 // Use preferences directory by default
480 return Config.getDirs().getPreferencesDirectory(false);
481 }
482
483 /**
484 * <p>Add more fallback fonts to the Java runtime, in order to get
485 * support for more scripts.</p>
486 *
487 * <p>The font configuration in Java doesn't include some Indic scripts,
488 * even though MS Windows ships with fonts that cover these unicode ranges.</p>
489 *
490 * <p>To fix this, the fontconfig.properties template is copied to the JOSM
491 * cache folder. Then, the additional entries are added to the font
492 * configuration. Finally the system property "sun.awt.fontconfig" is set
493 * to the customized fontconfig.properties file.</p>
494 *
495 * <p>This is a crude hack, but better than no font display at all for these languages.
496 * There is no guarantee, that the template file
497 * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default
498 * configuration (which is in a binary format).
499 * Furthermore, the system property "sun.awt.fontconfig" is undocumented and
500 * may no longer work in future versions of Java.</p>
501 *
502 * <p>Related Java bug: <a href="https://bugs.openjdk.java.net/browse/JDK-8008572">JDK-8008572</a></p>
503 *
504 * @param templateFileName file name of the fontconfig.properties template file
505 */
506 protected void extendFontconfig(String templateFileName) {
507 String customFontconfigFile = Config.getPref().get("fontconfig.properties", null);
508 if (customFontconfigFile != null) {
509 Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile);
510 return;
511 }
512 if (!Config.getPref().getBoolean("font.extended-unicode", true))
513 return;
514
515 String javaLibPath = System.getProperty("java.home") + File.separator + "lib";
516 Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName);
517 if (!Files.isReadable(templateFile)) {
518 Logging.warn("extended font config - unable to find font config template file {0}", templateFile.toString());
519 return;
520 }
521 try (InputStream fis = Files.newInputStream(templateFile)) {
522 Properties props = new Properties();
523 props.load(fis);
524 byte[] content = Files.readAllBytes(templateFile);
525 File cachePath = Config.getDirs().getCacheDirectory(true);
526 Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties");
527 OutputStream os = Files.newOutputStream(fontconfigFile);
528 os.write(content);
529 try (Writer w = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
530 Collection<FontEntry> extrasPref = StructUtils.getListOfStructs(Config.getPref(),
531 "font.extended-unicode.extra-items", getAdditionalFonts(), FontEntry.class);
532 Collection<FontEntry> extras = new ArrayList<>();
533 w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n");
534 List<String> allCharSubsets = new ArrayList<>();
535 for (FontEntry entry: extrasPref) {
536 Collection<String> fontsAvail = getInstalledFonts();
537 if (fontsAvail != null && fontsAvail.contains(entry.file.toUpperCase(Locale.ENGLISH))) {
538 if (!allCharSubsets.contains(entry.charset)) {
539 allCharSubsets.add(entry.charset);
540 extras.add(entry);
541 } else {
542 Logging.trace("extended font config - already registered font for charset ''{0}'' - skipping ''{1}''",
543 entry.charset, entry.name);
544 }
545 } else {
546 Logging.trace("extended font config - Font ''{0}'' not found on system - skipping", entry.name);
547 }
548 }
549 for (FontEntry entry: extras) {
550 allCharSubsets.add(entry.charset);
551 if ("".equals(entry.name)) {
552 continue;
553 }
554 String key = "allfonts." + entry.charset;
555 String value = entry.name;
556 String prevValue = props.getProperty(key);
557 if (prevValue != null && !prevValue.equals(value)) {
558 Logging.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
559 }
560 w.append(key + '=' + value + '\n');
561 }
562 w.append('\n');
563 for (FontEntry entry: extras) {
564 if ("".equals(entry.name) || "".equals(entry.file)) {
565 continue;
566 }
567 String key = "filename." + entry.name.replace(' ', '_');
568 String value = entry.file;
569 String prevValue = props.getProperty(key);
570 if (prevValue != null && !prevValue.equals(value)) {
571 Logging.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value);
572 }
573 w.append(key + '=' + value + '\n');
574 }
575 w.append('\n');
576 String fallback = props.getProperty("sequence.fallback");
577 if (fallback != null) {
578 w.append("sequence.fallback=" + fallback + ',' + Utils.join(",", allCharSubsets) + '\n');
579 } else {
580 w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + '\n');
581 }
582 }
583 Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString());
584 } catch (IOException | InvalidPathException ex) {
585 Logging.error(ex);
586 }
587 }
588
589 /**
590 * Get a list of fonts that are installed on the system.
591 *
592 * Must be done without triggering the Java Font initialization.
593 * (See {@link #extendFontconfig(java.lang.String)}, have to set system
594 * property first, which is then read by sun.awt.FontConfiguration upon initialization.)
595 *
596 * @return list of file names
597 */
598 protected Collection<String> getInstalledFonts() {
599 // Cannot use GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
600 // because we have to set the system property before Java initializes its fonts.
601 // Use more low-level method to find the installed fonts.
602 List<String> fontsAvail = new ArrayList<>();
603 Path fontPath = FileSystems.getDefault().getPath(System.getenv("SYSTEMROOT"), "Fonts");
604 try (DirectoryStream<Path> ds = Files.newDirectoryStream(fontPath)) {
605 for (Path p : ds) {
606 Path filename = p.getFileName();
607 if (filename != null) {
608 fontsAvail.add(filename.toString().toUpperCase(Locale.ENGLISH));
609 }
610 }
611 fontsAvail.add(""); // for devanagari
612 } catch (IOException ex) {
613 Logging.log(Logging.LEVEL_ERROR, ex);
614 Logging.warn("extended font config - failed to load available Fonts");
615 fontsAvail = null;
616 }
617 return fontsAvail;
618 }
619
620 /**
621 * Get default list of additional fonts to add to the configuration.
622 *
623 * Java will choose thee first font in the list that can render a certain character.
624 *
625 * @return list of FontEntry objects
626 */
627 protected Collection<FontEntry> getAdditionalFonts() {
628 Collection<FontEntry> def = new ArrayList<>(33);
629 def.add(new FontEntry("devanagari", "", "")); // just include in fallback list font already defined in template
630
631 // Windows scripts: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx
632 // IE default fonts: https://msdn.microsoft.com/en-us/library/ie/dn467844(v=vs.85).aspx
633
634 // Windows 10 and later
635 def.add(new FontEntry("historic", "Segoe UI Historic", "SEGUIHIS.TTF")); // historic charsets
636
637 // Windows 8/8.1 and later
638 def.add(new FontEntry("javanese", "Javanese Text", "JAVATEXT.TTF")); // ISO 639: jv
639 def.add(new FontEntry("leelawadee", "Leelawadee", "LEELAWAD.TTF")); // ISO 639: bug
640 def.add(new FontEntry("myanmar", "Myanmar Text", "MMRTEXT.TTF")); // ISO 639: my
641 def.add(new FontEntry("nirmala", "Nirmala UI", "NIRMALA.TTF")); // ISO 639: sat,srb
642 def.add(new FontEntry("segoeui", "Segoe UI", "SEGOEUI.TTF")); // ISO 639: lis
643 def.add(new FontEntry("emoji", "Segoe UI Emoji", "SEGUIEMJ.TTF")); // emoji symbol characters
644
645 // Windows 7 and later
646 def.add(new FontEntry("nko_tifinagh_vai_osmanya", "Ebrima", "EBRIMA.TTF")); // ISO 639: ber. Nko only since Win 8
647 def.add(new FontEntry("khmer1", "Khmer UI", "KHMERUI.TTF")); // ISO 639: km
648 def.add(new FontEntry("lao1", "Lao UI", "LAOUI.TTF")); // ISO 639: lo
649 def.add(new FontEntry("tai_le", "Microsoft Tai Le", "TAILE.TTF")); // ISO 639: khb
650 def.add(new FontEntry("new_tai_lue", "Microsoft New Tai Lue", "NTHAILU.TTF")); // ISO 639: khb
651
652 // Windows Vista and later:
653 def.add(new FontEntry("ethiopic", "Nyala", "NYALA.TTF")); // ISO 639: am,gez,ti
654 def.add(new FontEntry("tibetan", "Microsoft Himalaya", "HIMALAYA.TTF")); // ISO 639: bo,dz
655 def.add(new FontEntry("cherokee", "Plantagenet Cherokee", "PLANTC.TTF")); // ISO 639: chr
656 def.add(new FontEntry("unified_canadian", "Euphemia", "EUPHEMIA.TTF")); // ISO 639: cr,in
657 def.add(new FontEntry("khmer2", "DaunPenh", "DAUNPENH.TTF")); // ISO 639: km
658 def.add(new FontEntry("khmer3", "MoolBoran", "MOOLBOR.TTF")); // ISO 639: km
659 def.add(new FontEntry("lao_thai", "DokChampa", "DOKCHAMP.TTF")); // ISO 639: lo
660 def.add(new FontEntry("mongolian", "Mongolian Baiti", "MONBAITI.TTF")); // ISO 639: mn
661 def.add(new FontEntry("oriya", "Kalinga", "KALINGA.TTF")); // ISO 639: or
662 def.add(new FontEntry("sinhala", "Iskoola Pota", "ISKPOTA.TTF")); // ISO 639: si
663 def.add(new FontEntry("yi", "Yi Baiti", "MSYI.TTF")); // ISO 639: ii
664
665 // Windows XP and later
666 def.add(new FontEntry("gujarati", "Shruti", "SHRUTI.TTF"));
667 def.add(new FontEntry("kannada", "Tunga", "TUNGA.TTF"));
668 def.add(new FontEntry("gurmukhi", "Raavi", "RAAVI.TTF"));
669 def.add(new FontEntry("telugu", "Gautami", "GAUTAMI.TTF"));
670 def.add(new FontEntry("bengali", "Vrinda", "VRINDA.TTF")); // since XP SP2
671 def.add(new FontEntry("syriac", "Estrangelo Edessa", "ESTRE.TTF")); // ISO 639: arc
672 def.add(new FontEntry("thaana", "MV Boli", "MVBOLI.TTF")); // ISO 639: dv
673 def.add(new FontEntry("malayalam", "Kartika", "KARTIKA.TTF")); // ISO 639: ml; since XP SP2
674
675 // Windows 2000 and later
676 def.add(new FontEntry("tamil", "Latha", "LATHA.TTF"));
677
678 // Comes with MS Office & Outlook 2000. Good unicode coverage, so add if available.
679 def.add(new FontEntry("arialuni", "Arial Unicode MS", "ARIALUNI.TTF"));
680
681 return def;
682 }
683}
Note: See TracBrowser for help on using the repository browser.