/**
* Copyright 2011 multibit.org
*
* Licensed under the MIT license (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.multibit;
import com.google.bitcoin.core.StoredBlock;
import com.google.bitcoin.core.Wallet;
import org.multibit.controller.Controller;
import org.multibit.controller.bitcoin.BitcoinController;
import org.multibit.controller.core.CoreController;
import org.multibit.controller.exchange.ExchangeController;
import org.multibit.exchange.CurrencyConverter;
import org.multibit.file.BackupManager;
import org.multibit.file.FileHandler;
import org.multibit.message.Message;
import org.multibit.message.MessageManager;
import org.multibit.model.bitcoin.BitcoinModel;
import org.multibit.model.bitcoin.WalletData;
import org.multibit.model.bitcoin.WalletInfoData;
import org.multibit.model.core.CoreModel;
import org.multibit.model.exchange.ConnectHttps;
import org.multibit.model.exchange.ExchangeModel;
import org.multibit.network.*;
import org.multibit.platform.GenericApplication;
import org.multibit.platform.GenericApplicationFactory;
import org.multibit.platform.GenericApplicationSpecification;
import org.multibit.platform.listener.GenericOpenURIEvent;
import org.multibit.utils.OSUtils;
import org.multibit.viewsystem.DisplayHint;
import org.multibit.viewsystem.ViewSystem;
import org.multibit.viewsystem.swing.ColorAndFontConstants;
import org.multibit.viewsystem.swing.MultiBitFrame;
import org.multibit.viewsystem.swing.action.ExitAction;
import org.multibit.viewsystem.swing.view.components.FontSizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import java.awt.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.*;
import java.util.List;
/**
* Main MultiBit entry class.
*
* @author jim
*/
public final class MultiBit {
private static final Logger log = LoggerFactory.getLogger(MultiBit.class);
private static Controller controller = null;
private static CoreController coreController = null;
private static BitcoinController bitcoinController = null;
private static ExchangeController exchangeController = null;
private static String rememberedRawBitcoinURI;
/**
* Utility class should not have a public constructor
*/
private MultiBit() {
}
/**
* Start MultiBit user interface.
*
* @param args String encoding of arguments ([0]= Bitcoin URI)
*/
@SuppressWarnings("deprecation")
public static void main(String args[]) {
log.info("Starting MultiBit at " + (new Date()).toGMTString());
// Print out all the system properties.
for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
log.debug(String.format("%s = %s", e.getKey(), e.getValue()));
}
ViewSystem swingViewSystem = null;
// Enclosing try to enable graceful closure for unexpected errors.
try {
// Set any bespoke system properties.
try {
// Fix for Windows / Java 7 / VPN bug.
System.setProperty("java.net.preferIPv4Stack", "true");
// Fix for version.txt not visible for Java 7
System.setProperty ("jsse.enableSNIExtension", "false");
} catch (SecurityException se) {
log.error(se.getClass().getName() + " " + se.getMessage());
}
ApplicationDataDirectoryLocator applicationDataDirectoryLocator = new ApplicationDataDirectoryLocator();
// Load up the user preferences.
Properties userPreferences = FileHandler.loadUserPreferences(applicationDataDirectoryLocator);
// Create the controllers.
coreController = new CoreController(applicationDataDirectoryLocator);
controller = coreController;
bitcoinController = new BitcoinController(coreController);
exchangeController = new ExchangeController(coreController);
log.info("Configuring native event handling");
GenericApplicationSpecification specification = new GenericApplicationSpecification();
specification.getOpenURIEventListeners().add(coreController);
specification.getPreferencesEventListeners().add(coreController);
specification.getAboutEventListeners().add(coreController);
specification.getQuitEventListeners().add(coreController);
GenericApplication genericApplication = GenericApplicationFactory.INSTANCE.buildGenericApplication(specification);
log.info("Checking to see if this is the primary MultiBit instance");
String rawURI = null;
if (args != null && args.length > 0) {
rawURI = args[0];
log.debug("The args[0] passed into MultiBit = '" + args[0] +"'");
}
if (!ApplicationInstanceManager.registerInstance(rawURI)) {
// Instance already running.
log.debug("Another instance of MultiBit is already running. Exiting.");
System.exit(0);
}
final BitcoinController finalController = bitcoinController;
ApplicationInstanceManager.setApplicationInstanceListener(new ApplicationInstanceListener() {
@Override
public void newInstanceCreated(String rawURI) {
final String finalRawUri = rawURI;
log.debug("New instance of MultiBit detected, rawURI = " + rawURI + " ...");
Runnable doProcessCommandLine = new Runnable() {
@Override
public void run() {
processCommandLineURI(finalController, finalRawUri);
}
};
SwingUtilities.invokeLater(doProcessCommandLine);
}
});
Localiser localiser;
String userLanguageCode = userPreferences.getProperty(CoreModel.USER_LANGUAGE_CODE);
log.debug("userLanguageCode = {}", userLanguageCode);
if (userLanguageCode == null) {
// Initial install - no language info supplied - see if we can
// use the user default, else Localiser will set it to English.
localiser = new Localiser(Locale.getDefault());
userPreferences.setProperty(CoreModel.USER_LANGUAGE_CODE, localiser.getLocale().getLanguage());
} else {
if (CoreModel.USER_LANGUAGE_IS_DEFAULT.equals(userLanguageCode)) {
localiser = new Localiser(Locale.getDefault());
} else {
localiser = new Localiser(new Locale(userLanguageCode));
}
}
coreController.setLocaliser(localiser);
log.debug("MultiBit version = " + localiser.getVersionNumber());
log.debug("Creating model");
// Create the model.
// The model is set to the controller.
final CoreModel coreModel = new CoreModel(userPreferences);
final BitcoinModel model = new BitcoinModel(coreModel);
final ExchangeModel exchangeModel = new ExchangeModel(coreModel);
coreController.setModel(coreModel);
bitcoinController.setModel(model);
exchangeController.setModel(exchangeModel);
// Trust all HTTPS certificates.
ConnectHttps.trustAllCerts();
// Initialise currency converter.
CurrencyConverter.INSTANCE.initialise(finalController);
// Initialise replay manager.
ReplayManager.INSTANCE.initialise(bitcoinController, false);
log.debug("Setting look and feel");
try {
String lookAndFeel = userPreferences.getProperty(CoreModel.LOOK_AND_FEEL);
// If not set on Windows use 'Windows' L&F as system can be rendered as metal.
if ((lookAndFeel == null || lookAndFeel.equals("")) && System.getProperty("os.name").startsWith("Win")) {
lookAndFeel = "Windows";
userPreferences.setProperty(CoreModel.LOOK_AND_FEEL, lookAndFeel);
}
if (lookAndFeel != null && !lookAndFeel.equals("")) {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if (lookAndFeel.equalsIgnoreCase(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} else {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
} catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
// Carry on.
}
// Initialise singletons.
ColorAndFontConstants.init();
FontSizer.INSTANCE.initialise(controller);
CurrencyConverter.INSTANCE.initialise(finalController);
// This is when the GUI is first displayed to the user.
log.debug("Creating user interface with initial view : " + controller.getCurrentView());
swingViewSystem = new MultiBitFrame(coreController, bitcoinController, exchangeController, genericApplication, controller.getCurrentView());
log.debug("Registering with controller");
coreController.registerViewSystem(swingViewSystem);
String userDataString = localiser.getString("multibit.userDataDirectory", new String[] {applicationDataDirectoryLocator.getApplicationDataDirectory()});
log.debug(userDataString);
Message directoryMessage1 = new Message(userDataString);
directoryMessage1.setShowInStatusBar(false);
MessageManager.INSTANCE.addMessage(directoryMessage1);
String installationDirString = localiser.getString("multibit.installationDirectory", new String[]{applicationDataDirectoryLocator.getInstallationDirectory()});
log.debug(installationDirString);
Message directoryMessage2 = new Message(installationDirString);
directoryMessage2.setShowInStatusBar(false);
MessageManager.INSTANCE.addMessage(directoryMessage2);
log.debug("Creating Bitcoin service");
// Create the MultiBitService that connects to the bitcoin network.
MultiBitService multiBitService = new MultiBitService(bitcoinController);
bitcoinController.setMultiBitService(multiBitService);
log.debug("Locating wallets");
// Find the active wallet filename in the multibit.properties.
String activeWalletFilename = userPreferences.getProperty(BitcoinModel.ACTIVE_WALLET_FILENAME);
// Get the number of the early wallets - these are serialised and protobuf2
String numberOfEarlyWalletsAsString = userPreferences.getProperty(BitcoinModel.NUMBER_OF_EARLY_WALLETS);
log.debug("When loading early wallets, there were " + numberOfEarlyWalletsAsString);
// Get the number of the protobuf3 wallets
String numberOfProtobuf3WalletsAsString = userPreferences.getProperty(BitcoinModel.NUMBER_OF_PROTOBUF3_WALLETS);
log.debug("When loading protobuf3 wallets, there were " + numberOfProtobuf3WalletsAsString);
boolean useFastCatchup = false;
if (numberOfEarlyWalletsAsString == null || "".equals(numberOfEarlyWalletsAsString) || "null".equals(numberOfEarlyWalletsAsString)) {
// If this is missing then there is just the one wallet (old format
// properties or user has just started up for the first time).
useFastCatchup = true;
boolean thereWasAnErrorLoadingTheWallet = false;
try {
boolean backupWallet;
boolean moveSiblingFiles = false;
// If there is no active filename this is a new instance of MultiBit so backup the new wallet when created.
if (activeWalletFilename == null || "".equals(activeWalletFilename) || "null".equals(activeWalletFilename) ) {
backupWallet = true;
} else {
// See if a data directory is missing - if so we will move in any wallet or key files and backup.
String topLevelWalletDirectory = BackupManager.INSTANCE.calculateTopLevelBackupDirectoryName(new File(activeWalletFilename));
moveSiblingFiles = !(new File(topLevelWalletDirectory).exists());
backupWallet = moveSiblingFiles;
}
// ActiveWalletFilename may be null on first time startup.
bitcoinController.addWalletFromFilename(activeWalletFilename);
List<WalletData> perWalletModelDataList = bitcoinController.getModel().getPerWalletModelDataList();
if (perWalletModelDataList != null && !perWalletModelDataList.isEmpty()) {
activeWalletFilename = perWalletModelDataList.get(0).getWalletFilename();
bitcoinController.getModel().setActiveWalletByFilename(activeWalletFilename);
log.debug("Created/loaded wallet '" + activeWalletFilename + "'");
MessageManager.INSTANCE.addMessage(new Message(controller.getLocaliser().getString(
"multiBit.createdWallet", new Object[] { activeWalletFilename })));
// Clean out the "1Enjoy 1Sochi" spam
WalletInfoData walletInfo = perWalletModelDataList.get(0).getWalletInfo();
String walletCleanedOfSpam = walletInfo.getProperty(BitcoinModel.WALLET_CLEANED_OF_SPAM);
if (!Boolean.TRUE.toString().equalsIgnoreCase(walletCleanedOfSpam)) {
log.debug("Cleaning wallet '" + activeWalletFilename + "' of spam ...");
perWalletModelDataList.get(0).getWallet().cleanup();
walletInfo.put(BitcoinModel.WALLET_CLEANED_OF_SPAM, Boolean.TRUE.toString());
bitcoinController.getFileHandler().savePerWalletModelData(perWalletModelDataList.get(0), false);
log.debug("done.");
}
if (backupWallet) {
// Backup the wallet and wallet info.
BackupManager.INSTANCE.backupPerWalletModelData(bitcoinController.getFileHandler(), perWalletModelDataList.get(0));
}
if (moveSiblingFiles) {
// Move any timestamped key and wallet files into their appropriate directories
BackupManager.INSTANCE.moveSiblingTimestampedKeyAndWalletBackups(activeWalletFilename);
}
}
} catch (Exception e) {
String message = controller.getLocaliser().getString("openWalletSubmitAction.walletNotLoaded",
new Object[] { activeWalletFilename, e.getMessage() });
MessageManager.INSTANCE.addMessage(new Message(message));
log.error(message);
thereWasAnErrorLoadingTheWallet = true;
} finally {
if (thereWasAnErrorLoadingTheWallet) {
// Clear the backup wallet filename - this prevents it being automatically overwritten.
if (bitcoinController.getModel().getActiveWalletWalletInfo() != null) {
bitcoinController.getModel().getActiveWalletWalletInfo().put(BitcoinModel.WALLET_BACKUP_FILE, "");
}
}
if (swingViewSystem instanceof MultiBitFrame) {
((MultiBitFrame) swingViewSystem).getWalletsView().initUI();
((MultiBitFrame) swingViewSystem).getWalletsView().displayView(DisplayHint.COMPLETE_REDRAW);
}
controller.fireDataChangedUpdateNow();
}
} else {
try {
List<String> walletFilenamesToLoad = new ArrayList<String>();
try {
int numberOfEarlyWallets = Integer.parseInt(numberOfEarlyWalletsAsString);
if (numberOfEarlyWallets > 0) {
for (int i = 1; i <= numberOfEarlyWallets; i++) {
// Look up ith wallet filename.
String loopWalletFilename = userPreferences.getProperty(BitcoinModel.EARLY_WALLET_FILENAME_PREFIX + i);
if (!walletFilenamesToLoad.contains(loopWalletFilename)) {
walletFilenamesToLoad.add(loopWalletFilename);
}
}
}
} catch (NumberFormatException nfe) {
// Carry on.
}
try {
int numberOfProtobuf3Wallets = Integer.parseInt(numberOfProtobuf3WalletsAsString);
if (numberOfProtobuf3Wallets > 0) {
for (int i = 1; i <= numberOfProtobuf3Wallets; i++) {
// Look up ith wallet filename.
String loopWalletFilename = userPreferences.getProperty(BitcoinModel.PROTOBUF3_WALLET_FILENAME_PREFIX + i);
if (!walletFilenamesToLoad.contains(loopWalletFilename)) {
walletFilenamesToLoad.add(loopWalletFilename);
}
}
}
} catch (NumberFormatException nfe) {
// Carry on.
}
// Load up the order the wallets are to appear in.
// There may be wallets in this list of types from the future but only load wallets we know about
boolean haveWalletOrder = false;
List<String> walletFilenameOrder = new ArrayList<String>();
try {
String walletOrderTotalAsString = userPreferences.getProperty(BitcoinModel.WALLET_ORDER_TOTAL);
log.debug("When loading the wallet orders, there were " + walletOrderTotalAsString);
int walletOrderTotal = Integer.parseInt(walletOrderTotalAsString);
if (walletOrderTotal > 0) {
haveWalletOrder = true;
for (int i = 1; i <= walletOrderTotal; i++) {
// Add the wallet filename order.
String loopWalletFilename = userPreferences.getProperty(BitcoinModel.WALLET_ORDER_PREFIX + i);
if (!walletFilenameOrder.contains(loopWalletFilename)) {
walletFilenameOrder.add(loopWalletFilename);
}
}
}
} catch (NumberFormatException nfe) {
// Carry on.
}
List<String> actualOrderToLoad = new ArrayList<String>();
if (haveWalletOrder) {
for (String orderWallet : walletFilenameOrder) {
if (walletFilenamesToLoad.contains(orderWallet)) {
// Add it.
actualOrderToLoad.add(orderWallet);
}
}
// There may be some extras so add them to the end.
for (String loadWallet : walletFilenamesToLoad) {
if (!walletFilenameOrder.contains(loadWallet)) {
// Add it.
actualOrderToLoad.add(loadWallet);
}
}
} else {
// Just load all the wallets, early then later.
for (String loadWallet : walletFilenamesToLoad) {
if (!actualOrderToLoad.contains(loadWallet)) {
// Add it.
actualOrderToLoad.add(loadWallet);
}
}
}
if (actualOrderToLoad.size() > 0) {
boolean thereWasAnErrorLoadingTheWallet = false;
((MultiBitFrame) swingViewSystem).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
for (String actualOrder : actualOrderToLoad) {
log.debug("Loading wallet from '{}'", actualOrder);
Message message = new Message(controller.getLocaliser().getString("multiBit.openingWallet",
new Object[] { actualOrder }));
message.setShowInStatusBar(false);
MessageManager.INSTANCE.addMessage(message);
try {
// Check if this is the first time this wallet has been opened post addition of data directories.
String topLevelWalletDirectory = BackupManager.INSTANCE.calculateTopLevelBackupDirectoryName(new File(actualOrder));
boolean firstUsageSinceWalletDirectoriesIntroduced = !(new File(topLevelWalletDirectory).exists());
WalletData perWalletModelData;
if (activeWalletFilename != null && activeWalletFilename.equals(actualOrder)) {
perWalletModelData = bitcoinController.addWalletFromFilename(actualOrder);
bitcoinController.getModel().setActiveWalletByFilename(actualOrder);
} else {
perWalletModelData = bitcoinController.addWalletFromFilename(actualOrder);
}
Message message2 = new Message(controller.getLocaliser().getString("multiBit.openingWalletIsDone",
new Object[] { actualOrder }));
message2.setShowInStatusBar(false);
MessageManager.INSTANCE.addMessage(message2);
// Clean out the "1Enjoy 1Sochi" spam
WalletInfoData walletInfo = perWalletModelData.getWalletInfo();
String walletCleanedOfSpam = walletInfo.getProperty(BitcoinModel.WALLET_CLEANED_OF_SPAM);
if (!Boolean.TRUE.toString().equalsIgnoreCase(walletCleanedOfSpam)) {
log.debug("Cleaning wallet '" + activeWalletFilename + "' of spam ...");
perWalletModelData.getWallet().cleanup();
walletInfo.put(BitcoinModel.WALLET_CLEANED_OF_SPAM, Boolean.TRUE.toString());
bitcoinController.getFileHandler().savePerWalletModelData(perWalletModelData, false);
log.debug("done.");
}
if (firstUsageSinceWalletDirectoriesIntroduced) {
if (perWalletModelData != null && perWalletModelData.getWallet() != null) {
// Backup the wallet and wallet info.
BackupManager.INSTANCE.backupPerWalletModelData(bitcoinController.getFileHandler(), perWalletModelData);
// Move any timestamped key and wallet files into their appropriate directories
BackupManager.INSTANCE.moveSiblingTimestampedKeyAndWalletBackups(actualOrder);
}
}
} catch (Exception e) {
message = new Message(controller.getLocaliser().getString("openWalletSubmitAction.walletNotLoaded",
new Object[] { actualOrder, e.getMessage() }));
MessageManager.INSTANCE.addMessage(message);
log.error(message.getText());
thereWasAnErrorLoadingTheWallet = true;
}
if (thereWasAnErrorLoadingTheWallet) {
WalletData loopData = bitcoinController.getModel().getPerWalletModelDataByWalletFilename(actualOrder);
if (loopData != null) {
// Clear the backup wallet filename - this prevents it being automatically overwritten.
if (loopData.getWalletInfo() != null) {
loopData.getWalletInfo().put(BitcoinModel.WALLET_BACKUP_FILE, "");
}
}
}
}
}
} catch (NumberFormatException nfe) {
// Carry on.
} finally {
if (swingViewSystem instanceof MultiBitFrame) {
((MultiBitFrame) swingViewSystem).getWalletsView().initUI();
((MultiBitFrame) swingViewSystem).getWalletsView().displayView(DisplayHint.COMPLETE_REDRAW);
}
controller.fireDataChangedUpdateNow();
((MultiBitFrame) swingViewSystem).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
log.debug("Checking for Bitcoin URI on command line");
// Check for a valid entry on the command line (protocol handler).
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
log.debug("Started with args[{}]: '{}'", i, args[i]);
}
processCommandLineURI(bitcoinController, args[0]);
} else {
log.debug("No Bitcoin URI provided as an argument");
}
// Indicate to the application that startup has completed.
coreController.setApplicationStarting(false);
// Check for any pending URI operations.
bitcoinController.handleOpenURI(rememberedRawBitcoinURI);
log.debug("Downloading blockchain");
if (useFastCatchup) {
long earliestTimeSecs = bitcoinController.getModel().getActiveWallet().getEarliestKeyCreationTime();
bitcoinController.getMultiBitService().getPeerGroup().setFastCatchupTimeSecs(earliestTimeSecs);
log.debug("Using FastCatchup for blockchain sync with time of " + (new Date(earliestTimeSecs)).toString());
}
// Work out the late date/ block the wallets saw to see if it needs syncing
// or if we can use regular downloading.
int currentChainHeight = -1;
if (bitcoinController.getMultiBitService().getChain() != null) {
if (bitcoinController.getMultiBitService().getChain().getChainHead() != null) {
currentChainHeight = bitcoinController.getMultiBitService().getChain().getChainHead().getHeight();
}
}
log.debug("The current chain height is " + currentChainHeight);
List<WalletData> perWalletModelDataList = bitcoinController.getModel().getPerWalletModelDataList();
boolean needToSync = false;
int syncFromHeight = -1;
List<WalletData> replayPerWalletModelList = new ArrayList<WalletData>();
if (perWalletModelDataList != null) {
for (WalletData perWalletModelData : perWalletModelDataList) {
Wallet wallet = perWalletModelData.getWallet();
if (wallet != null) {
int lastBlockSeenHeight = wallet.getLastBlockSeenHeight();
log.debug("For wallet '" + perWalletModelData.getWalletFilename() + " the lastBlockSeenHeight was "
+ lastBlockSeenHeight);
// Check if we have both the lastBlockSeenHeight and the currentChainHeight.
if (lastBlockSeenHeight > 0 && currentChainHeight > 0) {
if (lastBlockSeenHeight >= currentChainHeight) {
// Wallet is at or ahead of current chain - no
// need to sync for this wallet.
} else {
// Wallet is behind the current chain - need to sync.
needToSync = true;
replayPerWalletModelList.add(perWalletModelData);
if (syncFromHeight == -1) {
syncFromHeight = lastBlockSeenHeight;
} else {
syncFromHeight = Math.min(syncFromHeight, lastBlockSeenHeight);
}
}
}
}
}
}
log.debug("needToSync = " + needToSync);
if (needToSync) {
StoredBlock syncFromStoredBlock = null;
MultiBitCheckpointManager checkpointManager = bitcoinController.getMultiBitService().getCheckpointManager();
if (checkpointManager != null) {
syncFromStoredBlock = checkpointManager.getCheckpointBeforeOrAtHeight(syncFromHeight);
}
ReplayTask replayTask;
if (syncFromStoredBlock == null) {
// Sync from genesis block.
replayTask = new ReplayTask(replayPerWalletModelList, null, 0);
} else {
Date syncDate = null;
if (syncFromStoredBlock.getHeader() != null) {
syncDate = new Date(syncFromStoredBlock.getHeader().getTimeSeconds() * 1000);
}
replayTask = new ReplayTask(replayPerWalletModelList, syncDate, syncFromStoredBlock.getHeight());
}
ReplayManager.INSTANCE.offerReplayTask(replayTask);
} else {
// Just sync the blockchain without a replay task being involved.
ReplayManager.INSTANCE.downloadBlockChain();
}
if (OSUtils.isWindowsXPOrEarlier()) {
log.error("Windows XP or earlier detected. Issuing warning.");
JOptionPane.showMessageDialog(
null, "This version of Windows is not recommended for security reasons.\nPlease upgrade.", "Error",
JOptionPane.ERROR_MESSAGE);
}
} catch (Exception e) {
// An odd unrecoverable error occurred.
log.error("An unexpected error caused MultiBit to quit.", e);
// Try saving any dirty wallets.
if (controller != null) {
ExitAction exitAction = new ExitAction(controller, (MultiBitFrame)swingViewSystem);
exitAction.actionPerformed(null);
}
}
}
static void processCommandLineURI(BitcoinController controller, String rawURI) {
try {
// Attempt to detect if the command line URI is valid.
// Note that this is largely because IE6-8 strip URL encoding
// when passing in URIs to a protocol handler.
// However, there is also the chance that anyone could
// hand-craft a URI and pass
// it in with non-ASCII character encoding present in the label
// This a really limited approach (no consideration of
// "amount=10.0&label=Black & White")
// but should be OK for early use cases.
int queryParamIndex = rawURI.indexOf('?');
if (queryParamIndex > 0 && !rawURI.contains("%")) {
// Possibly encoded but more likely not
String encodedQueryParams = URLEncoder.encode(rawURI.substring(queryParamIndex + 1), "UTF-8");
rawURI = rawURI.substring(0, queryParamIndex) + "?" + encodedQueryParams;
rawURI = rawURI.replaceAll("%3D", "=");
rawURI = rawURI.replaceAll("%26", "&");
}
final URI uri;
log.debug("Working with '{}' as a Bitcoin URI", rawURI);
// Construct an OpenURIEvent to simulate receiving this from a
// listener
uri = new URI(rawURI);
GenericOpenURIEvent event = new GenericOpenURIEvent() {
@Override
public URI getURI() {
return uri;
}
};
controller.displayView(controller.getCurrentView());
// Call the event which will attempt validation against the
// Bitcoin URI specification.
coreController.onOpenURIEvent(event);
} catch (URISyntaxException e) {
log.error("URI is malformed. Received: '{}'", rawURI);
} catch (UnsupportedEncodingException e) {
log.error("UTF=8 is not supported on this platform");
}
}
public static Controller getController() {
return controller;
}
public static CoreController getCoreController() {
return coreController;
}
public static BitcoinController getBitcoinController() {
return bitcoinController;
}
public static ExchangeController getExchangeController() {
return exchangeController;
}
/**
* Used in testing
*/
public static void setCoreController(CoreController coreController ) {
MultiBit.controller = coreController;
MultiBit.coreController = coreController;
}
public static void setBitcoinController(BitcoinController bitcoinController) {
MultiBit.bitcoinController = bitcoinController;
}
public static void setExchangeController(ExchangeController exchangeController) {
MultiBit.exchangeController = exchangeController;
}
public static String getRememberedRawBitcoinURI() {
return rememberedRawBitcoinURI;
}
public static void setRememberedRawBitcoinURI(String rememberedRawBitcoinURI) {
log.debug("Remembering the bitcoin URI to process of '" + rememberedRawBitcoinURI + "'");
MultiBit.rememberedRawBitcoinURI = rememberedRawBitcoinURI;
}
}