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

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

see #15992 - do not log PS 2.0 errors as such (debug level is enough)

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