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; 029 030import java.awt.GraphicsEnvironment; 031import java.io.BufferedWriter; 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.IOException; 035import java.io.OutputStream; 036import java.io.OutputStreamWriter; 037import java.io.Writer; 038import java.nio.charset.StandardCharsets; 039import java.nio.file.DirectoryStream; 040import java.nio.file.FileSystems; 041import java.nio.file.Files; 042import java.nio.file.Path; 043import java.security.InvalidKeyException; 044import java.security.KeyFactory; 045import java.security.KeyStore; 046import java.security.KeyStoreException; 047import java.security.NoSuchAlgorithmException; 048import java.security.NoSuchProviderException; 049import java.security.PublicKey; 050import java.security.SignatureException; 051import java.security.cert.CertificateException; 052import java.security.spec.InvalidKeySpecException; 053import java.security.spec.X509EncodedKeySpec; 054import java.util.ArrayList; 055import java.util.Collection; 056import java.util.Enumeration; 057import java.util.List; 058import java.util.Locale; 059import java.util.Properties; 060 061import javax.swing.JOptionPane; 062 063import org.openstreetmap.josm.Main; 064import org.openstreetmap.josm.data.Preferences; 065 066/** 067 * {@code PlatformHook} implementation for Microsoft Windows systems. 068 * @since 1023 069 */ 070public class PlatformHookWindows implements PlatformHook { 071 072 /** 073 * Simple data class to hold information about a font. 074 * 075 * Used for fontconfig.properties files. 076 */ 077 public static class FontEntry { 078 /** 079 * The character subset. Basically a free identifier, but should be unique. 080 */ 081 @Preferences.pref 082 public String charset; 083 084 /** 085 * Platform font name. 086 */ 087 @Preferences.pref 088 @Preferences.writeExplicitly 089 public String name = ""; 090 091 /** 092 * File name. 093 */ 094 @Preferences.pref 095 @Preferences.writeExplicitly 096 public String file = ""; 097 098 /** 099 * Constructs a new {@code FontEntry}. 100 */ 101 public FontEntry() { 102 // Default constructor needed for construction by reflection 103 } 104 105 /** 106 * Constructs a new {@code FontEntry}. 107 * @param charset The character subset. Basically a free identifier, but should be unique 108 * @param name Platform font name 109 * @param file File name 110 */ 111 public FontEntry(String charset, String name, String file) { 112 this.charset = charset; 113 this.name = name; 114 this.file = file; 115 } 116 } 117 118 private static final byte[] INSECURE_PUBLIC_KEY = new byte[] { 119 0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48, 120 (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0, 121 0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88, 122 (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc, 123 0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2, 124 (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51, 125 (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3, 126 (byte) 0xc7, (byte) 0xc3, 0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d, 127 (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba, 128 (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7, 129 (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc, 130 0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc, 131 (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c, 132 0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03, 133 (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0, 134 (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b, 135 0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60, 136 (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6, 137 (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f, 138 (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8, 139 0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4, 140 (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63, 141 (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e, 142 0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01 143 }; 144 145 private static final String WINDOWS_ROOT = "Windows-ROOT"; 146 147 @Override 148 public void afterPrefStartupHook() { 149 extendFontconfig("fontconfig.properties.src"); 150 } 151 152 @Override 153 public void openUrl(String url) throws IOException { 154 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 155 } 156 157 @Override 158 public void initSystemShortcuts() { 159 // CHECKSTYLE.OFF: LineLength 160 //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK); 161 Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results 162 163 // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts 164 165 // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all 166 167 // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page 168 Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic(); 169 170 // Ease of Access keyboard shortcuts 171 Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off 172 Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off 173 //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?) 174 175 // General keyboard shortcuts 176 //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0); // Display Help 177 Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK); // Copy the selected item 178 Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK); // Cut the selected item 179 Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK); // Paste the selected item 180 Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK); // Undo an action 181 Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK); // Redo an action 182 //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0); // Delete the selected item and move it to the Recycle Bin 183 //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK); // Delete the selected item without moving it to the Recycle Bin first 184 //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0); // Rename the selected item 185 Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next word 186 Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous word 187 Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK); // Move the cursor to the beginning of the next paragraph 188 Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK); // Move the cursor to the beginning of the previous paragraph 189 //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text 190 //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text 191 //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text 192 //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text 193 //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 194 //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 195 //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 196 //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 197 //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 ?) 198 //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 ?) 199 //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 ?) 200 //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 ?) 201 Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK); // Select all items in a document or window 202 //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0); // Search for a file or folder 203 Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic(); // Display properties for the selected item 204 Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program 205 Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic(); // Open the shortcut menu for the active window 206 //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) 207 Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic(); // Switch between open items 208 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 209 //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 ?) 210 //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 ?) 211 Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic(); // Cycle through items in the order in which they were opened 212 //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0); // Cycle through screen elements in a window or on the desktop 213 //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0); // Display the address bar list in Windows Explorer 214 Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK); // Display the shortcut menu for the selected item 215 Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu 216 //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0); // Activate the menu bar in the active program 217 //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0); // Open the next menu to the right, or open a submenu 218 //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0); // Open the next menu to the left, or close a submenu 219 //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0); // Refresh the active window 220 //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK); // View the folder one level up in Windows Explorer 221 //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0); // Cancel the current task 222 Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager 223 Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic(); // Switch the input language when multiple input languages are enabled 224 Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic(); // Switch the keyboard layout when multiple keyboard layouts are enabled 225 //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear) 226 // CHECKSTYLE.ON: LineLength 227 } 228 229 @Override 230 public String getDefaultStyle() { 231 return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; 232 } 233 234 @Override 235 public boolean rename(File from, File to) { 236 if (to.exists()) 237 Utils.deleteFile(to); 238 return from.renameTo(to); 239 } 240 241 @Override 242 public String getOSDescription() { 243 return Utils.strip(System.getProperty("os.name")) + ' ' + 244 ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit"; 245 } 246 247 /** 248 * Loads Windows-ROOT keystore. 249 * @return Windows-ROOT keystore 250 * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found 251 * @throws CertificateException if any of the certificates in the keystore could not be loaded 252 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given 253 * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT" 254 * @since 7343 255 */ 256 public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException { 257 KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT); 258 ks.load(null, null); 259 return ks; 260 } 261 262 /** 263 * Removes potential insecure certificates installed with previous versions of JOSM on Windows. 264 * @throws NoSuchAlgorithmException on unsupported signature algorithms 265 * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded 266 * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT" 267 * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given 268 * @since 7335 269 */ 270 public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { 271 // 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) 272 PublicKey insecurePubKey = null; 273 try { 274 insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY)); 275 } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { 276 Main.error(e); 277 return; 278 } 279 KeyStore ks = getRootKeystore(); 280 Enumeration<String> en = ks.aliases(); 281 Collection<String> insecureCertificates = new ArrayList<>(); 282 while (en.hasMoreElements()) { 283 String alias = en.nextElement(); 284 // Look for certificates associated with a private key 285 if (ks.isKeyEntry(alias)) { 286 try { 287 ks.getCertificate(alias).verify(insecurePubKey); 288 // If no exception, this is a certificate signed with the insecure key -> remove it 289 insecureCertificates.add(alias); 290 } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) { 291 // If exception this is not a certificate related to JOSM, just trace it 292 Main.trace(alias + " --> " + e.getClass().getName()); 293 Main.trace(e); 294 } 295 } 296 } 297 // Remove insecure certificates 298 if (!insecureCertificates.isEmpty()) { 299 StringBuilder message = new StringBuilder("<html>"); 300 message.append(tr("A previous version of JOSM has installed a custom certificate "+ 301 "in order to provide HTTPS support for Remote Control:")) 302 .append("<br><ul>"); 303 for (String alias : insecureCertificates) { 304 message.append("<li>") 305 .append(alias) 306 .append("</li>"); 307 } 308 message.append("</ul>") 309 .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+ 310 "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+ 311 "For your own safety, <b>please click Yes</b> in next dialog.")) 312 .append("</html>"); 313 JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE); 314 for (String alias : insecureCertificates) { 315 Main.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias)); 316 try { 317 ks.deleteEntry(alias); 318 } catch (KeyStoreException e) { 319 Main.error(e, tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage())); 320 } 321 } 322 } 323 } 324 325 @Override 326 public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert) 327 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { 328 KeyStore ks = getRootKeystore(); 329 // Look for certificate to install 330 String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate()); 331 if (alias != null) { 332 // JOSM certificate found, return 333 Main.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias)); 334 return false; 335 } 336 if (!GraphicsEnvironment.isHeadless()) { 337 // JOSM certificate not found, warn user 338 StringBuilder message = new StringBuilder("<html>"); 339 message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+ 340 "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+ 341 "You are now going to be prompted by Windows to confirm this operation.<br>"+ 342 "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+ 343 "If unsure, you can also click No then disable HTTPS support in Remote Control preferences.")) 344 .append("</html>"); 345 JOptionPane.showMessageDialog(Main.parent, message.toString(), 346 tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE); 347 } 348 // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox 349 Main.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT)); 350 ks.setEntry(entryAlias, trustedCert, null); 351 return true; 352 } 353 354 @Override 355 public File getDefaultCacheDirectory() { 356 String p = System.getenv("LOCALAPPDATA"); 357 if (p == null || p.isEmpty()) { 358 // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined 359 p = System.getenv("APPDATA"); 360 } 361 return new File(new File(p, Main.pref.getJOSMDirectoryBaseName()), "cache"); 362 } 363 364 @Override 365 public File getDefaultPrefDirectory() { 366 return new File(System.getenv("APPDATA"), Main.pref.getJOSMDirectoryBaseName()); 367 } 368 369 @Override 370 public File getDefaultUserDataDirectory() { 371 // Use preferences directory by default 372 return Main.pref.getPreferencesDirectory(); 373 } 374 375 /** 376 * <p>Add more fallback fonts to the Java runtime, in order to get 377 * support for more scripts.</p> 378 * 379 * <p>The font configuration in Java doesn't include some Indic scripts, 380 * even though MS Windows ships with fonts that cover these unicode ranges.</p> 381 * 382 * <p>To fix this, the fontconfig.properties template is copied to the JOSM 383 * cache folder. Then, the additional entries are added to the font 384 * configuration. Finally the system property "sun.awt.fontconfig" is set 385 * to the customized fontconfig.properties file.</p> 386 * 387 * <p>This is a crude hack, but better than no font display at all for these languages. 388 * There is no guarantee, that the template file 389 * ($JAVA_HOME/lib/fontconfig.properties.src) matches the default 390 * configuration (which is in a binary format). 391 * Furthermore, the system property "sun.awt.fontconfig" is undocumented and 392 * may no longer work in future versions of Java.</p> 393 * 394 * <p>Related Java bug: <a href="https://bugs.openjdk.java.net/browse/JDK-8008572">JDK-8008572</a></p> 395 * 396 * @param templateFileName file name of the fontconfig.properties template file 397 */ 398 protected void extendFontconfig(String templateFileName) { 399 String customFontconfigFile = Main.pref.get("fontconfig.properties", null); 400 if (customFontconfigFile != null) { 401 Utils.updateSystemProperty("sun.awt.fontconfig", customFontconfigFile); 402 return; 403 } 404 if (!Main.pref.getBoolean("font.extended-unicode", true)) 405 return; 406 407 String javaLibPath = System.getProperty("java.home") + File.separator + "lib"; 408 Path templateFile = FileSystems.getDefault().getPath(javaLibPath, templateFileName); 409 if (!Files.isReadable(templateFile)) { 410 Main.warn("extended font config - unable to find font config template file {0}", templateFile.toString()); 411 return; 412 } 413 try (FileInputStream fis = new FileInputStream(templateFile.toFile())) { 414 Properties props = new Properties(); 415 props.load(fis); 416 byte[] content = Files.readAllBytes(templateFile); 417 File cachePath = Main.pref.getCacheDirectory(); 418 Path fontconfigFile = cachePath.toPath().resolve("fontconfig.properties"); 419 OutputStream os = Files.newOutputStream(fontconfigFile); 420 os.write(content); 421 try (Writer w = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) { 422 Collection<FontEntry> extrasPref = Main.pref.getListOfStructs( 423 "font.extended-unicode.extra-items", getAdditionalFonts(), FontEntry.class); 424 Collection<FontEntry> extras = new ArrayList<>(); 425 w.append("\n\n# Added by JOSM to extend unicode coverage of Java font support:\n\n"); 426 List<String> allCharSubsets = new ArrayList<>(); 427 for (FontEntry entry: extrasPref) { 428 Collection<String> fontsAvail = getInstalledFonts(); 429 if (fontsAvail != null && fontsAvail.contains(entry.file.toUpperCase(Locale.ENGLISH))) { 430 if (!allCharSubsets.contains(entry.charset)) { 431 allCharSubsets.add(entry.charset); 432 extras.add(entry); 433 } else { 434 Main.trace("extended font config - already registered font for charset ''{0}'' - skipping ''{1}''", 435 entry.charset, entry.name); 436 } 437 } else { 438 Main.trace("extended font config - Font ''{0}'' not found on system - skipping", entry.name); 439 } 440 } 441 for (FontEntry entry: extras) { 442 allCharSubsets.add(entry.charset); 443 if ("".equals(entry.name)) { 444 continue; 445 } 446 String key = "allfonts." + entry.charset; 447 String value = entry.name; 448 String prevValue = props.getProperty(key); 449 if (prevValue != null && !prevValue.equals(value)) { 450 Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value); 451 } 452 w.append(key + '=' + value + '\n'); 453 } 454 w.append('\n'); 455 for (FontEntry entry: extras) { 456 if ("".equals(entry.name) || "".equals(entry.file)) { 457 continue; 458 } 459 String key = "filename." + entry.name.replace(' ', '_'); 460 String value = entry.file; 461 String prevValue = props.getProperty(key); 462 if (prevValue != null && !prevValue.equals(value)) { 463 Main.warn("extended font config - overriding ''{0}={1}'' with ''{2}''", key, prevValue, value); 464 } 465 w.append(key + '=' + value + '\n'); 466 } 467 w.append('\n'); 468 String fallback = props.getProperty("sequence.fallback"); 469 if (fallback != null) { 470 w.append("sequence.fallback=" + fallback + ',' + Utils.join(",", allCharSubsets) + '\n'); 471 } else { 472 w.append("sequence.fallback=" + Utils.join(",", allCharSubsets) + '\n'); 473 } 474 } 475 Utils.updateSystemProperty("sun.awt.fontconfig", fontconfigFile.toString()); 476 } catch (IOException ex) { 477 Main.error(ex); 478 } 479 } 480 481 /** 482 * Get a list of fonts that are installed on the system. 483 * 484 * Must be done without triggering the Java Font initialization. 485 * (See {@link #extendFontconfig(java.lang.String)}, have to set system 486 * property first, which is then read by sun.awt.FontConfiguration upon initialization.) 487 * 488 * @return list of file names 489 */ 490 protected Collection<String> getInstalledFonts() { 491 // Cannot use GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames() 492 // because we have to set the system property before Java initializes its fonts. 493 // Use more low-level method to find the installed fonts. 494 List<String> fontsAvail = new ArrayList<>(); 495 Path fontPath = FileSystems.getDefault().getPath(System.getenv("SYSTEMROOT"), "Fonts"); 496 try (DirectoryStream<Path> ds = Files.newDirectoryStream(fontPath)) { 497 for (Path p : ds) { 498 Path filename = p.getFileName(); 499 if (filename != null) { 500 fontsAvail.add(filename.toString().toUpperCase(Locale.ENGLISH)); 501 } 502 } 503 fontsAvail.add(""); // for devanagari 504 } catch (IOException ex) { 505 Main.error(ex, false); 506 Main.warn("extended font config - failed to load available Fonts"); 507 fontsAvail = null; 508 } 509 return fontsAvail; 510 } 511 512 /** 513 * Get default list of additional fonts to add to the configuration. 514 * 515 * Java will choose thee first font in the list that can render a certain character. 516 * 517 * @return list of FontEntry objects 518 */ 519 protected Collection<FontEntry> getAdditionalFonts() { 520 Collection<FontEntry> def = new ArrayList<>(33); 521 def.add(new FontEntry("devanagari", "", "")); // just include in fallback list font already defined in template 522 523 // Windows scripts: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx 524 // IE default fonts: https://msdn.microsoft.com/en-us/library/ie/dn467844(v=vs.85).aspx 525 526 // Windows 10 and later 527 def.add(new FontEntry("historic", "Segoe UI Historic", "SEGUIHIS.TTF")); // historic charsets 528 529 // Windows 8/8.1 and later 530 def.add(new FontEntry("javanese", "Javanese Text", "JAVATEXT.TTF")); // ISO 639: jv 531 def.add(new FontEntry("leelawadee", "Leelawadee", "LEELAWAD.TTF")); // ISO 639: bug 532 def.add(new FontEntry("myanmar", "Myanmar Text", "MMRTEXT.TTF")); // ISO 639: my 533 def.add(new FontEntry("nirmala", "Nirmala UI", "NIRMALA.TTF")); // ISO 639: sat,srb 534 def.add(new FontEntry("segoeui", "Segoe UI", "SEGOEUI.TTF")); // ISO 639: lis 535 536 // Windows 7 and later 537 def.add(new FontEntry("nko_tifinagh_vai_osmanya", "Ebrima", "EBRIMA.TTF")); // ISO 639: ber. Nko only since Win 8 538 def.add(new FontEntry("khmer1", "Khmer UI", "KHMERUI.TTF")); // ISO 639: km 539 def.add(new FontEntry("lao1", "Lao UI", "LAOUI.TTF")); // ISO 639: lo 540 def.add(new FontEntry("tai_le", "Microsoft Tai Le", "TAILE.TTF")); // ISO 639: khb 541 def.add(new FontEntry("new_tai_lue", "Microsoft New Tai Lue", "NTHAILU.TTF")); // ISO 639: khb 542 543 // Windows Vista and later: 544 def.add(new FontEntry("ethiopic", "Nyala", "NYALA.TTF")); // ISO 639: am,gez,ti 545 def.add(new FontEntry("tibetan", "Microsoft Himalaya", "HIMALAYA.TTF")); // ISO 639: bo,dz 546 def.add(new FontEntry("cherokee", "Plantagenet Cherokee", "PLANTC.TTF")); // ISO 639: chr 547 def.add(new FontEntry("unified_canadian", "Euphemia", "EUPHEMIA.TTF")); // ISO 639: cr,in 548 def.add(new FontEntry("khmer2", "DaunPenh", "DAUNPENH.TTF")); // ISO 639: km 549 def.add(new FontEntry("khmer3", "MoolBoran", "MOOLBOR.TTF")); // ISO 639: km 550 def.add(new FontEntry("lao_thai", "DokChampa", "DOKCHAMP.TTF")); // ISO 639: lo 551 def.add(new FontEntry("mongolian", "Mongolian Baiti", "MONBAITI.TTF")); // ISO 639: mn 552 def.add(new FontEntry("oriya", "Kalinga", "KALINGA.TTF")); // ISO 639: or 553 def.add(new FontEntry("sinhala", "Iskoola Pota", "ISKPOTA.TTF")); // ISO 639: si 554 def.add(new FontEntry("yi", "Yi Baiti", "MSYI.TTF")); // ISO 639: ii 555 556 // Windows XP and later 557 def.add(new FontEntry("gujarati", "Shruti", "SHRUTI.TTF")); 558 def.add(new FontEntry("kannada", "Tunga", "TUNGA.TTF")); 559 def.add(new FontEntry("gurmukhi", "Raavi", "RAAVI.TTF")); 560 def.add(new FontEntry("telugu", "Gautami", "GAUTAMI.TTF")); 561 def.add(new FontEntry("bengali", "Vrinda", "VRINDA.TTF")); // since XP SP2 562 def.add(new FontEntry("syriac", "Estrangelo Edessa", "ESTRE.TTF")); // ISO 639: arc 563 def.add(new FontEntry("thaana", "MV Boli", "MVBOLI.TTF")); // ISO 639: dv 564 def.add(new FontEntry("malayalam", "Kartika", "KARTIKA.TTF")); // ISO 639: ml; since XP SP2 565 566 // Windows 2000 and later 567 def.add(new FontEntry("tamil", "Latha", "LATHA.TTF")); 568 569 // Comes with MS Office & Outlook 2000. Good unicode coverage, so add if available. 570 def.add(new FontEntry("arialuni", "Arial Unicode MS", "ARIALUNI.TTF")); 571 572 return def; 573 } 574}