diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletPasswordDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletPasswordDialog.java index d84cc03f..d0bdbd7c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletPasswordDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletPasswordDialog.java @@ -22,17 +22,24 @@ public class WalletPasswordDialog extends Dialog { private final CustomPasswordField passwordConfirm; private final CheckBox backupExisting; private final CheckBox changePassword; + private final CheckBox deleteBackups; + private boolean addingPassword; public WalletPasswordDialog(PasswordRequirement requirement) { this(null, requirement); } public WalletPasswordDialog(String walletName, PasswordRequirement requirement) { + this(null, requirement, false); + } + + public WalletPasswordDialog(String walletName, PasswordRequirement requirement, boolean suggestChangePassword) { this.requirement = requirement; this.password = (CustomPasswordField)TextFields.createClearablePasswordField(); this.passwordConfirm = (CustomPasswordField)TextFields.createClearablePasswordField(); this.backupExisting = new CheckBox("Backup existing wallet first"); this.changePassword = new CheckBox("Change password"); + this.deleteBackups = new CheckBox("Delete any backups"); final DialogPane dialogPane = getDialogPane(); setTitle("Wallet Password" + (walletName != null ? " - " + walletName : "")); @@ -52,13 +59,21 @@ public class WalletPasswordDialog extends Dialog { content.getChildren().add(password); content.getChildren().add(passwordConfirm); - if(requirement == PasswordRequirement.UPDATE_EMPTY || requirement == PasswordRequirement.UPDATE_SET) { - content.getChildren().add(backupExisting); - backupExisting.setSelected(true); - } - if(requirement == PasswordRequirement.UPDATE_SET) { content.getChildren().add(changePassword); + changePassword.selectedProperty().addListener((observable, oldValue, newValue) -> { + backupExisting.setVisible(!newValue); + }); + changePassword.setSelected(suggestChangePassword); + } + + if(requirement == PasswordRequirement.UPDATE_EMPTY || requirement == PasswordRequirement.UPDATE_SET) { + backupExisting.managedProperty().bind(backupExisting.visibleProperty()); + deleteBackups.managedProperty().bind(deleteBackups.visibleProperty()); + deleteBackups.visibleProperty().bind(backupExisting.visibleProperty().not()); + content.getChildren().addAll(backupExisting, deleteBackups); + backupExisting.setSelected(true); + deleteBackups.setSelected(true); } dialogPane.setContent(content); @@ -87,10 +102,14 @@ public class WalletPasswordDialog extends Dialog { okButton.setText("No Password"); passwordConfirm.setVisible(false); passwordConfirm.setManaged(false); + backupExisting.setVisible(true); + addingPassword = false; } else { okButton.setText("Set Password"); passwordConfirm.setVisible(true); passwordConfirm.setManaged(true); + backupExisting.setVisible(false); + addingPassword = true; } }); } @@ -103,13 +122,17 @@ public class WalletPasswordDialog extends Dialog { } public boolean isBackupExisting() { - return backupExisting.isSelected(); + return !(addingPassword || isChangePassword()) && backupExisting.isSelected(); } public boolean isChangePassword() { return changePassword.isSelected(); } + public boolean isDeleteBackups() { + return (addingPassword || isChangePassword()) && deleteBackups.isSelected(); + } + public enum PasswordRequirement { LOAD("Please enter the wallet password:", "Unlock"), UPDATE_NEW("Add a password to the wallet?\nLeave empty for none:", "No Password"), diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index 9c1e7b69..6e7e084f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -184,6 +184,18 @@ public class Storage { Files.copy(walletFile, backupFile); } + public void deleteBackups() { + File backupDir = getWalletsBackupDir(); + File[] unencryptedBackups = backupDir.listFiles((dir, name) -> { + int dotIndex = name.lastIndexOf('.'); + return name.startsWith(walletFile.getName() + "-") && name.substring(walletFile.getName().length() + 1, dotIndex > -1 ? dotIndex : name.length()).matches("[0-9]+"); + }); + + for(File unencryptedBackup : unencryptedBackups) { + unencryptedBackup.delete(); + } + } + public ECKey getEncryptionPubKey() { return encryptionPubKey; } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/AdvancedDialog.java b/src/main/java/com/sparrowwallet/sparrow/wallet/AdvancedDialog.java index 32c8953f..6befe0bb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/AdvancedDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/AdvancedDialog.java @@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.wallet; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppServices; +import com.sparrowwallet.sparrow.io.Storage; import javafx.fxml.FXMLLoader; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; @@ -11,10 +12,11 @@ import org.controlsfx.tools.Borders; import java.io.IOException; -public class AdvancedDialog extends Dialog { - public AdvancedDialog(Wallet wallet) { +public class AdvancedDialog extends Dialog { + public AdvancedDialog(WalletForm walletForm) { final DialogPane dialogPane = getDialogPane(); AppServices.setStageIcon(dialogPane.getScene().getWindow()); + Wallet wallet = walletForm.getWallet(); try { FXMLLoader advancedLoader = new FXMLLoader(AppServices.class.getResource("wallet/advanced.fxml")); @@ -22,11 +24,18 @@ public class AdvancedDialog extends Dialog { AdvancedController settingsAdvancedController = advancedLoader.getController(); settingsAdvancedController.initializeView(wallet); + boolean noPassword = Storage.NO_PASSWORD_KEY.equals(walletForm.getStorage().getEncryptionPubKey()); final ButtonType closeButtonType = new javafx.scene.control.ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE); - dialogPane.getButtonTypes().addAll(closeButtonType); + final ButtonType passwordButtonType = new javafx.scene.control.ButtonType(noPassword ? "Add Password..." : "Change Password...", ButtonBar.ButtonData.LEFT); + dialogPane.getButtonTypes().add(closeButtonType); + if(wallet.isValid()) { + dialogPane.getButtonTypes().add(passwordButtonType); + } dialogPane.setPrefWidth(400); dialogPane.setPrefHeight(300); + + setResultConverter(dialogButton -> dialogButton == passwordButtonType); } catch(IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index d0220be9..c5b4db07 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -183,7 +183,7 @@ public class SettingsController extends WalletFormController implements Initiali apply.setOnAction(event -> { revert.setDisable(true); apply.setDisable(true); - saveWallet(false); + saveWallet(false, false); }); setFieldsFromWallet(walletForm.getWallet()); @@ -303,8 +303,13 @@ public class SettingsController extends WalletFormController implements Initiali } public void showAdvanced(ActionEvent event) { - AdvancedDialog advancedDialog = new AdvancedDialog(walletForm.getWallet()); - advancedDialog.showAndWait(); + AdvancedDialog advancedDialog = new AdvancedDialog(walletForm); + Optional optApply = advancedDialog.showAndWait(); + if(optApply.isPresent() && optApply.get() && walletForm.getWallet().isValid()) { + revert.setDisable(true); + apply.setDisable(true); + saveWallet(false, true); + } } @Override @@ -355,7 +360,7 @@ public class SettingsController extends WalletFormController implements Initiali } } - private void saveWallet(boolean changePassword) { + private void saveWallet(boolean changePassword, boolean suggestChangePassword) { ECKey existingPubKey = walletForm.getStorage().getEncryptionPubKey(); WalletPasswordDialog.PasswordRequirement requirement; @@ -380,7 +385,7 @@ public class SettingsController extends WalletFormController implements Initiali } } - WalletPasswordDialog dlg = new WalletPasswordDialog(requirement); + WalletPasswordDialog dlg = new WalletPasswordDialog(null, requirement, suggestChangePassword); Optional password = dlg.showAndWait(); if(password.isPresent()) { if(dlg.isBackupExisting()) { @@ -399,7 +404,7 @@ public class SettingsController extends WalletFormController implements Initiali try { walletForm.getStorage().setEncryptionPubKey(Storage.NO_PASSWORD_KEY); walletForm.saveAndRefresh(); - if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW) { + if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW || requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_CHANGE) { EventManager.get().post(new RequestOpenWalletsEvent()); } } catch (IOException e) { @@ -425,18 +430,28 @@ public class SettingsController extends WalletFormController implements Initiali return; } + key = new Key(encryptionFullKey.getPrivKeyBytes(), walletForm.getStorage().getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2); + if(dlg.isChangePassword()) { + if(dlg.isDeleteBackups()) { + walletForm.deleteBackups(); + } + walletForm.getStorage().setEncryptionPubKey(null); - saveWallet(true); + walletForm.getWallet().decrypt(key); + saveWallet(true, false); return; } - key = new Key(encryptionFullKey.getPrivKeyBytes(), walletForm.getStorage().getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2); walletForm.getWallet().encrypt(key); - walletForm.getStorage().setEncryptionPubKey(encryptionPubKey); walletForm.saveAndRefresh(); - if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW) { + + if(dlg.isDeleteBackups()) { + walletForm.deleteBackups(); + } + + if(requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_NEW || requirement == WalletPasswordDialog.PasswordRequirement.UPDATE_EMPTY) { EventManager.get().post(new RequestOpenWalletsEvent()); } } catch (Exception e) { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index cd41af46..04b83146 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -75,6 +75,10 @@ public class WalletForm { storage.backupWallet(); } + public void deleteBackups() { + storage.deleteBackups(); + } + public void refreshHistory(Integer blockHeight, Wallet pastWallet) { refreshHistory(blockHeight, pastWallet, null); }