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

Last change on this file since 13465 was 13465, checked in by Don-vip, 8 years ago

fix #15992 - also make sure recent version of PowerShell is installed

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