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