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

Last change on this file since 12781 was 12776, checked in by bastiK, 7 years ago

see #15229 - remove dependency of NTV2GridShiftFileWrapper on Main.platform

  • PlatformHook bundles all application-wide platform dependent code, which is

convenient, but problematic for separating modules

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