21 changed files with 361 additions and 237 deletions
@ -0,0 +1,121 @@ |
|||||
|
package com.sparrowwallet.sparrow.control; |
||||
|
|
||||
|
import com.google.gson.JsonParseException; |
||||
|
import com.sparrowwallet.drongo.crypto.InvalidPasswordException; |
||||
|
import com.sparrowwallet.drongo.wallet.Keystore; |
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.EventManager; |
||||
|
import com.sparrowwallet.sparrow.event.KeystoreImportEvent; |
||||
|
import com.sparrowwallet.sparrow.io.FileImport; |
||||
|
import com.sparrowwallet.sparrow.io.ImportException; |
||||
|
import com.sparrowwallet.sparrow.io.KeystoreFileImport; |
||||
|
import javafx.beans.property.SimpleStringProperty; |
||||
|
import javafx.geometry.Insets; |
||||
|
import javafx.geometry.Pos; |
||||
|
import javafx.scene.Node; |
||||
|
import javafx.scene.control.Button; |
||||
|
import javafx.scene.control.Control; |
||||
|
import javafx.scene.layout.HBox; |
||||
|
import javafx.scene.layout.Priority; |
||||
|
import javafx.stage.FileChooser; |
||||
|
import javafx.stage.Stage; |
||||
|
import org.controlsfx.control.textfield.CustomPasswordField; |
||||
|
import org.controlsfx.control.textfield.TextFields; |
||||
|
|
||||
|
import java.io.BufferedInputStream; |
||||
|
import java.io.File; |
||||
|
import java.io.FileInputStream; |
||||
|
import java.io.InputStream; |
||||
|
|
||||
|
public abstract class FileImportPane extends TitledDescriptionPane { |
||||
|
private final FileImport importer; |
||||
|
private Button importButton; |
||||
|
private final SimpleStringProperty password = new SimpleStringProperty(""); |
||||
|
|
||||
|
public FileImportPane(FileImport importer, String title, String description, String content, String imageUrl) { |
||||
|
super(title, description, content, imageUrl); |
||||
|
this.importer = importer; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Control createButton() { |
||||
|
importButton = new Button("Import File..."); |
||||
|
importButton.setAlignment(Pos.CENTER_RIGHT); |
||||
|
importButton.setOnAction(event -> { |
||||
|
importFile(); |
||||
|
}); |
||||
|
return importButton; |
||||
|
} |
||||
|
|
||||
|
private void importFile() { |
||||
|
Stage window = new Stage(); |
||||
|
|
||||
|
FileChooser fileChooser = new FileChooser(); |
||||
|
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore"); |
||||
|
fileChooser.getExtensionFilters().addAll( |
||||
|
new FileChooser.ExtensionFilter("All Files", "*.*"), |
||||
|
new FileChooser.ExtensionFilter("JSON", "*.json") |
||||
|
); |
||||
|
|
||||
|
File file = fileChooser.showOpenDialog(window); |
||||
|
if(file != null) { |
||||
|
importFile(file, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void importFile(File file, String password) { |
||||
|
if(file.exists()) { |
||||
|
try { |
||||
|
if(importer.isEncrypted(file) && password == null) { |
||||
|
setDescription("Password Required"); |
||||
|
showHideLink.setVisible(false); |
||||
|
setContent(getPasswordEntry(file)); |
||||
|
importButton.setDisable(true); |
||||
|
setExpanded(true); |
||||
|
} else { |
||||
|
InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); |
||||
|
importFile(file.getName(), inputStream, password); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
String errorMessage = e.getMessage(); |
||||
|
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) { |
||||
|
errorMessage = e.getCause().getMessage(); |
||||
|
} |
||||
|
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) { |
||||
|
errorMessage = "Invalid wallet password"; |
||||
|
} |
||||
|
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) { |
||||
|
errorMessage = "File was not in JSON format"; |
||||
|
} |
||||
|
setError("Import Error", errorMessage); |
||||
|
importButton.setDisable(false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected abstract void importFile(String fileName, InputStream inputStream, String password) throws ImportException; |
||||
|
|
||||
|
private Node getPasswordEntry(File file) { |
||||
|
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField(); |
||||
|
passwordField.setPromptText("Wallet password"); |
||||
|
password.bind(passwordField.textProperty()); |
||||
|
HBox.setHgrow(passwordField, Priority.ALWAYS); |
||||
|
|
||||
|
Button importEncryptedButton = new Button("Import"); |
||||
|
importEncryptedButton.setOnAction(event -> { |
||||
|
showHideLink.setVisible(true); |
||||
|
setExpanded(false); |
||||
|
importFile(file, password.get()); |
||||
|
}); |
||||
|
|
||||
|
HBox contentBox = new HBox(); |
||||
|
contentBox.setAlignment(Pos.TOP_RIGHT); |
||||
|
contentBox.setSpacing(20); |
||||
|
contentBox.getChildren().add(passwordField); |
||||
|
contentBox.getChildren().add(importEncryptedButton); |
||||
|
contentBox.setPadding(new Insets(10, 30, 10, 30)); |
||||
|
contentBox.setPrefHeight(60); |
||||
|
|
||||
|
return contentBox; |
||||
|
} |
||||
|
} |
@ -1,121 +1,26 @@ |
|||||
package com.sparrowwallet.sparrow.control; |
package com.sparrowwallet.sparrow.control; |
||||
|
|
||||
import com.google.gson.JsonParseException; |
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException; |
|
||||
import com.sparrowwallet.drongo.wallet.Keystore; |
import com.sparrowwallet.drongo.wallet.Keystore; |
||||
import com.sparrowwallet.drongo.wallet.Wallet; |
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
import com.sparrowwallet.sparrow.EventManager; |
import com.sparrowwallet.sparrow.EventManager; |
||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent; |
import com.sparrowwallet.sparrow.event.KeystoreImportEvent; |
||||
|
import com.sparrowwallet.sparrow.io.ImportException; |
||||
import com.sparrowwallet.sparrow.io.KeystoreFileImport; |
import com.sparrowwallet.sparrow.io.KeystoreFileImport; |
||||
import com.sparrowwallet.sparrow.io.KeystoreImport; |
|
||||
import javafx.beans.property.SimpleStringProperty; |
|
||||
import javafx.geometry.Insets; |
|
||||
import javafx.geometry.Pos; |
|
||||
import javafx.scene.Node; |
|
||||
import javafx.scene.control.Button; |
|
||||
import javafx.scene.layout.HBox; |
|
||||
import javafx.scene.layout.Priority; |
|
||||
import javafx.stage.FileChooser; |
|
||||
import javafx.stage.Stage; |
|
||||
import org.controlsfx.control.textfield.CustomPasswordField; |
|
||||
import org.controlsfx.control.textfield.TextFields; |
|
||||
|
|
||||
import java.io.*; |
import java.io.*; |
||||
|
|
||||
public class FileKeystoreImportPane extends KeystoreImportPane { |
public class FileKeystoreImportPane extends FileImportPane { |
||||
|
protected final Wallet wallet; |
||||
private final KeystoreFileImport importer; |
private final KeystoreFileImport importer; |
||||
private Button importButton; |
|
||||
private final SimpleStringProperty password = new SimpleStringProperty(""); |
|
||||
|
|
||||
public FileKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) { |
public FileKeystoreImportPane(Wallet wallet, KeystoreFileImport importer) { |
||||
super(importAccordion, wallet, importer); |
super(importer, importer.getName(), "Keystore file import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png"); |
||||
|
this.wallet = wallet; |
||||
this.importer = importer; |
this.importer = importer; |
||||
} |
} |
||||
|
|
||||
@Override |
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException { |
||||
protected Node getTitle(KeystoreImport importer) { |
|
||||
Node title = super.getTitle(importer); |
|
||||
|
|
||||
setDescription("Keystore file import"); |
|
||||
|
|
||||
importButton = new Button("Import File..."); |
|
||||
importButton.setAlignment(Pos.CENTER_RIGHT); |
|
||||
importButton.setOnAction(event -> { |
|
||||
importFile(); |
|
||||
}); |
|
||||
buttonBox.getChildren().add(importButton); |
|
||||
|
|
||||
return title; |
|
||||
} |
|
||||
|
|
||||
private void importFile() { |
|
||||
Stage window = new Stage(); |
|
||||
|
|
||||
FileChooser fileChooser = new FileChooser(); |
|
||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore"); |
|
||||
fileChooser.getExtensionFilters().addAll( |
|
||||
new FileChooser.ExtensionFilter("All Files", "*.*"), |
|
||||
new FileChooser.ExtensionFilter("JSON", "*.json") |
|
||||
); |
|
||||
|
|
||||
File file = fileChooser.showOpenDialog(window); |
|
||||
if(file != null) { |
|
||||
importFile(file, null); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void importFile(File file, String password) { |
|
||||
if(file.exists()) { |
|
||||
try { |
|
||||
if(importer.isEncrypted(file) && password == null) { |
|
||||
setDescription("Password Required"); |
|
||||
showHideLink.setVisible(false); |
|
||||
setContent(getPasswordEntry(file)); |
|
||||
importButton.setDisable(true); |
|
||||
setExpanded(true); |
|
||||
} else { |
|
||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); |
|
||||
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password); |
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password); |
||||
EventManager.get().post(new KeystoreImportEvent(keystore)); |
EventManager.get().post(new KeystoreImportEvent(keystore)); |
||||
} |
} |
||||
} catch (Exception e) { |
|
||||
String errorMessage = e.getMessage(); |
|
||||
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) { |
|
||||
errorMessage = e.getCause().getMessage(); |
|
||||
} |
|
||||
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) { |
|
||||
errorMessage = "Invalid wallet password"; |
|
||||
} |
|
||||
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) { |
|
||||
errorMessage = "File was not in JSON format"; |
|
||||
} |
|
||||
setError("Import Error", errorMessage); |
|
||||
importButton.setDisable(false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private Node getPasswordEntry(File file) { |
|
||||
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField(); |
|
||||
passwordField.setPromptText("Wallet password"); |
|
||||
password.bind(passwordField.textProperty()); |
|
||||
HBox.setHgrow(passwordField, Priority.ALWAYS); |
|
||||
|
|
||||
Button importEncryptedButton = new Button("Import"); |
|
||||
importEncryptedButton.setOnAction(event -> { |
|
||||
showHideLink.setVisible(true); |
|
||||
setExpanded(false); |
|
||||
importFile(file, password.get()); |
|
||||
}); |
|
||||
|
|
||||
HBox contentBox = new HBox(); |
|
||||
contentBox.setAlignment(Pos.TOP_RIGHT); |
|
||||
contentBox.setSpacing(20); |
|
||||
contentBox.getChildren().add(passwordField); |
|
||||
contentBox.getChildren().add(importEncryptedButton); |
|
||||
contentBox.setPadding(new Insets(10, 30, 10, 30)); |
|
||||
contentBox.setPrefHeight(60); |
|
||||
|
|
||||
return contentBox; |
|
||||
} |
|
||||
} |
} |
||||
|
@ -0,0 +1,27 @@ |
|||||
|
package com.sparrowwallet.sparrow.control; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.EventManager; |
||||
|
import com.sparrowwallet.sparrow.event.WalletImportEvent; |
||||
|
import com.sparrowwallet.sparrow.io.ImportException; |
||||
|
import com.sparrowwallet.sparrow.io.WalletImport; |
||||
|
|
||||
|
import java.io.InputStream; |
||||
|
|
||||
|
public class FileWalletImportPane extends FileImportPane { |
||||
|
private final WalletImport importer; |
||||
|
|
||||
|
public FileWalletImportPane(WalletImport importer) { |
||||
|
super(importer, importer.getName(), "Wallet file import", importer.getWalletImportDescription(), "image/" + importer.getWalletModel().getType() + ".png"); |
||||
|
this.importer = importer; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException { |
||||
|
Wallet wallet = importer.importWallet(inputStream, password); |
||||
|
if(wallet.getName() == null) { |
||||
|
wallet.setName(fileName); |
||||
|
} |
||||
|
EventManager.get().post(new WalletImportEvent(wallet)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
package com.sparrowwallet.sparrow.control; |
||||
|
|
||||
|
import com.google.common.eventbus.Subscribe; |
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.EventManager; |
||||
|
import com.sparrowwallet.sparrow.event.WalletImportEvent; |
||||
|
import com.sparrowwallet.sparrow.io.*; |
||||
|
import javafx.scene.control.*; |
||||
|
import javafx.scene.layout.AnchorPane; |
||||
|
import javafx.scene.layout.StackPane; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public class WalletImportDialog extends Dialog<Wallet> { |
||||
|
private Wallet wallet; |
||||
|
|
||||
|
public WalletImportDialog() { |
||||
|
EventManager.get().register(this); |
||||
|
|
||||
|
final DialogPane dialogPane = getDialogPane(); |
||||
|
|
||||
|
StackPane stackPane = new StackPane(); |
||||
|
dialogPane.setContent(stackPane); |
||||
|
|
||||
|
AnchorPane anchorPane = new AnchorPane(); |
||||
|
stackPane.getChildren().add(anchorPane); |
||||
|
|
||||
|
ScrollPane scrollPane = new ScrollPane(); |
||||
|
scrollPane.setPrefWidth(500); |
||||
|
scrollPane.setPrefHeight(500); |
||||
|
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); |
||||
|
anchorPane.getChildren().add(scrollPane); |
||||
|
scrollPane.setFitToWidth(true); |
||||
|
AnchorPane.setLeftAnchor(scrollPane, 0.0); |
||||
|
AnchorPane.setRightAnchor(scrollPane, 0.0); |
||||
|
|
||||
|
List<WalletImport> importers = List.of(new ColdcardMultisig(), new Electrum()); |
||||
|
Accordion importAccordion = new Accordion(); |
||||
|
for(WalletImport importer : importers) { |
||||
|
FileWalletImportPane importPane = new FileWalletImportPane(importer); |
||||
|
importAccordion.getPanes().add(importPane); |
||||
|
} |
||||
|
scrollPane.setContent(importAccordion); |
||||
|
|
||||
|
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); |
||||
|
dialogPane.getButtonTypes().addAll(cancelButtonType); |
||||
|
dialogPane.setPrefWidth(650); |
||||
|
dialogPane.setPrefHeight(600); |
||||
|
|
||||
|
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null); |
||||
|
} |
||||
|
|
||||
|
@Subscribe |
||||
|
public void walletImported(WalletImportEvent event) { |
||||
|
wallet = event.getWallet(); |
||||
|
setResult(wallet); |
||||
|
this.close(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package com.sparrowwallet.sparrow.event; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
|
||||
|
public class WalletImportEvent { |
||||
|
private Wallet wallet; |
||||
|
|
||||
|
public WalletImportEvent(Wallet wallet) { |
||||
|
this.wallet = wallet; |
||||
|
} |
||||
|
|
||||
|
public Wallet getWallet() { |
||||
|
return wallet; |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
package com.sparrowwallet.sparrow.io; |
||||
|
|
||||
|
import java.io.File; |
||||
|
|
||||
|
public interface FileImport extends Import { |
||||
|
boolean isEncrypted(File file); |
||||
|
} |
@ -1,5 +1,8 @@ |
|||||
package com.sparrowwallet.sparrow.io; |
package com.sparrowwallet.sparrow.io; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.wallet.WalletModel; |
||||
|
|
||||
public interface Import { |
public interface Import { |
||||
String getName(); |
String getName(); |
||||
|
WalletModel getWalletModel(); |
||||
} |
} |
||||
|
@ -1,13 +0,0 @@ |
|||||
package com.sparrowwallet.sparrow.io; |
|
||||
|
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType; |
|
||||
import com.sparrowwallet.drongo.wallet.Wallet; |
|
||||
|
|
||||
import java.io.File; |
|
||||
import java.io.InputStream; |
|
||||
|
|
||||
public interface SinglesigWalletImport extends Import { |
|
||||
String getWalletImportDescription(); |
|
||||
Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException; |
|
||||
boolean isEncrypted(File file); |
|
||||
} |
|
Loading…
Reference in new issue