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.security.InvalidKeyException;
034import java.security.KeyFactory;
035import java.security.KeyStore;
036import java.security.KeyStoreException;
037import java.security.NoSuchAlgorithmException;
038import java.security.NoSuchProviderException;
039import java.security.PublicKey;
040import java.security.SignatureException;
041import java.security.cert.CertificateException;
042import java.security.spec.InvalidKeySpecException;
043import java.security.spec.X509EncodedKeySpec;
044import java.util.ArrayList;
045import java.util.Collection;
046import java.util.Enumeration;
047
048import javax.swing.JOptionPane;
049
050import org.openstreetmap.josm.Main;
051
052/**
053  * {@code PlatformHook} implementation for Microsoft Windows systems.
054  * @since 1023
055  */
056public class PlatformHookWindows extends PlatformHookUnixoid implements PlatformHook {
057
058    private static final byte[] INSECURE_PUBLIC_KEY = new byte[] {
059        0x30, (byte) 0x82, 0x1, 0x22, 0x30, 0xd, 0x6, 0x9, 0x2a, (byte) 0x86, 0x48,
060        (byte) 0x86, (byte) 0xf7, 0xd, 0x1, 0x1, 0x1, 0x5, 0x0, 0x3, (byte) 0x82, 0x1, 0xf, 0x0,
061        0x30, (byte) 0x82, 0x01, 0x0a, 0x02, (byte) 0x82, 0x01, 0x01, 0x00, (byte) 0x95, (byte) 0x95, (byte) 0x88,
062        (byte) 0x84, (byte) 0xc8, (byte) 0xd9, 0x6b, (byte) 0xc5, (byte) 0xda, 0x0b, 0x69, (byte) 0xbf, (byte) 0xfc,
063        0x7e, (byte) 0xb9, (byte) 0x96, 0x2c, (byte) 0xeb, (byte) 0x8f, (byte) 0xbc, 0x6e, 0x40, (byte) 0xe6, (byte) 0xe2,
064        (byte) 0xfc, (byte) 0xf1, 0x7f, 0x73, (byte) 0xa7, (byte) 0x9d, (byte) 0xde, (byte) 0xc7, (byte) 0x88, 0x57, 0x51,
065        (byte) 0x84, (byte) 0xed, (byte) 0x96, (byte) 0xfb, (byte) 0xe1, 0x38, (byte) 0xef, 0x08, 0x2b, (byte) 0xf3,
066        (byte) 0xc7, (byte) 0xc3,  0x5d, (byte) 0xfe, (byte) 0xf9, 0x51, (byte) 0xe6, 0x29, (byte) 0xfc, (byte) 0xe5, 0x0d,
067        (byte) 0xa1, 0x0d, (byte) 0xa8, (byte) 0xb4, (byte) 0xae, 0x26, 0x18, 0x19, 0x4d, 0x6c, 0x0c, 0x3b, 0x12, (byte) 0xba,
068        (byte) 0xbc, 0x5f, 0x32, (byte) 0xb3, (byte) 0xbe, (byte) 0x9d, 0x17, 0x0d, 0x4d, 0x2f, 0x1a, 0x48, (byte) 0xb7,
069        (byte) 0xac, (byte) 0xf7, 0x1a, 0x43, 0x01, (byte) 0x97, (byte) 0xf4, (byte) 0xf8, 0x4c, (byte) 0xbb, 0x6a, (byte) 0xbc,
070        0x33, (byte) 0xe1, 0x73, 0x1e, (byte) 0x86, (byte) 0xfb, 0x2e, (byte) 0xb1, 0x63, 0x75, (byte) 0x85, (byte) 0xdc,
071        (byte) 0x82, 0x6c, 0x28, (byte) 0xf1, (byte) 0xe3, (byte) 0x90, 0x63, (byte) 0x9d, 0x3d, 0x48, (byte) 0x8a, (byte) 0x8c,
072        0x47, (byte) 0xe2, 0x10, 0x0b, (byte) 0xef, (byte) 0x91, (byte) 0x94, (byte) 0xb0, 0x6c, 0x4c, (byte) 0x80, 0x76, 0x03,
073        (byte) 0xe1, (byte) 0xb6, (byte) 0x90, (byte) 0x87, (byte) 0xd9, (byte) 0xae, (byte) 0xf4, (byte) 0x8e, (byte) 0xe0,
074        (byte) 0x9f, (byte) 0xe7, 0x3a, 0x2c, 0x2f, 0x21, (byte) 0xd4, 0x46, (byte) 0xba, (byte) 0x95, 0x70, (byte) 0xa9, 0x5b,
075        0x20, 0x2a, (byte) 0xfa, 0x52, 0x3e, (byte) 0x9d, (byte) 0xd9, (byte) 0xef, 0x28, (byte) 0xc5, (byte) 0xd1, 0x60,
076        (byte) 0x89, 0x68, 0x6e, 0x7f, (byte) 0xd7, (byte) 0x9e, (byte) 0x89, 0x4c, (byte) 0xeb, 0x4d, (byte) 0xd2, (byte) 0xc6,
077        (byte) 0xf4, 0x2d, 0x02, 0x5d, (byte) 0xda, (byte) 0xde, 0x33, (byte) 0xfe, (byte) 0xc1, 0x7e, (byte) 0xde, 0x4f, 0x1f,
078        (byte) 0x9b, 0x6e, 0x6f, 0x0f, 0x66, 0x71, 0x19, (byte) 0xe9, 0x43, 0x3c, (byte) 0x83, 0x0a, 0x0f, 0x28, 0x21, (byte) 0xc8,
079        0x38, (byte) 0xd3, 0x4e, 0x48, (byte) 0xdf, (byte) 0xd4, (byte) 0x99, (byte) 0xb5, (byte) 0xc6, (byte) 0x8d, (byte) 0xd4,
080        (byte) 0xc1, 0x69, 0x58, 0x79, (byte) 0x82, 0x32, (byte) 0x82, (byte) 0xd4, (byte) 0x86, (byte) 0xe2, 0x04, 0x08, 0x63,
081        (byte) 0x87, (byte) 0xf0, 0x2a, (byte) 0xf6, (byte) 0xec, 0x3e, 0x51, 0x0f, (byte) 0xda, (byte) 0xb4, 0x67, 0x19, 0x5e,
082        0x16, 0x02, (byte) 0x9f, (byte) 0xf1, 0x19, 0x0c, 0x3e, (byte) 0xb8, 0x04, 0x49, 0x07, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01
083    };
084
085    private static final String WINDOWS_ROOT = "Windows-ROOT";
086
087    @Override
088    public void openUrl(String url) throws IOException {
089        Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
090    }
091
092    @Override
093    public void initSystemShortcuts() {
094        //Shortcut.registerSystemCut("system:menuexit", tr("reserved"), VK_Q, CTRL_DOWN_MASK);
095        Shortcut.registerSystemShortcut("system:duplicate", tr("reserved"), VK_D, CTRL_DOWN_MASK); // not really system, but to avoid odd results
096
097        // Windows 7 shortcuts: http://windows.microsoft.com/en-US/windows7/Keyboard-shortcuts
098
099        // Shortcuts with setAutomatic(): items with automatic shortcuts will not be added to the menu bar at all
100
101        // Don't know why Ctrl-Alt-Del isn't even listed on official Microsoft support page
102        Shortcut.registerSystemShortcut("system:reset", tr("reserved"), VK_DELETE, CTRL_DOWN_MASK | ALT_DOWN_MASK).setAutomatic();
103
104        // Ease of Access keyboard shortcuts
105        Shortcut.registerSystemShortcut("microsoft-reserved-01", tr("reserved"), VK_PRINTSCREEN, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn High Contrast on or off
106        Shortcut.registerSystemShortcut("microsoft-reserved-02", tr("reserved"), VK_NUM_LOCK, ALT_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Turn Mouse Keys on or off
107        //Shortcut.registerSystemCut("microsoft-reserved-03", tr("reserved"), VK_U, );// Open the Ease of Access Center (TODO: Windows-U, how to handle it in Java ?)
108
109        // General keyboard shortcuts
110        //Shortcut.registerSystemShortcut("system:help", tr("reserved"), VK_F1, 0);                            // Display Help
111        Shortcut.registerSystemShortcut("system:copy", tr("reserved"), VK_C, CTRL_DOWN_MASK);                // Copy the selected item
112        Shortcut.registerSystemShortcut("system:cut", tr("reserved"), VK_X, CTRL_DOWN_MASK);                 // Cut the selected item
113        Shortcut.registerSystemShortcut("system:paste", tr("reserved"), VK_V, CTRL_DOWN_MASK);               // Paste the selected item
114        Shortcut.registerSystemShortcut("system:undo", tr("reserved"), VK_Z, CTRL_DOWN_MASK);                // Undo an action
115        Shortcut.registerSystemShortcut("system:redo", tr("reserved"), VK_Y, CTRL_DOWN_MASK);                // Redo an action
116        //Shortcut.registerSystemCut("microsoft-reserved-10", tr("reserved"), VK_DELETE, 0);                  // Delete the selected item and move it to the Recycle Bin
117        //Shortcut.registerSystemCut("microsoft-reserved-11", tr("reserved"), VK_DELETE, SHIFT_DOWN_MASK);    // Delete the selected item without moving it to the Recycle Bin first
118        //Shortcut.registerSystemCut("system:rename", tr("reserved"), VK_F2, 0);                          // Rename the selected item
119        Shortcut.registerSystemShortcut("system:movefocusright", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK);  // Move the cursor to the beginning of the next word
120        Shortcut.registerSystemShortcut("system:movefocusleft", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK);    // Move the cursor to the beginning of the previous word
121        Shortcut.registerSystemShortcut("system:movefocusdown", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK);    // Move the cursor to the beginning of the next paragraph
122        Shortcut.registerSystemShortcut("system:movefocusup", tr("reserved"), VK_UP, CTRL_DOWN_MASK);        // Move the cursor to the beginning of the previous paragraph
123        //Shortcut.registerSystemCut("microsoft-reserved-17", tr("reserved"), VK_RIGHT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK); // Select a block of text
124        //Shortcut.registerSystemCut("microsoft-reserved-18", tr("reserved"), VK_LEFT, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);  // Select a block of text
125        //Shortcut.registerSystemCut("microsoft-reserved-19", tr("reserved"), VK_DOWN, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);  // Select a block of text
126        //Shortcut.registerSystemCut("microsoft-reserved-20", tr("reserved"), VK_UP, CTRL_DOWN_MASK | SHIFT_DOWN_MASK);    // Select a block of text
127        //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
128        //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
129        //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
130        //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
131        //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 ?)
132        //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 ?)
133        //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 ?)
134        //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 ?)
135        Shortcut.registerSystemShortcut("system:selectall", tr("reserved"), VK_A, CTRL_DOWN_MASK);           // Select all items in a document or window
136        //Shortcut.registerSystemCut("system:search", tr("reserved"), VK_F3, 0);                          // Search for a file or folder
137        Shortcut.registerSystemShortcut("microsoft-reserved-31", tr("reserved"), VK_ENTER, ALT_DOWN_MASK).setAutomatic();   // Display properties for the selected item
138        Shortcut.registerSystemShortcut("system:exit", tr("reserved"), VK_F4, ALT_DOWN_MASK).setAutomatic(); // Close the active item, or exit the active program
139        Shortcut.registerSystemShortcut("microsoft-reserved-33", tr("reserved"), VK_SPACE, ALT_DOWN_MASK).setAutomatic();   // Open the shortcut menu for the active window
140        //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)
141        Shortcut.registerSystemShortcut("microsoft-reserved-35", tr("reserved"), VK_TAB, ALT_DOWN_MASK).setAutomatic();     // Switch between open items
142        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
143        //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 ?)
144        //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 ?)
145        Shortcut.registerSystemShortcut("microsoft-reserved-39", tr("reserved"), VK_ESCAPE, ALT_DOWN_MASK).setAutomatic();  // Cycle through items in the order in which they were opened
146        //Shortcut.registerSystemCut("microsoft-reserved-40", tr("reserved"), VK_F6, 0);                  // Cycle through screen elements in a window or on the desktop
147        //Shortcut.registerSystemCut("microsoft-reserved-41", tr("reserved"), VK_F4, 0);                  // Display the address bar list in Windows Explorer
148        Shortcut.registerSystemShortcut("microsoft-reserved-42", tr("reserved"), VK_F10, SHIFT_DOWN_MASK);   // Display the shortcut menu for the selected item
149        Shortcut.registerSystemShortcut("microsoft-reserved-43", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK).setAutomatic(); // Open the Start menu
150        //Shortcut.registerSystemShortcut("microsoft-reserved-44", tr("reserved"), VK_F10, 0);                 // Activate the menu bar in the active program
151        //Shortcut.registerSystemCut("microsoft-reserved-45", tr("reserved"), VK_RIGHT, 0);               // Open the next menu to the right, or open a submenu
152        //Shortcut.registerSystemCut("microsoft-reserved-46", tr("reserved"), VK_LEFT, 0);                // Open the next menu to the left, or close a submenu
153        //Shortcut.registerSystemCut("microsoft-reserved-47", tr("reserved"), VK_F5, 0);                  // Refresh the active window
154        //Shortcut.registerSystemCut("microsoft-reserved-48", tr("reserved"), VK_UP, ALT_DOWN_MASK);      // View the folder one level up in Windows Explorer
155        //Shortcut.registerSystemCut("microsoft-reserved-49", tr("reserved"), VK_ESCAPE, 0);              // Cancel the current task
156        Shortcut.registerSystemShortcut("microsoft-reserved-50", tr("reserved"), VK_ESCAPE, CTRL_DOWN_MASK | SHIFT_DOWN_MASK).setAutomatic(); // Open Task Manager
157        Shortcut.registerSystemShortcut("microsoft-reserved-51", tr("reserved"), VK_SHIFT, ALT_DOWN_MASK).setAutomatic();   // Switch the input language when multiple input languages are enabled
158        Shortcut.registerSystemShortcut("microsoft-reserved-52", tr("reserved"), VK_SHIFT, CTRL_DOWN_MASK).setAutomatic();  // Switch the keyboard layout when multiple keyboard layouts are enabled
159        //Shortcut.registerSystemCut("microsoft-reserved-53", tr("reserved"), ); // Change the reading direction of text in right-to-left reading languages (TODO: unclear)
160    }
161
162    @Override
163    public String getDefaultStyle() {
164        return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
165    }
166
167    @Override
168    public boolean rename(File from, File to) {
169        if (to.exists())
170            to.delete();
171        return from.renameTo(to);
172    }
173
174    @Override
175    public String getOSDescription() {
176        return Utils.strip(System.getProperty("os.name")) + " " +
177                ((System.getenv("ProgramFiles(x86)") == null) ? "32" : "64") + "-Bit";
178    }
179
180    /**
181     * Loads Windows-ROOT keystore.
182     * @return Windows-ROOT keystore
183     * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
184     * @throws CertificateException if any of the certificates in the keystore could not be loaded
185     * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
186     * @throws KeyStoreException if no Provider supports a KeyStore implementation for the type "Windows-ROOT"
187     * @since 7343
188     */
189    public static KeyStore getRootKeystore() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
190        KeyStore ks = KeyStore.getInstance(WINDOWS_ROOT);
191        ks.load(null, null);
192        return ks;
193    }
194
195    /**
196     * Removes potential insecure certificates installed with previous versions of JOSM on Windows.
197     * @throws NoSuchAlgorithmException on unsupported signature algorithms
198     * @throws CertificateException if any of the certificates in the Windows keystore could not be loaded
199     * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the type "Windows-ROOT"
200     * @throws IOException if there is an I/O or format problem with the keystore data, if a password is required but not given
201     * @since 7335
202     */
203    public static void removeInsecureCertificates() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
204        // 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)
205        PublicKey insecurePubKey = null;
206        try {
207            insecurePubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(INSECURE_PUBLIC_KEY));
208        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
209            Main.error(e);
210            return;
211        }
212        KeyStore ks = getRootKeystore();
213        Enumeration<String> en = ks.aliases();
214        Collection<String> insecureCertificates = new ArrayList<>();
215        while (en.hasMoreElements()) {
216            String alias = en.nextElement();
217            // Look for certificates associated with a private key
218            if (ks.isKeyEntry(alias)) {
219                try {
220                    ks.getCertificate(alias).verify(insecurePubKey);
221                    // If no exception, this is a certificate signed with the insecure key -> remove it
222                    insecureCertificates.add(alias);
223                } catch (InvalidKeyException | NoSuchProviderException | SignatureException e) {
224                    // If exception this is not a certificate related to JOSM, just trace it
225                    Main.trace(alias + " --> " + e.getClass().getName());
226                }
227            }
228        }
229        // Remove insecure certificates
230        if (!insecureCertificates.isEmpty()) {
231            StringBuilder message = new StringBuilder("<html>");
232            message.append(tr("A previous version of JOSM has installed a custom certificate "+
233                    "in order to provide HTTPS support for Remote Control:"));
234            message.append("<br><ul>");
235            for (String alias : insecureCertificates) {
236                message.append("<li>");
237                message.append(alias);
238                message.append("</li>");
239            }
240            message.append("</ul>");
241            message.append(tr("It appears it could be an important <b>security risk</b>.<br><br>"+
242                    "You are now going to be prompted by Windows to remove this insecure certificate.<br>"+
243                    "For your own safety, <b>please click Yes</b> in next dialog."));
244            message.append("</html>");
245            JOptionPane.showMessageDialog(Main.parent, message.toString(), tr("Warning"), JOptionPane.WARNING_MESSAGE);
246            for (String alias : insecureCertificates) {
247                Main.warn(tr("Removing insecure certificate from {0} keystore: {1}", WINDOWS_ROOT, alias));
248                try {
249                    ks.deleteEntry(alias);
250                } catch (KeyStoreException e) {
251                    Main.error(tr("Unable to remove insecure certificate from keystore: {0}", e.getMessage()));
252                }
253            }
254        }
255    }
256
257    @Override
258    public boolean setupHttpsCertificate(String entryAlias, KeyStore.TrustedCertificateEntry trustedCert)
259            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
260        KeyStore ks = getRootKeystore();
261        // Look for certificate to install
262        String alias = ks.getCertificateAlias(trustedCert.getTrustedCertificate());
263        if (alias != null) {
264            // JOSM certificate found, return
265            Main.debug(tr("JOSM localhost certificate found in {0} keystore: {1}", WINDOWS_ROOT, alias));
266            return false;
267        }
268        if (!GraphicsEnvironment.isHeadless()) {
269            // JOSM certificate not found, warn user
270            StringBuilder message = new StringBuilder("<html>");
271            message.append(tr("Remote Control is configured to provide HTTPS support.<br>"+
272                    "This requires to add a custom certificate generated by JOSM to the Windows Root CA store.<br><br>"+
273                    "You are now going to be prompted by Windows to confirm this operation.<br>"+
274                    "To enable proper HTTPS support, <b>please click Yes</b> in next dialog.<br><br>"+
275                    "If unsure, you can also click No then disable HTTPS support in Remote Control preferences."));
276            message.append("</html>");
277            JOptionPane.showMessageDialog(Main.parent, message.toString(),
278                    tr("HTTPS support in Remote Control"), JOptionPane.INFORMATION_MESSAGE);
279        }
280        // install it to Windows-ROOT keystore, used by IE, Chrome and Safari, but not by Firefox
281        Main.info(tr("Adding JOSM localhost certificate to {0} keystore", WINDOWS_ROOT));
282        ks.setEntry(entryAlias, trustedCert, null);
283        return true;
284    }
285
286    @Override
287    public File getDefaultCacheDirectory() {
288        String p = System.getenv("LOCALAPPDATA");
289        if (p == null || p.isEmpty()) {
290            // Fallback for Windows OS earlier than Windows Vista, where the variable is not defined
291            p = System.getenv("APPDATA");
292        }
293        return new File(new File(p, "JOSM"), "cache");
294    }
295
296    @Override
297    public File getDefaultPrefDirectory() {
298        return new File(System.getenv("APPDATA"), "JOSM");
299    }
300}