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

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

see #10989, #16215, fix #16199 - load "Malgun Gothic" font on Windows in order to display Korean characters on Windows 10 without Korean language pack installed

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