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; |
|||
|
|||
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.ImportException; |
|||
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.*; |
|||
|
|||
public class FileKeystoreImportPane extends KeystoreImportPane { |
|||
public class FileKeystoreImportPane extends FileImportPane { |
|||
protected final Wallet wallet; |
|||
private final KeystoreFileImport importer; |
|||
private Button importButton; |
|||
private final SimpleStringProperty password = new SimpleStringProperty(""); |
|||
|
|||
public FileKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) { |
|||
super(importAccordion, wallet, importer); |
|||
public FileKeystoreImportPane(Wallet wallet, KeystoreFileImport importer) { |
|||
super(importer, importer.getName(), "Keystore file import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png"); |
|||
this.wallet = wallet; |
|||
this.importer = importer; |
|||
} |
|||
|
|||
@Override |
|||
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); |
|||
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; |
|||
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException { |
|||
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password); |
|||
EventManager.get().post(new KeystoreImportEvent(keystore)); |
|||
} |
|||
} |
|||
|
@ -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; |
|||
|
|||
import com.sparrowwallet.drongo.wallet.WalletModel; |
|||
|
|||
public interface Import { |
|||
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