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

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

see #15229 - code refactoring - add PlatformHook.getPossiblePreferenceDirs()

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