mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 13:16:44 +00:00
improvements to download verifier drag and drop
This commit is contained in:
parent
3d18477560
commit
81c7bc7ecb
3 changed files with 115 additions and 28 deletions
|
@ -219,6 +219,8 @@ public class AppController implements Initializable {
|
||||||
|
|
||||||
private SendToManyDialog sendToManyDialog;
|
private SendToManyDialog sendToManyDialog;
|
||||||
|
|
||||||
|
private DownloadVerifierDialog downloadVerifierDialog;
|
||||||
|
|
||||||
private Tab previouslySelectedTab;
|
private Tab previouslySelectedTab;
|
||||||
|
|
||||||
private boolean subTabsVisible;
|
private boolean subTabsVisible;
|
||||||
|
@ -634,7 +636,7 @@ public class AppController implements Initializable {
|
||||||
} catch(TransactionParseException e) {
|
} catch(TransactionParseException e) {
|
||||||
showErrorDialog("Invalid transaction", e.getMessage());
|
showErrorDialog("Invalid transaction", e.getMessage());
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
showErrorDialog("Invalid file", e.getMessage());
|
showErrorDialog("Invalid file", "Cannot recognise the format of this file.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -993,7 +995,7 @@ public class AppController implements Initializable {
|
||||||
public void openFile(File file) {
|
public void openFile(File file) {
|
||||||
if(isWalletFile(file)) {
|
if(isWalletFile(file)) {
|
||||||
openWalletFile(file, true);
|
openWalletFile(file, true);
|
||||||
} else if(isSignatureOrManifestFile(file)) {
|
} else if(isVerifyDownloadFile(file)) {
|
||||||
verifyDownload(new ActionEvent(file, rootStack));
|
verifyDownload(new ActionEvent(file, rootStack));
|
||||||
} else {
|
} else {
|
||||||
openTransactionFile(file);
|
openTransactionFile(file);
|
||||||
|
@ -1479,8 +1481,20 @@ public class AppController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void verifyDownload(ActionEvent event) {
|
public void verifyDownload(ActionEvent event) {
|
||||||
DownloadVerifierDialog downloadVerifierDialog = new DownloadVerifierDialog(event.getSource() instanceof File file ? file : null);
|
if(downloadVerifierDialog != null) {
|
||||||
|
Stage stage = (Stage)downloadVerifierDialog.getDialogPane().getScene().getWindow();
|
||||||
|
stage.setAlwaysOnTop(true);
|
||||||
|
stage.setAlwaysOnTop(false);
|
||||||
|
if(event.getSource() instanceof File file) {
|
||||||
|
downloadVerifierDialog.setSignatureFile(file);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadVerifierDialog = new DownloadVerifierDialog(event.getSource() instanceof File file ? file : null);
|
||||||
|
downloadVerifierDialog.initOwner(rootStack.getScene().getWindow());
|
||||||
downloadVerifierDialog.showAndWait();
|
downloadVerifierDialog.showAndWait();
|
||||||
|
downloadVerifierDialog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void minimizeToTray(ActionEvent event) {
|
public void minimizeToTray(ActionEvent event) {
|
||||||
|
|
|
@ -786,7 +786,7 @@ public class AppServices {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Window getActiveWindow() {
|
public static Window getActiveWindow() {
|
||||||
return Stage.getWindows().stream().filter(Window::isFocused).findFirst().orElse(get().walletWindows.keySet().iterator().hasNext() ? get().walletWindows.keySet().iterator().next() : null);
|
return Stage.getWindows().stream().filter(Window::isFocused).findFirst().orElse(get().walletWindows.keySet().iterator().hasNext() ? get().walletWindows.keySet().iterator().next() : (Stage.getWindows().iterator().hasNext() ? Stage.getWindows().iterator().next() : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void moveToActiveWindowScreen(Dialog<?> dialog) {
|
public static void moveToActiveWindowScreen(Dialog<?> dialog) {
|
||||||
|
@ -885,7 +885,7 @@ public class AppServices {
|
||||||
for(File file : openFiles) {
|
for(File file : openFiles) {
|
||||||
if(isWalletFile(file)) {
|
if(isWalletFile(file)) {
|
||||||
EventManager.get().post(new RequestWalletOpenEvent(openWindow, file));
|
EventManager.get().post(new RequestWalletOpenEvent(openWindow, file));
|
||||||
} else if(isSignatureOrManifestFile(file)) {
|
} else if(isVerifyDownloadFile(file)) {
|
||||||
EventManager.get().post(new RequestVerifyDownloadEvent(openWindow, file));
|
EventManager.get().post(new RequestVerifyDownloadEvent(openWindow, file));
|
||||||
} else {
|
} else {
|
||||||
EventManager.get().post(new RequestTransactionOpenEvent(openWindow, file));
|
EventManager.get().post(new RequestTransactionOpenEvent(openWindow, file));
|
||||||
|
|
|
@ -18,6 +18,8 @@ import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.input.Dragboard;
|
||||||
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
@ -35,12 +37,19 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.sparrow.AppController.DRAG_OVER_CLASS;
|
||||||
|
|
||||||
public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(DownloadVerifierDialog.class);
|
private static final Logger log = LoggerFactory.getLogger(DownloadVerifierDialog.class);
|
||||||
|
|
||||||
private static final DateFormat signatureDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy z");
|
private static final DateFormat signatureDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy z");
|
||||||
|
|
||||||
private static final long MAX_VALID_MANIFEST_SIZE = 100 * 1024;
|
private static final long MAX_VALID_MANIFEST_SIZE = 100 * 1024;
|
||||||
|
private static final String SHA256SUMS_MANIFEST_PREFIX = "sha256sums";
|
||||||
|
|
||||||
private static final List<String> SIGNATURE_EXTENSIONS = List.of("asc", "sig", "gpg");
|
private static final List<String> SIGNATURE_EXTENSIONS = List.of("asc", "sig", "gpg");
|
||||||
private static final List<String> MANIFEST_EXTENSIONS = List.of("txt");
|
private static final List<String> MANIFEST_EXTENSIONS = List.of("txt");
|
||||||
|
@ -48,6 +57,13 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
private static final List<String> MACOS_RELEASE_EXTENSIONS = List.of("dmg");
|
private static final List<String> MACOS_RELEASE_EXTENSIONS = List.of("dmg");
|
||||||
private static final List<String> WINDOWS_RELEASE_EXTENSIONS = List.of("exe", "zip");
|
private static final List<String> WINDOWS_RELEASE_EXTENSIONS = List.of("exe", "zip");
|
||||||
private static final List<String> LINUX_RELEASE_EXTENSIONS = List.of("deb", "rpm", "tar.gz");
|
private static final List<String> LINUX_RELEASE_EXTENSIONS = List.of("deb", "rpm", "tar.gz");
|
||||||
|
private static final List<String> DISK_IMAGE_EXTENSIONS = List.of("img", "bin", "dfu");
|
||||||
|
private static final List<String> ARCHIVE_EXTENSIONS = List.of("zip", "tar.gz", "tar.bz2", "tar.xz", "rar", "7z");
|
||||||
|
|
||||||
|
private static final String SPARROW_RELEASE_PREFIX = "sparrow-";
|
||||||
|
private static final String SPARROW_SIGNATURE_SUFFIX = "-manifest.txt.asc";
|
||||||
|
private static final Pattern SPARROW_RELEASE_VERSION = Pattern.compile("[0-9]+(\\.[0-9]+)*");
|
||||||
|
private static final long MIN_VALID_SPARROW_RELEASE_SIZE = 10 * 1024 * 1024;
|
||||||
|
|
||||||
private final ObjectProperty<File> signature = new SimpleObjectProperty<>();
|
private final ObjectProperty<File> signature = new SimpleObjectProperty<>();
|
||||||
private final ObjectProperty<File> manifest = new SimpleObjectProperty<>();
|
private final ObjectProperty<File> manifest = new SimpleObjectProperty<>();
|
||||||
|
@ -69,6 +85,7 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
dialogPane.getStylesheets().add(AppServices.class.getResource("dialog.css").toExternalForm());
|
dialogPane.getStylesheets().add(AppServices.class.getResource("dialog.css").toExternalForm());
|
||||||
AppServices.setStageIcon(dialogPane.getScene().getWindow());
|
AppServices.setStageIcon(dialogPane.getScene().getWindow());
|
||||||
dialogPane.setHeader(new Header());
|
dialogPane.setHeader(new Header());
|
||||||
|
setupDrag(dialogPane);
|
||||||
|
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
vBox.setSpacing(20);
|
vBox.setSpacing(20);
|
||||||
|
@ -179,26 +196,10 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
boolean verify = true;
|
boolean verify = true;
|
||||||
try {
|
try {
|
||||||
Map<File, String> manifestMap = getManifest(manifestFile);
|
Map<File, String> manifestMap = getManifest(manifestFile);
|
||||||
List<String> releaseExtensions = getReleaseFileExtensions();
|
File releaseFile = findReleaseFile(manifestFile, manifestMap);
|
||||||
for(File file : manifestMap.keySet()) {
|
if(releaseFile != null && !releaseFile.equals(release.get())) {
|
||||||
if(releaseExtensions.stream().anyMatch(ext -> file.getName().toLowerCase(Locale.ROOT).endsWith(ext))) {
|
release.set(releaseFile);
|
||||||
File releaseFile = new File(manifestFile.getParent(), file.getName());
|
verify = false;
|
||||||
if(releaseFile.exists() && !releaseFile.equals(release.get())) {
|
|
||||||
release.set(releaseFile);
|
|
||||||
verify = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(release.get() == null) {
|
|
||||||
for(File file : manifestMap.keySet()) {
|
|
||||||
File releaseFile = new File(manifestFile.getParent(), file.getName());
|
|
||||||
if(releaseFile.exists() && !releaseFile.equals(release.get())) {
|
|
||||||
release.set(releaseFile);
|
|
||||||
verify = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
log.debug("Error reading manifest file", e);
|
log.debug("Error reading manifest file", e);
|
||||||
|
@ -227,6 +228,39 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupDrag(DialogPane dialogPane) {
|
||||||
|
dialogPane.setOnDragOver(event -> {
|
||||||
|
if(event.getGestureSource() != dialogPane && event.getDragboard().hasFiles()) {
|
||||||
|
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
|
||||||
|
}
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogPane.setOnDragDropped(event -> {
|
||||||
|
Dragboard db = event.getDragboard();
|
||||||
|
boolean success = false;
|
||||||
|
if(db.hasFiles()) {
|
||||||
|
for(File file : db.getFiles()) {
|
||||||
|
if(isVerifyDownloadFile(file)) {
|
||||||
|
signature.set(file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
event.setDropCompleted(success);
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogPane.setOnDragEntered(event -> {
|
||||||
|
dialogPane.getStyleClass().add(DRAG_OVER_CLASS);
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogPane.setOnDragExited(event -> {
|
||||||
|
dialogPane.getStyleClass().removeAll(DRAG_OVER_CLASS);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void verify() {
|
public void verify() {
|
||||||
boolean signatureVerified = verifySignature();
|
boolean signatureVerified = verifySignature();
|
||||||
if(signatureVerified) {
|
if(signatureVerified) {
|
||||||
|
@ -412,6 +446,17 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(providedFile.getName().toLowerCase(Locale.ROOT).startsWith(SPARROW_RELEASE_PREFIX)) {
|
||||||
|
Matcher matcher = SPARROW_RELEASE_VERSION.matcher(providedFile.getName());
|
||||||
|
if(matcher.find()) {
|
||||||
|
String version = matcher.group();
|
||||||
|
File signatureFile = new File(providedFile.getParentFile(), SPARROW_RELEASE_PREFIX + version + SPARROW_SIGNATURE_SUFFIX);
|
||||||
|
if(signatureFile.exists()) {
|
||||||
|
return signatureFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +472,24 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File findReleaseFile(File manifestFile, Map<File, String> manifestMap) {
|
||||||
|
List<String> releaseExtensions = getReleaseFileExtensions();
|
||||||
|
List<List<String>> extensionLists = List.of(releaseExtensions, DISK_IMAGE_EXTENSIONS, ARCHIVE_EXTENSIONS, List.of(""));
|
||||||
|
|
||||||
|
for(List<String> extensions : extensionLists) {
|
||||||
|
for(File file : manifestMap.keySet()) {
|
||||||
|
if(extensions.stream().anyMatch(ext -> file.getName().toLowerCase(Locale.ROOT).endsWith(ext))) {
|
||||||
|
File releaseFile = new File(manifestFile.getParent(), file.getName());
|
||||||
|
if(releaseFile.exists()) {
|
||||||
|
return releaseFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> getReleaseFileExtensions() {
|
private List<String> getReleaseFileExtensions() {
|
||||||
Platform platform = Platform.getCurrent();
|
Platform platform = Platform.getCurrent();
|
||||||
switch(platform) {
|
switch(platform) {
|
||||||
|
@ -477,13 +540,14 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSignatureOrManifestFile(File file) {
|
public static boolean isVerifyDownloadFile(File file) {
|
||||||
if(file != null) {
|
if(file != null) {
|
||||||
if(file.getName().length() > 4 && SIGNATURE_EXTENSIONS.stream().anyMatch(ext -> file.getName().toLowerCase(Locale.ROOT).endsWith("." + ext))) {
|
String name = file.getName().toLowerCase(Locale.ROOT);
|
||||||
|
if(name.length() > 4 && SIGNATURE_EXTENSIONS.stream().anyMatch(ext -> name.endsWith("." + ext))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(file.getName().toLowerCase(Locale.ROOT).endsWith(".txt")) {
|
if(MANIFEST_EXTENSIONS.stream().anyMatch(ext -> name.endsWith("." + ext)) || name.startsWith(SHA256SUMS_MANIFEST_PREFIX)) {
|
||||||
try {
|
try {
|
||||||
Map<File, String> manifest = getManifest(file);
|
Map<File, String> manifest = getManifest(file);
|
||||||
return !manifest.isEmpty();
|
return !manifest.isEmpty();
|
||||||
|
@ -491,11 +555,20 @@ public class DownloadVerifierDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
//ignore
|
//ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(name.startsWith(SPARROW_RELEASE_PREFIX) && file.length() >= MIN_VALID_SPARROW_RELEASE_SIZE) {
|
||||||
|
Matcher matcher = SPARROW_RELEASE_VERSION.matcher(name);
|
||||||
|
return matcher.find();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSignatureFile(File signatureFile) {
|
||||||
|
signature.set(signatureFile);
|
||||||
|
}
|
||||||
|
|
||||||
private static class Header extends GridPane {
|
private static class Header extends GridPane {
|
||||||
public Header() {
|
public Header() {
|
||||||
setMaxWidth(Double.MAX_VALUE);
|
setMaxWidth(Double.MAX_VALUE);
|
||||||
|
|
Loading…
Reference in a new issue