MultiBit HD / Technical library
INDEPENDENT KEYCHAINX RESOURCE
InstallationManager: data locations
Defines the application name and chooses the default application-data directory according to operating system. Read locateApplicationDataDirectory before treating a remembered path as definitive. Redirected profiles or copied disk images can require additional interpretation.
Source inspection only. Historical Java code; not executed or security-audited for this publication.
mbhd-core/src/main/java/org/multibit/hd/core/managers/InstallationManager.java
View the exact upstream file · Read raw source · License notices
SHA-256: e8023bd31fca7dac9461df4980bd9909d4d61cbb99b9fccdf843a395dcc1d042
1package org.multibit.hd.core.managers;23import com.google.common.base.Preconditions;4import com.google.common.io.ByteStreams;5import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;6import org.multibit.hd.core.events.ShutdownEvent;7import org.multibit.commons.files.SecureFiles;8import org.multibit.hd.core.utils.OSUtils;9import org.slf4j.Logger;10import org.slf4j.LoggerFactory;1112import java.io.File;13import java.io.FileInputStream;14import java.io.FileOutputStream;15import java.io.IOException;16import java.io.InputStream;17import java.lang.reflect.Field;18import java.net.URI;19import java.security.Permission;20import java.security.PermissionCollection;21import java.util.Map;2223/**24 * <p>Manager to provide the following to other core classes:</p>25 * <ul>26 * <li>Location of the installation directory</li>27 * <li>Access the configuration file</li>28 * <li>Utility methods eg copying checkpoint files from installation directory</li>29 * </ul>30 */31public class InstallationManager {3233 private static final Logger log = LoggerFactory.getLogger(InstallationManager.class);3435 /**36 * The main MultiBit download site (HTTPS)37 */38 public static final URI MBHD_WEBSITE_URI = URI.create("https://multibit.org");3940 /**41 * The main MultiBit help site (HTTPS to allow secure connection without redirect, with fall back to local help on failure)42 */43 public static final String MBHD_WEBSITE_HELP_DOMAIN = "https://multibit.org";44 public static final String MBHD_WEBSITE_HELP_BASE = MBHD_WEBSITE_HELP_DOMAIN + "/hd0.4";4546 public static final String MBHD_APP_NAME = "MultiBitHD";47 public static final String MBHD_PREFIX = "mbhd";48 public static final String MBHD_CONFIGURATION_FILE = MBHD_PREFIX + ".yaml";4950 public static final String SPV_BLOCKCHAIN_SUFFIX = ".spvchain";51 public static final String CHECKPOINTS_SUFFIX = ".checkpoints";52 public static final String CA_CERTS_NAME = MBHD_PREFIX + "-cacerts";5354 /**55 * The current application data directory56 */57 private static File currentApplicationDataDirectory = null;5859 /**60 * A test flag to allow FEST tests to run efficiently61 */62 // This arises from the global nature of the flag - consider deriving it from test classpath presence63 @SuppressFBWarnings({"MS_SHOULD_BE_FINAL"})64 public static boolean unrestricted = false;6566 /**67 * <p>Handle any shutdown code</p>68 *69 * @param shutdownType The shutdown type70 */71 public static void shutdownNow(ShutdownEvent.ShutdownType shutdownType) {7273 log.debug("Received shutdown: {}", shutdownType.name());7475 switch (shutdownType) {7677 case HARD:78 case SOFT:79 // Force a reset of the application directory (useful for persistence tests)80 currentApplicationDataDirectory = null;81 break;82 case SWITCH:83 // Reset of the current application directory causes problems during84 // switch and is not required in normal operation85 break;86 }8788 // Reset of the unrestricted field causes problems during FEST tests8990 }9192 /**93 * @return A reference to where the configuration file should be located94 */95 public static File getConfigurationFile() {9697 return new File(getOrCreateApplicationDataDirectory().getAbsolutePath() + File.separator + MBHD_CONFIGURATION_FILE);9899 }100101 /**102 * <p>Get the directory for the user's application data, creating if not present</p>103 * <p>Checks a few OS-dependent locations first</p>104 * <p>For tests (unrestricted mode) this will create a long-lived temporary directory - use reset() to clear in the tearDown() phase</p>105 *106 * @return A suitable application directory for the OS and if running unit tests (unrestricted mode)107 */108 public static File getOrCreateApplicationDataDirectory() {109110 if (currentApplicationDataDirectory != null) {111 return currentApplicationDataDirectory;112 }113114 if (unrestricted) {115 try {116 log.debug("Unrestricted mode requires a temporary application directory");117 // In order to preserve the same behaviour between the test and production environments118 // this must be maintained throughout the lifetime of a unit test119 // At tearDown() use reset() to clear120 currentApplicationDataDirectory = SecureFiles.createTemporaryDirectory();121 return currentApplicationDataDirectory;122 } catch (IOException e) {123 throw new IllegalStateException("Cannot run without access to temporary directory.", e);124 }125 } else {126127 // Fail safe check for unit tests to avoid overwriting existing configuration file128 try {129 Class.forName("org.multibit.hd.core.managers.InstallationManagerTest");130 throw new IllegalStateException("Cannot run without unrestricted when unit tests are present. You could overwrite live configuration.");131 } catch (ClassNotFoundException e) {132 // We have passed the fail safe check133 }134135 }136137 // Check the current working directory for the configuration file138 File multibitPropertiesFile = new File(MBHD_CONFIGURATION_FILE);139 if (multibitPropertiesFile.exists()) {140 return new File(".");141 }142143 final String applicationDataDirectoryName;144145 // Locations are OS-dependent146 if (OSUtils.isWindows()) {147148 // Windows149 applicationDataDirectoryName = System.getenv("APPDATA") + File.separator + MBHD_APP_NAME;150151 } else if (OSUtils.isMac()) {152153 // OSX154 if ((new File("../../../../" + MBHD_CONFIGURATION_FILE)).exists()) {155 applicationDataDirectoryName = new File("../../../..").getAbsolutePath();156 } else {157 applicationDataDirectoryName = System.getProperty("user.home") + "/Library/Application Support/" + MBHD_APP_NAME;158 }159 } else {160161 // Other (probably a Unix variant)162 // Keep a clean home directory by prefixing with "."163 applicationDataDirectoryName = System.getProperty("user.home") + "/." + MBHD_APP_NAME;164 }165166 log.debug("Application data directory is\n'{}'", applicationDataDirectoryName);167168 // Create the application data directory if it does not exist169 File applicationDataDirectory = new File(applicationDataDirectoryName);170 SecureFiles.verifyOrCreateDirectory(applicationDataDirectory);171172 // Must be OK to be here so set this as the current173 currentApplicationDataDirectory = applicationDataDirectory;174175 return applicationDataDirectory;176 }177178 /**179 * Copy the checkpoints file from the MultiBitHD installation to the specified filename180 *181 * @param destinationCheckpointsFile The sink to receive the source checkpoints file182 */183 public static void copyCheckpointsTo(File destinationCheckpointsFile) throws IOException {184185 Preconditions.checkNotNull(destinationCheckpointsFile, "'checkpointsFile' must be present");186187 // TODO overwrite if larger/ newer188 if (!destinationCheckpointsFile.exists() || destinationCheckpointsFile.length() == 0) {189190 log.debug("Copying checkpoints to '{}'", destinationCheckpointsFile);191192 // Work out the source checkpoints (put into the program installation directory by the installer)193 File currentWorkingDirectory = new File(".");194 File sourceBlockCheckpointsFile = new File(currentWorkingDirectory.getAbsolutePath() + File.separator + MBHD_PREFIX + CHECKPOINTS_SUFFIX);195196 // Prepare an input stream to the checkpoints197 final InputStream sourceCheckpointsStream;198 if (sourceBlockCheckpointsFile.exists()) {199 // Use the file system200 log.debug("Using source checkpoints from working directory.");201 sourceCheckpointsStream = new FileInputStream(sourceBlockCheckpointsFile);202 } else {203 // Use the classpath204 log.debug("Using source checkpoints from classpath.");205 sourceCheckpointsStream = InstallationManager.class.getResourceAsStream("/mbhd.checkpoints");206 }207208 // Create the output stream209 long bytes;210 try (FileOutputStream sinkCheckpointsStream = new FileOutputStream(destinationCheckpointsFile)) {211212 // Copy the checkpoints213 bytes = ByteStreams.copy(sourceCheckpointsStream, sinkCheckpointsStream);214215 // Clean up216 sourceCheckpointsStream.close();217 sinkCheckpointsStream.flush();218 sinkCheckpointsStream.close();219 } finally {220 if (sourceCheckpointsStream != null) {221 sourceCheckpointsStream.close();222 }223 }224225 log.debug("New checkpoints are {} bytes in length.", bytes);226227 if (bytes < 13_000) {228 log.warn("Checkpoints are short.");229 }230231 } else {232233 log.debug("Checkpoints already exist.");234235 }236 }237238 /**239 * Use for testing only (several different test packages use this)240 *241 * @param currentApplicationDataDirectory the application data directory to use242 */243 public static void setCurrentApplicationDataDirectory(File currentApplicationDataDirectory) {244 InstallationManager.currentApplicationDataDirectory = currentApplicationDataDirectory;245 }246247 /**248 * Do the following, but with reflection to bypass access checks:249 *250 * JceSecurity.isRestricted = false;251 * JceSecurity.defaultPolicy.perms.clear();252 * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);253 */254 public static void removeCryptographyRestrictions() {255256 if (!isRestrictedCryptography()) {257 log.debug("Cryptography restrictions removal not needed");258 return;259 }260261 try {262 final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");263 final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");264 final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");265266 final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");267 isRestrictedField.setAccessible(true);268 isRestrictedField.set(null, false);269270 final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");271 defaultPolicyField.setAccessible(true);272 final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);273274 final Field perms = cryptoPermissions.getDeclaredField("perms");275 perms.setAccessible(true);276 ((Map<?, ?>) perms.get(defaultPolicy)).clear();277278 final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");279 instance.setAccessible(true);280 defaultPolicy.add((Permission) instance.get(null));281282 log.debug("Successfully removed cryptography restrictions");283 } catch (final Exception e) {284 log.warn("Failed to remove cryptography restrictions", e);285 }286287 }288289 private static boolean isRestrictedCryptography() {290291 // This simply matches the Oracle JRE, but not OpenJDK292 return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));293 }294295}Read in context
Defines the application name and chooses the default application-data directory according to operating system. Read locateApplicationDataDirectory before treating a remembered path as definitive. Redirected profiles or copied disk images can require additional interpretation.
Compare the callers, dependency versions and corresponding tests. The pinned file is one part of a larger application. Do not execute write, prune or migration routines against your only copy of a wallet.