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

Last change on this file since 12241 was 12219, checked in by Don-vip, 7 years ago

see #14652 - ask Windows/macOS users to update their version of Java when it expires (i.e when the built-in JRE expiration date is passed, about 4 months after release, 1 month after Java should have asked to update by itself). It currently proposes to update all versions of Java 8 up to update 121, released on January 17, 2017, as its expiration date is May 18, 2017.

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