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.File;
032import java.io.IOException;
033import java.nio.file.DirectoryStream;
034import java.nio.file.FileSystems;
035import java.nio.file.Files;
036import java.nio.file.Path;
037import java.security.InvalidKeyException;
038import java.security.KeyFactory;
039import java.security.KeyStore;
040import java.security.KeyStoreException;
041import java.security.NoSuchAlgorithmException;
042import java.security.NoSuchProviderException;
043import java.security.PublicKey;
044import java.security.SignatureException;
045import java.security.cert.CertificateException;
046import java.security.spec.InvalidKeySpecException;
047import java.security.spec.X509EncodedKeySpec;
048import java.util.ArrayList;
049import java.util.Collection;
050import java.util.Enumeration;
051import java.util.List;
052import java.util.Locale;
053
054import javax.swing.JOptionPane;
055
056import org.openstreetmap.josm.Main;
057
058/**
059  * {@code PlatformHook} implementation for Microsoft Windows systems.
060  * @since 1023
061  */
062public class PlatformHookWindows extends PlatformHookUnixoid {
063
064    @Override
065    public void startupHook() {
066        // Invite users to install Java 8 if they are still with Java 7
067        String version = System.getProperty("java.version");
068        if (version != null && version.startsWith("1.7")) {
069            askUpdateJava(version);
070        }
071    }
072
073    private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
074        0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
075        (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
076        0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
077        (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
078        0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
079        (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
080        (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
081        (byte) 0xc7, (byte) 0xc3,  0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
082        (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
083        (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
084        (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
085        0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
086        (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
087        0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
088        (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
089        (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
090        0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
091        (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
092        (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
093        (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
094        0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
095        (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
096        (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
097        0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
098    };
099
100    private static final String WINDOWS_ROOT = "Windows-ROOT";
101
102    @Override
103    public void afterPrefStartupHook() {
104        extendFontconfig("fontconfig.properties.src");
105    }
106
107    @Override
108    public void openUrl(String url) throws IOException {
109        Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
110    }
111
112    @Override
113    public void initSystemShortcuts() {
114        // CHECKSTYLE.OFF: LineLength
115        //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK);
116        Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results
117
118        // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
119
120        // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all
121
122        // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page
123        Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic();
124
125        // Ease of Access keyboard shortcuts
126        Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off
127        Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off
128        //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?)
129
130        // General keyboard shortcuts
131        //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0);                            // Display Help
132        Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK);                // Copy the selected item
133        Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK);                 // Cut the selected item
134        Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK);               // Paste the selected item
135        Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK);                // Undo an action
136        Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK);                // Redo an action
137        //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0);                  // Delete the selected item and move it to the Recycle Bin
138        //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK);    // Delete the selected item without moving it to the Recycle Bin first
139        //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0);                          // Rename the selected item
140        Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK);  // Move the cursor to the beginning of the next word
141        Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK);    // Move the cursor to the beginning of the previous word
142        Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK);    // Move the cursor to the beginning of the next paragraph
143        Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK);        // Move the cursor to the beginning of the previous paragraph
144        //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
145        //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);  // Select a block of text
146        //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);  // Select a block of text
147        //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);    // Select a block of text
148        //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
149        //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
150        //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
151        //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
152        //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 ?)
153        //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 ?)
154        //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 ?)
155        //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 ?)
156        Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK);           // Select all items in a document or window
157        //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0);                          // Search for a file or folder
158        Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic();   // Display properties for the selected item
159        Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program
160        Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic();   // Open the shortcut menu for the active window
161        //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)
162        Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic();     // Switch between open items
163        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
164        //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 ?)
165        //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 ?)
166        Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic();  // Cycle through items in the order in which they were opened
167        //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0);                  // Cycle through screen elements in a window or on the desktop
168        //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0);                  // Display the address bar list in Windows Explorer
169        Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK);   // Display the shortcut menu for the selected item
170        Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu
171        //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0);                 // Activate the menu bar in the active program
172        //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0);               // Open the next menu to the right, or open a submenu
173        //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0);                // Open the next menu to the left, or close a submenu
174        //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0);                  // Refresh the active window
175        //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK);      // View the folder one level up in Windows Explorer
176        //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0);              // Cancel the current task
177        Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager
178        Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic();   // Switch the input language when multiple input languages are enabled
179        Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic();  // Switch the keyboard layout when multiple keyboard layouts are enabled
180        //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear)
181        // CHECKSTYLE.ON: LineLength
182    }
183
184    @Override
185    public String getDefaultStyle() {
186        return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
187    }
188
189    @Override
190    public boolean rename(File from, File to) {
191        if (to.exists())
192            Utils.deleteFile(to);
193        return from.renameTo(to);
194    }
195
196    @Override
197    public String getOSDescription() {
198        return Utils.strip(System.getProperty("os.name")) + ' ' +
199                ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit";
200    }
201
202    /**
203     * Loads Windows-ROOT keystore.
204     * @return Windows-ROOT keystore
205     * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
206     * @throws CertificateException if any of the certificates in the keystore could not be loaded
207     * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
208     * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT"
209     * @since 7343
210     */
211    public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
212        KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT);
213        ks.load(null, null);
214        return ks;
215    }
216
217    /**
218     * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
219     * @throws NoSuchAlgorithmException on unsupported signature algorithms
220     * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
221     * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
222     * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
223     * @since 7335
224     */
225    public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
226        // 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)
227        PublicKey insecurePubKey = null;
228        try {
229            insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
230        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
231            Main.error(e);
232            return;
233        }
234        KeyStore ks = getRootKeystore();
235        Enumeration<String> en = ks.aliases();
236        Collection<String> insecureCertificates = new ArrayList<>();
237        while (en.hasMoreElements()) {
238            String alias = en.nextElement();
239            // Look for certificates associated with a private key
240            if (ks.isKeyEntry(alias)) {
241                try {
242                    ks.getCertificate(alias).verify(insecurePubKey);
243                    // If no exception, this is a certificate signed with the insecure key -> remove it
244                    insecureCertificates.add(alias);
245                } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
246                    // If exception this is not a certificate related to JOSM, just trace it
247                    Main.trace(alias + " --> " + e.getClass().getName());
248                }
249            }
250        }
251        // Remove insecure certificates
252        if (!insecureCertificates.isEmpty()) {
253            StringBuilder message = new StringBuilder("<html>");
254            message.append(tr("A previous version of JOSM has installed a custom certificate "+
255                    "in order to provide HTTPS support for Remote Control:"))
256                   .append("<br><ul>");
257            for (String alias : insecureCertificates) {
258                message.append("<li>")
259                       .append(alias)
260                       .append("</li>");
261            }
262            message.append("</ul>")
263                   .append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
264                    "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
265                    "For your own safety, <b>please click Yes</b> in next dialog."))
266                   .append("</html>");
267            JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
268            for (String alias : insecureCertificates) {
269                Main.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
270                try {
271                    ks.deleteEntry(alias);
272                } catch (KeyStoreException e) {
273                    Main.error(tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()));
274                }
275            }
276        }
277    }
278
279    @Override
280    public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
281            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
282        KeyStore ks = getRootKeystore();
283        // Look for certificate to install
284        String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
285        if (alias != null) {
286            // JOSM certificate found, return
287            Main.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
288            return false;
289        }
290        if (!GraphicsEnvironment.isHeadless()) {
291            // JOSM certificate not found, warn user
292            StringBuilder message = new StringBuilder("<html>");
293            message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
294                    "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
295                    "You are now going to be prompted by Windows to confirm this operation.<br>"+
296                    "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
297                    "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."))
298                   .append("</html>");
299            JOptionPane.showMessageDialog(Main.parent, message.toString(),
300                    tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
301        }
302        // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
303        Main.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
304        ks.setEntry(entryAlias, trustedCert, null);
305        return true;
306    }
307
308    @Override
309    public File getDefaultCacheDirectory() {
310        String p = System.getenv("LOCALAPPDATA");
311        if (p == null || p.isEmpty()) {
312            // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined
313            p = System.getenv("APPDATA");
314        }
315        return new File(new File(p, "JOSM"), "cache");
316    }
317
318    @Override
319    public File getDefaultPrefDirectory() {
320        return new File(System.getenv("APPDATA"), "JOSM");
321    }
322
323    @Override
324    public Collection<String> getInstalledFonts() {
325        // Cannot use GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
326        // because we have to set the system property before Java initializes its fonts.
327        // Use more low-level method to find the installed fonts.
328        List<String> fontsAvail = new ArrayList<>();
329        Path fontPath = FileSystems.getDefault().getPath(System.getenv("SYSTEMROOT"), "Fonts");
330        try (DirectoryStream<Path> ds = Files.newDirectoryStream(fontPath)) {
331            for (Path p : ds) {
332                Path filename = p.getFileName();
333                if (filename != null) {
334                    fontsAvail.add(filename.toString().toUpperCase(Locale.ENGLISH));
335                }
336            }
337            fontsAvail.add(""); // for devanagari
338        } catch (IOException ex) {
339            Main.error(ex, false);
340            Main.warn("extended font config - failed to load available Fonts");
341            fontsAvail = null;
342        }
343        return fontsAvail;
344    }
345
346    @Override
347    public Collection<FontEntry> getAdditionalFonts() {
348        Collection<FontEntry> def = new ArrayList<>();
349        def.add(new FontEntry("devanagari", "", "")); // just include in fallback list font already defined in template
350
351        // Windows scripts: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx
352        // IE default fonts: https://msdn.microsoft.com/en-us/library/ie/dn467844(v=vs.85).aspx
353
354        // Windows 8/8.1 and later
355        def.add(new FontEntry("javanese", "Javanese Text", "JAVATEXT.TTF"));           // ISO 639: jv
356        def.add(new FontEntry("leelawadee", "Leelawadee", "LEELAWAD.TTF"));            // ISO 639: bug
357        def.add(new FontEntry("myanmar", "Myanmar Text", "MMRTEXT.TTF"));              // ISO 639: my
358        def.add(new FontEntry("nirmala", "Nirmala UI", "NIRMALA.TTF"));                // ISO 639: sat,srb
359        def.add(new FontEntry("segoeui", "Segoe UI", "SEGOEUI.TTF"));                  // ISO 639: lis
360
361        // Windows 7 and later
362        def.add(new FontEntry("nko_tifinagh_vai_osmanya", "Ebrima", "EBRIMA.TTF"));    // ISO 639: ber. Nko only since Win 8
363        def.add(new FontEntry("khmer1", "Khmer UI", "KHMERUI.TTF"));                   // ISO 639: km
364        def.add(new FontEntry("lao1", "Lao UI", "LAOUI.TTF"));                         // ISO 639: lo
365        def.add(new FontEntry("tai_le", "Microsoft Tai Le", "TAILE.TTF"));             // ISO 639: khb
366        def.add(new FontEntry("new_tai_lue", "Microsoft New Tai Lue", "NTHAILU.TTF")); // ISO 639: khb
367
368        // Windows Vista and later:
369        def.add(new FontEntry("ethiopic", "Nyala", "NYALA.TTF"));                   // ISO 639: am,gez,ti
370        def.add(new FontEntry("tibetan", "Microsoft Himalaya", "HIMALAYA.TTF"));    // ISO 639: bo,dz
371        def.add(new FontEntry("cherokee", "Plantagenet Cherokee", "PLANTC.TTF"));   // ISO 639: chr
372        def.add(new FontEntry("unified_canadian", "Euphemia", "EUPHEMIA.TTF"));     // ISO 639: cr,in
373        def.add(new FontEntry("khmer2", "DaunPenh", "DAUNPENH.TTF"));               // ISO 639: km
374        def.add(new FontEntry("khmer3", "MoolBoran", "MOOLBOR.TTF"));               // ISO 639: km
375        def.add(new FontEntry("lao_thai", "DokChampa", "DOKCHAMP.TTF"));            // ISO 639: lo
376        def.add(new FontEntry("mongolian", "Mongolian Baiti", "MONBAITI.TTF"));     // ISO 639: mn
377        def.add(new FontEntry("oriya", "Kalinga", "KALINGA.TTF"));                  // ISO 639: or
378        def.add(new FontEntry("sinhala", "Iskoola Pota", "ISKPOTA.TTF"));           // ISO 639: si
379        def.add(new FontEntry("yi", "Yi Baiti", "MSYI.TTF"));                       // ISO 639: ii
380
381        // Windows XP and later
382        def.add(new FontEntry("gujarati", "Shruti", "SHRUTI.TTF"));
383        def.add(new FontEntry("kannada", "Tunga", "TUNGA.TTF"));
384        def.add(new FontEntry("gurmukhi", "Raavi", "RAAVI.TTF"));
385        def.add(new FontEntry("telugu", "Gautami", "GAUTAMI.TTF"));
386        def.add(new FontEntry("bengali", "Vrinda", "VRINDA.TTF"));                  // since XP SP2
387        def.add(new FontEntry("syriac", "Estrangelo Edessa", "ESTRE.TTF"));         // ISO 639: arc
388        def.add(new FontEntry("thaana", "MV Boli", "MVBOLI.TTF"));                  // ISO 639: dv
389        def.add(new FontEntry("malayalam", "Kartika", "KARTIKA.TTF"));              // ISO 639: ml; since XP SP2
390
391        // Windows 2000 and later
392        def.add(new FontEntry("tamil", "Latha", "LATHA.TTF"));
393
394        // Comes with MS Office & Outlook 2000. Good unicode coverage, so add if available.
395        def.add(new FontEntry("arialuni", "Arial Unicode MS", "ARIALUNI.TTF"));
396
397        return def;
398    }
399}