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