diff --git a/src/org/bytedream/cryptogx/Controller.java b/src/org/bytedream/cryptogx/Controller.java
new file mode 100644
index 0000000..bb9071c
--- /dev/null
+++ b/src/org/bytedream/cryptogx/Controller.java
@@ -0,0 +1,1647 @@
+package org.bytedream.cryptogx;
+
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.event.Event;
+import javafx.fxml.Initializable;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuBar;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.*;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.VBox;
+import javafx.stage.*;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.bytedream.cryptogx.Settings.*;
+import static org.bytedream.cryptogx.Main.*;
+
+public class Controller implements Initializable {
+
+ private Event fileEnDecryptLabelEvent;
+
+ private double menubarX, menubarY;
+ private boolean textLoading = false;
+ private boolean fileEnDecryptLoading = false;
+ private boolean fileDeleteLoading = false;
+ private final AtomicInteger textThreads = new AtomicInteger(0);
+ private final AtomicInteger totalThreads = new AtomicInteger(0);
+ private final int tooltipShow = 15;
+ private final int DATAFILEURL = 2;
+ private final int FILEFILEURL = 1;
+ private final int NONSPECIFICFILEURL = 0;
+ private final byte[] buffer = new byte[64];
+ private final KeyCombination paste = new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN);
+ private final Image loadingImage = new Image(getClass().getResource("resources/loading.gif").toExternalForm());
+
+ private HashMap currentConfigSettings = new HashMap<>();
+
+ private final HashMap> enDecryptInputOutputFiles = new HashMap<>();
+ private final HashMap> enDecryptInputOutputInternetFiles = new HashMap<>();
+ private final HashMap enDecryptInputOutputClipboardImages = new HashMap<>();
+ private final HashMap deleteInputFiles = new HashMap<>();
+ private final List fileEnDecryptThreads = Collections.synchronizedList(new ArrayList<>());
+ private final List fileDeleteThreads = Collections.synchronizedList(new ArrayList<>());
+
+ private final ContextMenu fileEnDecryptInputContextMenu = new ContextMenu();
+ private final ContextMenu fileDeleteInputContextMenu = new ContextMenu();
+ private Label choosedLabel = null;
+ private String choosedLabelType = null;
+ private final MenuItem fileOutputFileChangeDest = new MenuItem("Change output file");
+ private final MenuItem getChoosedLabelInputFileFolder = new MenuItem("Open source directory");
+ private final MenuItem getChoosedLabelOutputFileFolder = new MenuItem("Open source directory");
+ private final Tooltip tooltip = new Tooltip();
+
+ public AnchorPane rootWindow;
+
+ public Button fileEnDecryptFilesButton;
+ public Button fileDecrypt;
+ public Button fileEncrypt;
+ public Button fileEnDecryptStop;
+
+ public ComboBox textAlgorithmBox;
+ public ComboBox fileEnDecryptAlgorithmBox;
+
+ public ImageView minimizeWindow;
+ public ImageView closeWindow;
+ public ImageView textLoadingImage;
+ public ImageView fileEnDecryptLoadingImage;
+ public ImageView fileDeleteLoadingImage;
+
+ public Menu settingsMenu;
+ public Menu helpMenu;
+
+ public MenuBar menubar;
+
+ public MenuItem setDefaultOutputPath;
+ public MenuItem saveSettings;
+ public MenuItem loadSettings;
+ public MenuItem exportSettings;
+ public MenuItem importSettings;
+
+ public RadioMenuItem removeFileFromFileBox;
+ public RadioMenuItem limitNumberOfThreads;
+
+ public ScrollPane fileEnDecryptInputScroll;
+
+ public TextArea textDecryptedEntry;
+ public TextArea textEncryptedEntry;
+
+ public TextField textKeyEntry;
+ public TextField textSaltEntry;
+ public TextField fileEnDecryptKeyEntry;
+ public TextField fileDecryptOutputFile;
+ public TextField fileEncryptOutputFile;
+ public TextField fileEnDecryptSaltEntry;
+ public TextField fileDeleteIterationsEntry;
+
+ public VBox fileEnDecryptInputFiles;
+ public VBox fileDeleteInputFiles;
+
+ //-----general-----//
+
+ /**
+ * Shows a tooltip when the user type in some text in a text field, text area, etc. and the mouse is over this entry
+ *
+ * @param event from which this method is called
+ */
+ public void keyTypedTooltip(KeyEvent event) {
+ String id = null;
+ String text = "";
+ try {
+ id = ((TextField) event.getSource()).getId();
+ text = ((TextField) event.getSource()).getText() + event.getCharacter();
+ tooltip.setText(text);
+ } catch (ClassCastException e) {
+ tooltip.setText(((TextArea) event.getSource()).getText() + event.getCharacter());
+ }
+ if (id != null) {
+ switch (id) {
+ case ("textKeyEntry"):
+ currentConfigSettings.replace("textKey", text);
+ break;
+ case ("textSaltEntry"):
+ currentConfigSettings.replace("textSalt", text);
+ break;
+ case ("fileEnDecryptKeyEntry"):
+ currentConfigSettings.replace("fileEnDecryptKey", text);
+ break;
+ case ("fileEnDecryptSaltEntry"):
+ currentConfigSettings.replace("fileEnDecryptSalt", text);
+ break;
+ case ("fileDeleteIterationsEntry"):
+ currentConfigSettings.replace("fileDeleteIterations", String.valueOf(Integer.parseInt(text)));
+ break;
+ }
+ }
+ }
+
+ /**
+ * Shows a tooltip when to mouse is over a text field, text area, etc.
+ *
+ * @param event from which this method is called
+ */
+ public void mouseOverEntryTooltip(MouseEvent event) {
+ try {
+ tooltip.setText(((TextField) event.getSource()).getText());
+ } catch (ClassCastException e) {
+ try {
+ tooltip.setText(((TextArea) event.getSource()).getText());
+ } catch (ClassCastException ex) {
+ tooltip.setText(((Label) event.getSource()).getText());
+ }
+ }
+ if (!tooltip.getText().trim().isEmpty()) {
+ tooltip.show(rootWindow.getScene().getWindow(), event.getScreenX(), event.getScreenY() + tooltipShow);
+ }
+ }
+
+ /**
+ * Hides the tooltip if the mouse exit a text field, text area, etc.
+ */
+ public void mouseExitEntryTooltip() {
+ tooltip.hide();
+ }
+
+ //-----menu / close bar-----//
+
+ /**
+ * Closed the application.
+ * Get called if red close button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void closeApplication() {
+ Stage rootStage = (Stage) rootWindow.getScene().getWindow();
+ rootStage.close();
+ System.exit(0);
+ }
+
+ /**
+ * Hides the application.
+ * Get called if the green minimize button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void minimizeApplication() {
+ Stage rootStage = (Stage) rootWindow.getScene().getWindow();
+ rootStage.setIconified(true);
+ }
+
+ //-----text-----//
+
+ /**
+ * Encrypt text in {@link Controller#textDecryptedEntry}.
+ * Get called if the text 'Encrypt' button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void textEncryptButton() {
+ final byte[] salt;
+ if (!textSaltEntry.getText().isEmpty()) {
+ salt = textSaltEntry.getText().getBytes(StandardCharsets.UTF_8);
+ } else {
+ salt = new byte[16];
+ }
+ if (!textLoading) {
+ textLoadingImage.setImage(loadingImage);
+ }
+ Thread textEncrypt = new Thread(() -> {
+ textThreads.getAndIncrement();
+ if (limitNumberOfThreads.isSelected()) {
+ while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().stop();
+ }
+ }
+ }
+ totalThreads.getAndIncrement();
+ String textAlgorithm = textAlgorithmBox.getSelectionModel().getSelectedItem();
+ EnDecrypt.AES encrypt = new EnDecrypt.AES(textKeyEntry.getText(), salt, Integer.parseInt(textAlgorithm.substring(textAlgorithm.indexOf('-') + 1)));
+ try {
+ String encryptedText = encrypt.encrypt(textDecryptedEntry.getText());
+ Platform.runLater(() -> textEncryptedEntry.setText(encryptedText));
+ } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException | IllegalBlockSizeException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("Wrong text for encryption is given", e.getMessage()));
+ }
+ if ((textThreads.get() - 1) <= 0) {
+ textLoadingImage.setImage(null);
+ textLoading = false;
+ }
+ textThreads.getAndDecrement();
+ totalThreads.getAndDecrement();
+ });
+ textEncrypt.setDaemon(false);
+ textEncrypt.start();
+ textLoading = true;
+ }
+
+ /**
+ * Decrypt text in {@link Controller#textEncryptedEntry}.
+ * Get called if the text 'Decrypt' button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void textDecryptButton() {
+ final byte[] salt;
+ if (!textSaltEntry.getText().isEmpty()) {
+ salt = textSaltEntry.getText().getBytes(StandardCharsets.UTF_8);
+ } else {
+ salt = new byte[16];
+ }
+ if (!textLoading) {
+ textLoadingImage.setImage(loadingImage);
+ }
+ Thread textDecrypt = new Thread(() -> {
+ textThreads.getAndIncrement();
+ if (limitNumberOfThreads.isSelected()) {
+ while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().stop();
+ }
+ }
+ }
+ totalThreads.getAndIncrement();
+ String textAlgorithm = textAlgorithmBox.getSelectionModel().getSelectedItem();
+ EnDecrypt.AES decrypt = new EnDecrypt.AES(textKeyEntry.getText(), salt, Integer.parseInt(textAlgorithm.substring(textAlgorithm.indexOf('-') + 1)));
+ try {
+ String DecryptedText = decrypt.decrypt(textEncryptedEntry.getText());
+ Platform.runLater(() -> textDecryptedEntry.setText(DecryptedText));
+ } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("Wrong key and / or salt is given", e.getMessage()));
+ } catch (IllegalArgumentException | IllegalBlockSizeException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("Wrong text for decryption is given", e.getMessage()));
+ }
+ if ((textThreads.get() - 1) <= 0) {
+ textLoading = false;
+ Platform.runLater(() -> textLoadingImage.setImage(null));
+ }
+ textThreads.getAndDecrement();
+ totalThreads.getAndDecrement();
+ });
+ textDecrypt.setDaemon(false);
+ textDecrypt.start();
+ textLoading = true;
+ }
+
+ //-----fileEnDecrypt-----//
+
+ /**
+ * Synchronized method to get the list of threads which en- / decrypt files
+ *
+ * @return list of en- / decryption threads
+ */
+ private synchronized List getFileEnDecryptThreads() {
+ return fileEnDecryptThreads;
+ }
+
+ /**
+ * Synchronized method to get the number of threads which en- / decrypt files
+ *
+ * @return number of en- / decryption threads
+ *
+ * @since 1.2.0
+ */
+ private synchronized int getFileEnDecryptThreadsSize() {
+ return fileEnDecryptThreads.size();
+ }
+
+ /**
+ * Synchronized method to add a thread to the file en- / decryption list of current running file en- / decryption threads
+ *
+ * @param thread that should be added
+ *
+ * @since 1.2.0
+ */
+ private synchronized void addFileEnDecryptThread(Thread thread) {
+ fileEnDecryptThreads.add(thread);
+ }
+
+ /**
+ * Synchronized method to remove a thread from the file en- / decryption list of current running file en- / decryption threads
+ *
+ * @param thread that should be removed
+ *
+ * @since 1.2.0
+ */
+ private synchronized void removeFileEnDecryptThread(Thread thread) {
+ fileEnDecryptThreads.remove(thread);
+ }
+
+ /**
+ * Adds a file for en- / decryption
+ *
+ * @param file that should be added
+ *
+ * @since 1.0.0
+ */
+ private void fileEnDecryptAddFile(File file) {
+ for (Label l: enDecryptInputOutputFiles.keySet()) {
+ if (l.getText().equals(file.getAbsolutePath())) {
+ return;
+ }
+ }
+ Label newLabel = new Label(file.getAbsolutePath());
+ newLabel.setOnKeyTyped(this::keyTypedTooltip);
+ newLabel.setOnMouseMoved(this::mouseOverEntryTooltip);
+ newLabel.setOnMouseExited(event -> mouseExitEntryTooltip());
+ newLabel.setOnMouseClicked(event -> {
+ fileEnDecryptSelected(newLabel);
+ fileOutputFilesChangeText(newLabel, null, null);
+ fileEnDecryptLabelEvent = event;
+ });
+ newLabel.setContextMenu(fileEnDecryptInputContextMenu);
+ String fileAbsolutePath = file.getAbsolutePath();
+ String fileName = file.getName();
+
+ File encryptFile;
+ File decryptFile;
+ String fileOutputPath = file.getParent() + "/";;
+ String fileEnding;
+ ArrayList inputOutputList = new ArrayList<>();
+ if (!currentConfigSettings.get("fileOutputPath").trim().isEmpty()) {
+ fileOutputPath = currentConfigSettings.get("fileOutputPath").trim() + "/";
+ }
+ if (file.isFile()) {
+ fileEnding = ".cryptoGX";
+ } else {
+ fileEnding = "_cryptoGX";
+ }
+ encryptFile = new File(fileOutputPath + fileName + fileEnding);
+ while (encryptFile.exists()) {
+ encryptFile = new File(encryptFile.getAbsolutePath() + fileEnding);
+ }
+ if (fileAbsolutePath.endsWith(".cryptoGX") || fileAbsolutePath.endsWith("_cryptoGX")) {
+ decryptFile = new File(fileOutputPath + fileName.substring(0, fileName.length() - 9));
+ } else {
+ decryptFile = new File(fileOutputPath + fileName + fileEnding);
+ }
+ while (decryptFile.exists()) {
+ decryptFile = new File(decryptFile.getAbsolutePath() + fileEnding);
+ }
+ inputOutputList.add(0, encryptFile);
+ inputOutputList.add(1, decryptFile);
+ fileEnDecryptInputFiles.getChildren().add(newLabel);
+ enDecryptInputOutputFiles.put(newLabel, inputOutputList);
+ }
+
+ /**
+ * Adds an file from the internet for en- / decryption
+ *
+ * @param url of the file
+ * @param fileType of the file
+ * @throws URISyntaxException
+ *
+ * @since 1.5.0
+ */
+ private void fileEnDecryptAddInternetFile(String url, int fileType) throws URISyntaxException {
+ String filename;
+ switch (fileType) {
+ case FILEFILEURL:
+ filename = url.substring(url.lastIndexOf("/") + 1);
+ break;
+ case DATAFILEURL:
+ filename = url.substring(5, url.indexOf("/")) + "." + url.substring(url.indexOf("/") + 1, url.indexOf(";"));
+ break;
+ case NONSPECIFICFILEURL:
+ filename = "unknown" + System.nanoTime();
+ break;
+ default:
+ warningAlert("Cannot read given url '" + url + "'");
+ return;
+ }
+ for (Label l: enDecryptInputOutputInternetFiles.keySet()) {
+ if (l.getText().equals(filename)) {
+ return;
+ }
+ }
+ Label newLabel = new Label(filename);
+ newLabel.setOnKeyTyped(this::keyTypedTooltip);
+ newLabel.setOnMouseMoved(this::mouseOverEntryTooltip);
+ newLabel.setOnMouseExited(event -> mouseExitEntryTooltip());
+ newLabel.setOnMouseClicked(event -> {
+ fileEnDecryptSelected(newLabel);
+ fileOutputFilesChangeText(newLabel, null, null);
+ fileEnDecryptLabelEvent = event;
+ });
+ newLabel.setContextMenu(fileEnDecryptInputContextMenu);
+
+ File encryptFile;
+ File decryptFile;
+ ArrayList fileSpecs = new ArrayList<>();
+ ArrayList inputOutputFiles = new ArrayList<>();
+ fileSpecs.add(0, fileType);
+ fileSpecs.add(1, url);
+ String currentDir = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
+
+ if (currentConfigSettings.get("fileOutputPath").trim().isEmpty()) {
+ encryptFile = new File(currentDir + "/" + filename + ".cryptoGX");
+ while (encryptFile.isFile()) {
+ encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX");
+ }
+ if (url.endsWith(".cryptoGX") && filename.endsWith(".cryptoGX")) {
+ decryptFile = new File(currentDir + "/" + filename.substring(0, filename.length() - 9));
+ } else {
+ decryptFile = new File(currentDir + "/" + filename);
+ }
+ } else {
+ encryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename + ".cryptoGX");
+ while (encryptFile.isFile()) {
+ encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX");
+ }
+ if (url.endsWith(".cryptoGX") && filename.endsWith(".cryptoGX")) {
+ decryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename.substring(0, filename.length() - 9));
+ } else {
+ decryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename);
+ }
+ }
+ while (decryptFile.isFile()) {
+ decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX");
+ }
+ inputOutputFiles.add(0, encryptFile);
+ inputOutputFiles.add(1, decryptFile);
+
+ fileEnDecryptInputFiles.getChildren().add(newLabel);
+ enDecryptInputOutputInternetFiles.put(newLabel, fileSpecs);
+ enDecryptInputOutputFiles.put(newLabel, inputOutputFiles);
+ }
+
+ /**
+ * Adds an clipboard image for en- / decryption.
+ * This can be a normal image and an image stream
+ *
+ * @param image that should be added
+ * @throws URISyntaxException
+ *
+ * @since 1.7.0
+ */
+ private void fileEnDecryptAddClipboardImage(BufferedImage image) throws URISyntaxException {
+ String filename = "clipboardImage" + System.nanoTime() + ".png";
+ for (Label l: enDecryptInputOutputClipboardImages.keySet()) {
+ if (l.getText().equals(filename)) {
+ return;
+ }
+ }
+ Label newLabel = new Label(filename);
+ newLabel.setOnKeyTyped(this::keyTypedTooltip);
+ newLabel.setOnMouseMoved(this::mouseOverEntryTooltip);
+ newLabel.setOnMouseExited(event -> mouseExitEntryTooltip());
+ newLabel.setOnMouseClicked(event -> {
+ fileEnDecryptSelected(newLabel);
+ fileOutputFilesChangeText(newLabel, null, null);
+ fileEnDecryptLabelEvent = event;
+ });
+ newLabel.setContextMenu(fileEnDecryptInputContextMenu);
+
+ File encryptFile;
+ File decryptFile;
+ String currentDir = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
+ ArrayList inputOutputFiles = new ArrayList<>();
+
+ if (currentConfigSettings.get("fileOutputPath").trim().isEmpty()) {
+ encryptFile = new File(currentDir + "/" + filename + ".cryptoGX");
+ decryptFile = new File(currentDir + "/" + filename);
+ } else {
+ encryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + filename + ".cryptoGX");
+ decryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + filename);
+ }
+ while (encryptFile.isFile()) {
+ encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX");
+ }
+ while (decryptFile.isFile()) {
+ decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX");
+ }
+ inputOutputFiles.add(0, encryptFile);
+ inputOutputFiles.add(1, decryptFile);
+
+ fileEnDecryptInputFiles.getChildren().add(newLabel);
+ enDecryptInputOutputClipboardImages.put(newLabel, image);
+ enDecryptInputOutputFiles.put(newLabel, inputOutputFiles);
+ }
+
+ /**
+ * Changes the text in the file en- / decryption output file text fields
+ *
+ * @param label
+ * @param encryptOutputFile is the filename of the file it gets encrypted
+ * @param decryptOutputFile is the filename of the file it gets decrypted
+ *
+ * @since 1.2.0
+ */
+ private void fileOutputFilesChangeText(Label label, String encryptOutputFile, String decryptOutputFile) {
+ File encryptFile;
+ File decryptFile;
+ ArrayList change = new ArrayList<>();
+ if (encryptOutputFile == null) {
+ encryptFile = enDecryptInputOutputFiles.get(label).get(0);
+ } else {
+ encryptFile = new File(encryptOutputFile);
+ }
+ if (decryptOutputFile == null) {
+ decryptFile = enDecryptInputOutputFiles.get(label).get(1);
+ } else {
+ decryptFile = new File(decryptOutputFile);
+ }
+ change.add(0, encryptFile);
+ change.add(1, decryptFile);
+ if (encryptFile.toString().trim().isEmpty()) {
+ fileEncryptOutputFile.setText("");
+ } else {
+ fileEncryptOutputFile.setText(encryptFile.getAbsolutePath());
+ }
+ if (decryptFile.toString().trim().isEmpty()) {
+ fileDecryptOutputFile.setText("");
+ } else {
+ fileDecryptOutputFile.setText(decryptFile.getAbsolutePath());
+ }
+ enDecryptInputOutputFiles.replace(label, change);
+ }
+
+ /**
+ * Deletes an entry for en- / decryption.
+ * Get called if the user presses 'del' or delete the entry in the en- / decryption box via the right click tooltip
+ *
+ * @param label that should be deleted
+ *
+ * @since 1.2.0
+ */
+ private void fileEnDecryptDeleteEntry(Label label) {
+ enDecryptInputOutputFiles.remove(label);
+ if (fileEnDecryptInputFiles.getChildren().size() - 1 >= 1) {
+ for (int i = 0; i < fileEnDecryptInputFiles.getChildren().size(); i++) {
+ if (fileEnDecryptInputFiles.getChildren().get(i) == label) {
+ fileEnDecryptInputFiles.getChildren().remove(label);
+ if (label == choosedLabel) {
+ try {
+ choosedLabel = (Label) fileEnDecryptInputFiles.getChildren().get(i - 1);
+ choosedLabelType = "ENDECRYPT";
+ fileOutputFilesChangeText(choosedLabel, null, null);
+ fileEnDecryptSelected(choosedLabel);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ fileOutputFileChangeDest.setDisable(true);
+ getChoosedLabelOutputFileFolder.setDisable(true);
+ fileEncryptOutputFile.setEditable(false);
+ fileDecryptOutputFile.setEditable(false);
+ fileOutputFilesChangeText(choosedLabel, "", "");
+ choosedLabel = null;
+ choosedLabelType = null;
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ fileEnDecryptInputFiles.getChildren().remove(label);
+ fileOutputFileChangeDest.setDisable(true);
+ getChoosedLabelOutputFileFolder.setDisable(true);
+ fileEncryptOutputFile.setEditable(false);
+ fileDecryptOutputFile.setEditable(false);
+ if (label == choosedLabel) {
+ fileOutputFilesChangeText(choosedLabel, "", "");
+ choosedLabel = null;
+ choosedLabelType = null;
+ }
+ }
+ }
+
+ /**
+ * Changes the highlight of the clicked item in the en- / decryption box.
+ * Get called if the user click an non-highlighted item in the en- / decryption box
+ *
+ * @param changeLabel is the label that the user has clicked
+ *
+ * @since 1.0.0
+ */
+ private void fileEnDecryptSelected(Label changeLabel) {
+ if (changeLabel != null) {
+ fileDeleteSelected(null);
+ enDecryptInputOutputFiles.keySet().forEach(label -> label.setStyle(null));
+ changeLabel.setStyle("-fx-background-color: lightblue; -fx-border-color: #292929");
+ fileDecryptOutputFile.setEditable(true);
+ fileEncryptOutputFile.setEditable(true);
+ fileOutputFileChangeDest.setDisable(false);
+ getChoosedLabelOutputFileFolder.setDisable(false);
+ choosedLabel = changeLabel;
+ choosedLabelType = "ENDECRYPT";
+ } else {
+ enDecryptInputOutputFiles.keySet().forEach(label -> label.setStyle(null));
+ fileDecryptOutputFile.setEditable(false);
+ fileEncryptOutputFile.setEditable(false);
+ fileOutputFileChangeDest.setDisable(true);
+ getChoosedLabelOutputFileFolder.setDisable(true);
+ }
+ }
+
+ /**
+ * Opens a file chooser GUI where the user can select the files that should be en- / decrypted.
+ * Get called if the 'Choose files...' in the file en- / decrypt section button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileEnDecryptChooseFiles() {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Choose files");
+ fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*"));
+ List files = fileChooser.showOpenMultipleDialog(rootWindow.getScene().getWindow());
+ try {
+ if (files.size() > 0) {
+ files.forEach(this::fileEnDecryptAddFile);
+ }
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Opens a directory chooser GUI where the user can select the directories that should be en- / decrypted.
+ * Get called if the 'directories...' in the file en- / decrypt section button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileEnDecryptChooseDirectories() {
+ DirectoryChooser directoryChooser = new DirectoryChooser();
+ directoryChooser.setTitle("Choose directories");
+ File file = directoryChooser.showDialog(rootWindow.getScene().getWindow());
+ try {
+ fileEnDecryptAddFile(file);
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Get called if user drags a (normal or internet) file over the en- / decrypt file box
+ *
+ * @param event source
+ *
+ * @since 1.2.0
+ */
+ public void onFileEnDecryptDragOver(DragEvent event) {
+ Dragboard dragboard = event.getDragboard();
+ if (event.getGestureSource() != fileEnDecryptInputFiles) {
+ if (dragboard.hasFiles()) {
+ event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
+ } else if (dragboard.hasUrl()) {
+ String url = dragboard.getUrl();
+ String urlFilename = dragboard.getUrl().split("/")[dragboard.getUrl().split("/").length - 1];
+ if (url.startsWith("data:")) {
+ try {
+ final int dataStartIndex = url.indexOf(",") + 1;
+ final String data = url.substring(dataStartIndex);
+ java.util.Base64.getDecoder().decode(data);
+ event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else if (urlFilename.contains(".") && Utils.hasAnyCharacter("\\/:*?|<>\"", urlFilename)) {
+ try {
+ new URL(url);
+ event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return;
+ }
+ } else {
+ event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get called if the user drops the dragged (normal or internet) file over the en- / decrypt file box
+ *
+ * @param event source
+ * @throws URISyntaxException
+ *
+ * @since 1.2.0
+ */
+ public void onFileEnDecryptDragNDrop(DragEvent event) throws URISyntaxException {
+ Dragboard dragboard = event.getDragboard();
+ if (dragboard.hasFiles()) {
+ dragboard.getFiles().forEach(this::fileEnDecryptAddFile);
+ } else if (dragboard.hasUrl()) {
+ String url = dragboard.getUrl();
+ String urlFilename = dragboard.getUrl().split("/")[dragboard.getUrl().split("/").length - 1];
+ if (url.startsWith("data:")) {
+ fileEnDecryptAddInternetFile(url, DATAFILEURL);
+ } else if (urlFilename.contains(".") && Utils.hasAnyCharacter("\\/:*?|<>\"", urlFilename)) {
+ fileEnDecryptAddInternetFile(url, FILEFILEURL);
+ } else {
+ fileEnDecryptAddInternetFile(url, NONSPECIFICFILEURL);
+ }
+ }
+ }
+
+ /**
+ * If the user presses Ctrl + V: Adds the last object in clipboard (if file) for en- / decryption.
+ * Get called if the user presses a key while selected file en- / decryption box
+ *
+ * @param event source
+ * @throws URISyntaxException
+ *
+ * @since 1.7.0
+ */
+ public void onFileEnDecryptPaste(KeyEvent event) throws URISyntaxException {
+ if (paste.match(event)) {
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ Transferable transferable = clipboard.getContents(null);
+ try {
+ if (transferable != null) {
+ if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
+ Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor);
+ List files = (List) objectFileList;
+ files.forEach(o -> fileEnDecryptAddFile((File) o));
+ } else if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
+ Object objectImage = transferable.getTransferData(DataFlavor.imageFlavor);
+ fileEnDecryptAddClipboardImage((BufferedImage) objectImage);
+ }
+ }
+ } catch (UnsupportedFlavorException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Encrypt all files given files.
+ * Get called if file 'Encrypt' button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void fileEncryptButton() {
+ final byte[] salt;
+ if (!fileEnDecryptSaltEntry.getText().isEmpty()) {
+ salt = fileEnDecryptSaltEntry.getText().getBytes(StandardCharsets.UTF_8);
+ } else {
+ salt = new byte[16];
+ }
+ if (!enDecryptInputOutputFiles.isEmpty()) {
+ removeFileFromFileBox.setDisable(true);
+ limitNumberOfThreads.setDisable(true);
+ for(Map.Entry> entry: enDecryptInputOutputFiles.entrySet()) {
+ Thread thread = new Thread(() -> {
+ addFileEnDecryptThread(Thread.currentThread());
+ if (limitNumberOfThreads.isSelected()) {
+ while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().stop();
+ }
+ }
+ }
+ totalThreads.getAndIncrement();
+ Label inputFileLabel = entry.getKey();
+ ArrayList outputFileList = entry.getValue();
+ String fileEnDecryptAlgorithm = fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem();
+ EnDecrypt.AES fileEncrypt = new EnDecrypt.AES(fileEnDecryptKeyEntry.getText(), salt, Integer.parseInt(fileEnDecryptAlgorithm.substring(fileEnDecryptAlgorithm.indexOf('-') + 1)));
+ if (enDecryptInputOutputInternetFiles.containsKey(inputFileLabel)) {
+ ArrayList fileSpecs = enDecryptInputOutputInternetFiles.get(inputFileLabel);
+ int urlType = (int) fileSpecs.get(0);
+ String url = (String) fileSpecs.get(1);
+ try {
+ if (urlType == FILEFILEURL || urlType == NONSPECIFICFILEURL) {
+ URLConnection openURL = new URL(url).openConnection();
+ openURL.addRequestProperty("User-Agent", "Mozilla/5.0");
+ fileEncrypt.encryptFile(openURL.getInputStream(), new FileOutputStream((File) fileSpecs.get(2)), buffer);
+ } else if (urlType == DATAFILEURL) {
+ final int dataStartIndex = url.indexOf(",") + 1;
+ final String data = url.substring(dataStartIndex);
+ byte[] decoded = java.util.Base64.getDecoder().decode(data);
+ fileEncrypt.encryptFile(new ByteArrayInputStream(decoded), new FileOutputStream((File) fileSpecs.get(2)), buffer);
+ }
+ } catch (FileNotFoundException | InvalidKeySpecException | NoSuchAlgorithmException | MalformedURLException | InvalidKeyException | NoSuchPaddingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ }
+ } else if (enDecryptInputOutputClipboardImages.containsKey(inputFileLabel)) {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ BufferedImage bufferedImage = enDecryptInputOutputClipboardImages.get(inputFileLabel);
+ try {
+ ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
+ fileEncrypt.encryptFile(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), new FileOutputStream(outputFileList.get(0).getAbsoluteFile()), buffer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ File inputFile = new File(inputFileLabel.getText());
+ if (inputFile.isFile()) {
+ fileEncrypt.encryptFile(new FileInputStream(inputFile), new FileOutputStream(outputFileList.get(0)), buffer);
+ } else {
+ fileEncrypt.encryptDirectory(inputFileLabel.getText(), outputFileList.get(0).getAbsolutePath(), ".cryptoGX", buffer);
+ if (!outputFileList.get(0).isDirectory()) {
+ Platform.runLater(() -> warningAlert("Couldn't create directory\n '" + outputFileList.get(0).getAbsolutePath() + "'.\nTry again or restart cryptoGX with admin privileges"));
+ }
+ }
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ }
+ }
+ if (removeFileFromFileBox.isSelected()) {
+ Platform.runLater(() -> fileEnDecryptDeleteEntry(entry.getKey()));
+ }
+ if ((getFileEnDecryptThreadsSize() - 1) <= 0) {
+ fileEnDecryptLoading = false;
+ Platform.runLater(() -> {
+ fileEnDecryptLoadingImage.setImage(null);
+ removeFileFromFileBox.setDisable(false);
+ limitNumberOfThreads.setDisable(false);
+ });
+ }
+ removeFileEnDecryptThread(Thread.currentThread());
+ totalThreads.getAndDecrement();
+ });
+ thread.setDaemon(false);
+ thread.start();
+ if (!fileEnDecryptLoading) {
+ fileEnDecryptLoadingImage.setImage(loadingImage);
+ }
+ fileEnDecryptLoading = true;
+ }
+ }
+ }
+
+ /**
+ * Decrypt all files given files.
+ * Get called if file 'Decrypt' button is pressed
+ *
+ * @since 1.0.0
+ */
+ public void fileDecryptButton() {
+ final byte[] salt;
+ if (!fileEnDecryptSaltEntry.getText().isEmpty()) {
+ salt = fileEnDecryptSaltEntry.getText().getBytes(StandardCharsets.UTF_8);
+ } else {
+ salt = new byte[16];
+ }
+ if (!enDecryptInputOutputFiles.isEmpty()) {
+ removeFileFromFileBox.setDisable(true);
+ limitNumberOfThreads.setDisable(true);
+ for(Map.Entry> entry: enDecryptInputOutputFiles.entrySet()) {
+ Thread thread = new Thread(() -> {
+ addFileEnDecryptThread(Thread.currentThread());
+ if (limitNumberOfThreads.isSelected()) {
+ while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().stop();
+ }
+ }
+ }
+ totalThreads.getAndIncrement();
+ Label inputFileLabel = entry.getKey();
+ ArrayList outputFileList = entry.getValue();
+ String fileEnDecryptAlgorithm = fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem();
+ EnDecrypt.AES fileDecrypt = new EnDecrypt.AES(fileEnDecryptKeyEntry.getText(), salt, Integer.parseInt(fileEnDecryptAlgorithm.substring(fileEnDecryptAlgorithm.indexOf('-') + 1)));
+ if (enDecryptInputOutputInternetFiles.containsKey(entry.getKey())) {
+ ArrayList imageSpecs = enDecryptInputOutputInternetFiles.get(entry.getKey());
+ int urlType = (int) imageSpecs.get(0);
+ String url = (String) imageSpecs.get(1);
+ try {
+ if (urlType == FILEFILEURL) {
+ URLConnection openURL = new URL(url).openConnection();
+ openURL.addRequestProperty("User-Agent", "Mozilla/5.0");
+ fileDecrypt.decryptFile(openURL.getInputStream(), new FileOutputStream((File) imageSpecs.get(2)), buffer);
+ fileDecrypt.decryptFile(openURL.getInputStream(), new FileOutputStream((File) imageSpecs.get(2)), buffer);
+ } else if (urlType == DATAFILEURL) {
+ final int dataStartIndex = url.indexOf(",") + 1;
+ final String data = url.substring(dataStartIndex);
+ byte[] decoded = java.util.Base64.getDecoder().decode(data);
+ fileDecrypt.decryptFile(new ByteArrayInputStream(decoded), new FileOutputStream((File) imageSpecs.get(2)), buffer);
+ } else if (urlType == NONSPECIFICFILEURL) {
+ URLConnection openURL = new URL(url).openConnection();
+ openURL.addRequestProperty("User-Agent", "Mozilla/5.0");
+ fileDecrypt.decryptFile(openURL.getInputStream(), new FileOutputStream((File) imageSpecs.get(2)), buffer);
+ }
+ } catch (FileNotFoundException | InvalidKeySpecException | NoSuchAlgorithmException | MalformedURLException | InvalidKeyException | NoSuchPaddingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ }
+ } else if (enDecryptInputOutputClipboardImages.containsKey(inputFileLabel)) {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ BufferedImage bufferedImage = enDecryptInputOutputClipboardImages.get(inputFileLabel);
+ try {
+ ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
+ fileDecrypt.decryptFile(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), new FileOutputStream(outputFileList.get(1).getAbsolutePath()), buffer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ File inputFile = new File(inputFileLabel.getText());
+ if (inputFile.isFile()) {
+ fileDecrypt.decryptFile(new FileInputStream(inputFile), new FileOutputStream(outputFileList.get(1)), buffer);
+ } else {
+ fileDecrypt.decryptDirectory(inputFileLabel.getText(), outputFileList.get(1).getAbsolutePath(), "@.cryptoGX@", buffer);
+ if (!outputFileList.get(1).isDirectory()) {
+ Platform.runLater(() -> warningAlert("Couldn't create directory\n '" + outputFileList.get(1).getAbsolutePath() + "'.\nTry again or restart cryptoGX with admin privileges"));
+ }
+ }
+ } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage()));
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ Platform.runLater(() -> errorAlert("Wrong text for encryption is given", e.getMessage()));
+ }
+ }
+ if (removeFileFromFileBox.isSelected()) {
+ Platform.runLater(() -> fileEnDecryptDeleteEntry(entry.getKey()));
+ }
+ if ((getFileEnDecryptThreadsSize() - 1) <= 0) {
+ fileEnDecryptLoading = false;
+ Platform.runLater(() -> {
+ fileEnDecryptLoadingImage.setImage(null);
+ removeFileFromFileBox.setDisable(false);
+ limitNumberOfThreads.setDisable(false);
+ });
+ }
+ removeFileEnDecryptThread(Thread.currentThread());
+ totalThreads.getAndDecrement();
+ });
+ thread.setDaemon(false);
+ thread.start();
+ if (!fileEnDecryptLoading) {
+ fileEnDecryptLoadingImage.setImage(loadingImage);
+ }
+ fileEnDecryptLoading = true;
+ }
+ }
+ }
+
+ /**
+ * Cancels the file en- / decryption.
+ * Get called if the file en- / decrypt 'Cancel' button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileEnDecryptCancelButton() {
+ for (Iterator iterator = getFileEnDecryptThreads().iterator(); iterator.hasNext();) {
+ Thread thread = iterator.next();
+ while (thread.isAlive() && !thread.isInterrupted()) {
+ thread.stop();
+ thread.interrupt();
+ }
+ iterator.remove();
+ }
+ fileEnDecryptLoading = false;
+ fileEnDecryptLoadingImage.setImage(null);
+ removeFileFromFileBox.setDisable(false);
+ limitNumberOfThreads.setDisable(false);
+ }
+
+ //-----fileDelete-----//
+
+ /**
+ * Synchronized method to get the list of threads which delete files
+ *
+ * @return list of threads which delete files
+ *
+ * @since 1.2.0
+ */
+ private synchronized List getFileDeleteThreads() {
+ return fileDeleteThreads;
+ }
+
+ /**
+ * Synchronized method to get the number of threads which delete files
+ *
+ * @return number of threads which delete files
+ *
+ * @since 1.2.0
+ */
+ private synchronized int getFileDeleteThreadsSize() {
+ return fileDeleteThreads.size();
+ }
+
+ /**
+ * Synchronized method to add a thread to the file delete list of current running file delete threads
+ *
+ * @param thread that should be added
+ *
+ * @since 1.2.0
+ */
+ private synchronized void addFileDeleteThread(Thread thread) {
+ fileDeleteThreads.add(thread);
+ }
+
+ /**
+ * Synchronized method to remove a thread from the file delete list of current file delete threads
+ *
+ * @param thread that should be removed
+ *
+ * @since 1.2.0
+ */
+ private synchronized void removeFileDeleteThread(Thread thread) {
+ fileDeleteThreads.remove(thread);
+ }
+
+ /**
+ * Adds a file that should be deleted
+ *
+ * @param file that should be added
+ *
+ * @since 1.2.0
+ */
+ private void fileDeleteAddFile(File file) {
+ for (File f: deleteInputFiles.values()) {
+ if (f.getAbsolutePath().equals(file.getAbsolutePath())) {
+ return;
+ }
+ }
+ Label newLabel = new Label(file.getAbsolutePath());
+ newLabel.setOnKeyTyped(this::keyTypedTooltip);
+ newLabel.setOnMouseMoved(this::mouseOverEntryTooltip);
+ newLabel.setOnMouseExited(event -> mouseExitEntryTooltip());
+ newLabel.setOnMouseClicked(event -> fileDeleteSelected(newLabel));
+ newLabel.setContextMenu(fileDeleteInputContextMenu);
+ fileDeleteInputFiles.getChildren().add(newLabel);
+ deleteInputFiles.put(newLabel, file.getAbsoluteFile());
+ }
+
+ /**
+ * Changes the highlight of the clicked item in the file delete box.
+ * Get called if the user click an non-highlighted item in the file delete box
+ *
+ * @param changeLabel is the label that the user has clicked
+ *
+ * @since 1.2.0
+ */
+ private void fileDeleteSelected(Label changeLabel) {
+ if (changeLabel != null) {
+ fileEnDecryptSelected(null);
+ deleteInputFiles.keySet().forEach(label -> label.setStyle(null));
+ changeLabel.setStyle("-fx-background-color: lightblue; -fx-border-color: #292929");
+ choosedLabel = changeLabel;
+ choosedLabelType = "DELETE";
+ } else {
+ deleteInputFiles.keySet().forEach(label -> label.setStyle(null));
+ }
+ }
+
+ /**
+ * Deletes an entry for file delete.
+ * Get called if the user presses 'del' or delete the entry in the file delete box via the right click tooltip
+ *
+ * @param label that should be deleted
+ *
+ * @since 1.12.0
+ */
+ private void fileDeleteDeleteEntry(Label label) {
+ deleteInputFiles.remove(choosedLabel);
+ if (fileDeleteInputFiles.getChildren().size() - 1 >= 1) {
+ for (int i=0; iOpens a file chooser GUI where the user can select the files that should be en- / decrypted.
+ * Get called if the 'Choose files...' in the delete section button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileDeleteChooseFiles() {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Choose files");
+ fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All Files", "*.*"));
+ List files = fileChooser.showOpenMultipleDialog(rootWindow.getScene().getWindow());
+ try {
+ if (files.size() > 0) {
+ files.forEach(this::fileDeleteAddFile);
+ }
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Opens a directory chooser GUI where the user can select the directories that should be en- / decrypted.
+ * Get called if the 'Choose directories...' in the delete section button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileDeleteChooseDirectories() {
+ DirectoryChooser directoryChooser = new DirectoryChooser();
+ directoryChooser.setTitle("Choose directories");
+ File file = directoryChooser.showDialog(rootWindow.getScene().getWindow());
+ try {
+ fileDeleteAddFile(file);
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Get called if user drags a file over the delete file box
+ *
+ * @param event source
+ *
+ * @since 1.2.0
+ */
+ public void onFileDeleteDragOver(DragEvent event) {
+ Dragboard dragboard = event.getDragboard();
+ if (event.getGestureSource() != fileDeleteInputFiles && dragboard.hasFiles()) {
+ event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
+ }
+ }
+
+ /**
+ * Get called if the user drops the dragged file over the delete file box
+ *
+ * @param event source
+ *
+ * @since 1.2.0
+ */
+ public void onFileDeleteDragNDrop(DragEvent event) {
+ Dragboard dragboard = event.getDragboard();
+ if (dragboard.hasFiles()) {
+ dragboard.getFiles().forEach(file -> {
+ if (file.isFile() || file.isDirectory()) {
+ fileDeleteAddFile(file);
+ }
+ });
+ }
+ }
+
+ /**
+ * Delete all given files.
+ * Get called if 'Delete' button is pressed
+ *
+ * @since 1.2.0
+ */
+ public void fileDelete() {
+ if (!fileDeleteLoading && !deleteInputFiles.isEmpty()) {
+ fileDeleteLoadingImage.setImage(loadingImage);
+ }
+ int deleteIterations = Integer.parseInt(fileDeleteIterationsEntry.getText());
+ for (Map.Entry map : deleteInputFiles.entrySet()) {
+ Label label = map.getKey();
+ File file = map.getValue();
+ Thread thread = new Thread(() -> {
+ addFileDeleteThread(Thread.currentThread());
+ if (limitNumberOfThreads.isSelected()) {
+ while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().stop();
+ }
+ }
+ }
+ totalThreads.getAndIncrement();
+ try {
+ if (file.isFile()) {
+ SecureDelete.deleteFile(file, deleteIterations, buffer);
+ } else if (file.isDirectory()) {
+ SecureDelete.deleteDirectory(file.getAbsolutePath(), deleteIterations, buffer);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if ((getFileDeleteThreadsSize() - 1) <= 0) {
+ fileDeleteLoading = false;
+ Platform.runLater(() -> fileDeleteLoadingImage.setImage(null));
+ }
+ if (label == choosedLabel) {
+ choosedLabel = null;
+ choosedLabelType = null;
+ }
+ Platform.runLater(() -> fileDeleteInputFiles.getChildren().remove(label));
+ removeFileDeleteThread(Thread.currentThread());
+ totalThreads.getAndDecrement();
+ });
+ thread.setDaemon(false);
+ thread.start();
+ fileDeleteLoading = true;
+ }
+ }
+
+ /**
+ * Cancels the file en- / decryption.
+ * Get called if the file delete 'Cancel' button is pressed
+ *
+ * @since 1.12.0
+ */
+ public void fileDeleteCancelButton() {
+ for (Iterator iterator = getFileDeleteThreads().iterator(); iterator.hasNext();) {
+ Thread thread = iterator.next();
+ while (thread.isAlive() & !thread.isInterrupted()) {
+ thread.stop();
+ thread.interrupt();
+ }
+ iterator.remove();
+ }
+ fileDeleteLoading = false;
+ fileDeleteLoadingImage.setImage(null);
+ }
+
+ /**
+ * Called to initialize a controller after its root element has been
+ * completely processed.
+ *
+ * @param location
+ * The location used to resolve relative paths for the root object, or
+ * null if the location is not known.
+ *
+ * @param resources
+ * The resources used to localize the root object, or null if
+ * the root object was not localized.
+ *
+ * @since 1.0.0
+ */
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+
+ //-----general-----//
+
+ currentConfigSettings.put("textKey", configDefaultTextKey);
+ currentConfigSettings.put("textSalt", configDefaultTextSalt);
+ currentConfigSettings.put("textAlgorithm", configDefaultTextAlgorithm);
+
+ currentConfigSettings.put("fileEnDecryptKey", configDefaultFileEnDecryptKey);
+ currentConfigSettings.put("fileEnDecryptSalt", configDefaultFileEnDecryptSalt);
+ currentConfigSettings.put("fileEnDecryptAlgorithm", configDefaultFileEnDecryptAlgorithm);
+
+ currentConfigSettings.put("fileDeleteIterations", String.valueOf(configDefaultFileDeleteIterations));
+
+ currentConfigSettings.put("fileOutputPath", configDefaultFileOutputPath);
+ currentConfigSettings.put("removeFromFileBox", String.valueOf(configDefaultRemoveFileFromFileBox));
+ currentConfigSettings.put("limitNumberOfThreads", String.valueOf(configDefaultLimitNumberOfThreads));
+
+ menubar.setOnMouseDragged(event -> {
+ Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
+ stage.setX(event.getScreenX() + menubarX);
+ stage.setY(event.getScreenY() + menubarY);
+ });
+ menubar.setOnMousePressed(event -> {
+ Scene scene = ((Node) event.getSource()).getScene();
+ menubarX = scene.getX() - event.getSceneX();
+ menubarY = scene.getY() - event.getSceneY();
+ });
+
+ rootWindow.setOnKeyReleased(event -> {
+ if (event.getCode() == KeyCode.DELETE && choosedLabelType != null) {
+ if (choosedLabelType.equals("ENDECRYPT")) {
+ fileEnDecryptDeleteEntry(choosedLabel);
+ } else if (choosedLabelType.equals("DELETE")) {
+ fileDeleteDeleteEntry(choosedLabel);
+ }
+ }
+ });
+
+ getChoosedLabelInputFileFolder.setOnAction(event -> {
+ Desktop desktop = Desktop.getDesktop();
+ String filePath = choosedLabel.getText();
+ try {
+ desktop.open(new File(filePath.substring(0, filePath.lastIndexOf(System.getProperty("file.separator")))));
+ } catch (IOException e) {
+ errorAlert("An unexpected IO Exception occurred", e.getMessage());
+ }
+ });
+
+ getChoosedLabelOutputFileFolder.setOnAction(event -> {
+ Desktop desktop;
+ String filePath;
+ if (enDecryptInputOutputFiles.containsKey(choosedLabel)) {
+ desktop = Desktop.getDesktop();
+ filePath = enDecryptInputOutputFiles.get(choosedLabel).get(0).getAbsolutePath();
+ } else {
+ return;
+ }
+ try {
+ desktop.open(new File(filePath.substring(0, filePath.lastIndexOf(System.getProperty("file.separator")))));
+ } catch (IOException e) {
+ errorAlert("An unexpected IO Exception occurred", e.getMessage());
+ }
+ });
+
+ setDefaultOutputPath.setOnAction(event -> {
+ DirectoryChooser directoryChooser = new DirectoryChooser();
+ File directory = directoryChooser.showDialog(rootWindow.getScene().getWindow());
+ try {
+ currentConfigSettings.replace("fileOutputPath", directory.getAbsolutePath());
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ });
+
+ settingsMenu.setOnShowing(event -> {
+ loadSettings.setDisable(!isConfig);
+ exportSettings.setDisable(!isConfig);
+ });
+
+ removeFileFromFileBox.setOnAction(event -> currentConfigSettings.replace("removeFromFileBox", String.valueOf(removeFileFromFileBox.isSelected())));
+ limitNumberOfThreads.setOnAction(event -> currentConfigSettings.replace("limitNumberOfThreads", String.valueOf(limitNumberOfThreads.isSelected())));
+ saveSettings.setOnAction(event -> {
+ try {
+ addSettingGUI(rootWindow.getScene().getWindow(), currentConfigSettings);
+ if (config.isFile()) {
+ isConfig = true;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ loadSettings.setOnAction(event -> {
+ try {
+ currentConfigSettings = (HashMap) loadSettingsGUI(rootWindow.getScene().getWindow()).values().toArray()[0];
+ textKeyEntry.setText(currentConfigSettings.get("textKey"));
+ textSaltEntry.setText(currentConfigSettings.get("textSalt"));
+ textAlgorithmBox.setValue(currentConfigSettings.get("textAlgorithm"));
+
+ fileEnDecryptKeyEntry.setText(currentConfigSettings.get("fileEnDecryptKey"));
+ fileEnDecryptSaltEntry.setText(currentConfigSettings.get("fileEnDecryptSalt"));
+ fileEnDecryptAlgorithmBox.setValue(currentConfigSettings.get("fileEnDecryptAlgorithm"));
+
+ fileDeleteIterationsEntry.setText(currentConfigSettings.get("fileDeleteIterations"));
+
+ removeFileFromFileBox.setSelected(Boolean.parseBoolean(currentConfigSettings.get("removeFromFileBox")));
+ limitNumberOfThreads.setSelected(Boolean.parseBoolean(currentConfigSettings.get("limitNumberOfThreads")));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ try {
+ SecureDelete.deleteFile(config, 5, buffer);
+ isConfig = false;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ exportSettings.setOnAction(event -> {
+ try {
+ exportSettingsGUI(rootWindow.getScene().getWindow());
+ } catch (IOException e) {
+ e.printStackTrace();
+ errorAlert("IO Exception occurred", e.getMessage());
+ }
+ });
+ importSettings.setOnAction(event -> {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Import settings");
+ fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Config files", "*.config*", "*.xml"), new FileChooser.ExtensionFilter("All files", "*.*"));
+ File file = fileChooser.showOpenDialog(rootWindow.getScene().getWindow());
+ if (file != null) {
+ if (isConfig) {
+ writeSettings(config, readSettings(file));
+ } else {
+ writeSettings(config, readSettings(file));
+ isConfig = true;
+ }
+ }
+ });
+
+ //-----text------//
+
+ textAlgorithmBox.setItems(FXCollections.observableArrayList(Utils.algorithms.keySet()));
+ textAlgorithmBox.setValue(Utils.algorithms.keySet().toArray(new String[Utils.algorithms.size()])[0]);
+
+ //-----fileEnDecrypt-----//
+
+ fileEnDecryptAlgorithmBox.setItems(FXCollections.observableArrayList(Utils.algorithms.keySet()));
+ fileEnDecryptAlgorithmBox.setValue(Utils.algorithms.keySet().toArray(new String[Utils.algorithms.size()])[0]);
+
+ MenuItem enDecryptRemove = new MenuItem();
+ enDecryptRemove.setText("Remove");
+ enDecryptRemove.setOnAction(removeEvent -> fileEnDecryptDeleteEntry(choosedLabel));
+ MenuItem enDecryptChangeDest = new MenuItem();
+ enDecryptChangeDest.setText("Change output file / directory");
+ enDecryptChangeDest.setOnAction(outputFileChangeEvent -> {
+ File file;
+ if (new File(choosedLabel.getText()).isFile()) {
+ FileChooser destChooser = new FileChooser();
+ destChooser.setTitle("Choose or create new file");
+ destChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*"));
+ file = destChooser.showSaveDialog(rootWindow.getScene().getWindow());
+ } else {
+ DirectoryChooser destChooser = new DirectoryChooser();
+ destChooser.setTitle("Choose or create new directory");
+ file = destChooser.showDialog(rootWindow.getScene().getWindow());
+ }
+ if (file != null) {
+ for (Map.Entry> entry : enDecryptInputOutputFiles.entrySet()) {
+ if (entry.getKey().getText().equals(choosedLabel.getText())) {
+ ArrayList changedFile = new ArrayList<>();
+ changedFile.add(0, file);
+ changedFile.add(1, file);
+ enDecryptInputOutputFiles.replace(entry.getKey(), entry.getValue(), changedFile);
+ fileOutputFilesChangeText((Label) fileEnDecryptLabelEvent.getSource(), file.getAbsolutePath(), file.getAbsolutePath());
+ break;
+ }
+ }
+ }
+ });
+ fileEnDecryptInputContextMenu.getItems().addAll(enDecryptRemove, enDecryptChangeDest);
+
+ ContextMenu fileEnDecryptInputFilesMenu = new ContextMenu();
+ MenuItem enDecryptPaste = new MenuItem();
+ enDecryptPaste.setText("Paste");
+ enDecryptPaste.setOnAction(pasteEvent -> {
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ Transferable transferable = clipboard.getContents(null);
+ try {
+ if (transferable != null) {
+ if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
+ Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor);
+ List files = (List) objectFileList;
+ files.forEach(o -> fileEnDecryptAddFile((File) o));
+ } else if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
+ Object objectImage = transferable.getTransferData(DataFlavor.imageFlavor);
+ fileEnDecryptAddClipboardImage((BufferedImage) objectImage);
+ }
+ }
+ } catch (UnsupportedFlavorException | IOException | URISyntaxException e) {
+ e.printStackTrace();
+ }
+ });
+ fileEnDecryptInputFilesMenu.getItems().add(enDecryptPaste);
+
+ fileEnDecryptInputFiles.setOnContextMenuRequested(event -> {
+ if (!fileEnDecryptInputContextMenu.isShowing()) {
+ fileEnDecryptInputFilesMenu.show(((VBox) event.getSource()).getParent().getScene().getWindow(), event.getScreenX(), event.getScreenY());
+ }
+ });
+
+ fileOutputFileChangeDest.setOnAction(event -> {
+ FileChooser fileDestChooser = new FileChooser();
+ fileDestChooser.setTitle("Choose or create new file");
+ fileDestChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*"));
+ File file = fileDestChooser.showSaveDialog(rootWindow.getScene().getWindow());
+ if (file != null) {
+ for (Map.Entry> entry : enDecryptInputOutputFiles.entrySet()) {
+ if (entry.getKey().getText().equals(choosedLabel.getText())) {
+ ArrayList changedFile = new ArrayList<>();
+ changedFile.add(0, file);
+ changedFile.add(1, file);
+ enDecryptInputOutputFiles.replace(entry.getKey(), entry.getValue(), changedFile);
+ fileOutputFilesChangeText((Label) fileEnDecryptLabelEvent.getSource(), file.getAbsolutePath(), file.getAbsolutePath());
+ break;
+ }
+ }
+ }
+ });
+
+ fileOutputFileChangeDest.setDisable(true);
+ getChoosedLabelOutputFileFolder.setDisable(true);
+
+ fileEncryptOutputFile.textProperty().addListener((observable, oldValue, newValue) -> fileOutputFilesChangeText(choosedLabel, newValue, fileDecryptOutputFile.getText()));
+ fileDecryptOutputFile.textProperty().addListener((observable, oldValue, newValue) -> fileOutputFilesChangeText(choosedLabel, fileEncryptOutputFile.getText(), newValue));
+
+ //-----fileDelete-----//
+
+ MenuItem deleteRemove = new MenuItem();
+ deleteRemove.setText("Remove");
+ deleteRemove.setOnAction(removeEvent -> fileDeleteDeleteEntry(choosedLabel));
+ fileDeleteInputContextMenu.getItems().add(deleteRemove);
+
+ ContextMenu fileDeleteInputFilesMenu = new ContextMenu();
+ MenuItem deletePaste = new MenuItem();
+ deletePaste.setText("Paste");
+ deletePaste.setOnAction(pasteEvent -> {
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ Transferable transferable = clipboard.getContents(null);
+ try {
+ if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
+ Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor);
+ List files = (List) objectFileList;
+ files.forEach(o -> fileDeleteAddFile((File) o));
+ }
+ } catch (UnsupportedFlavorException | IOException e) {
+ e.printStackTrace();
+ }
+ });
+ fileDeleteInputFilesMenu.getItems().add(deletePaste);
+
+ fileDeleteInputFiles.setOnContextMenuRequested(event -> {
+ if (!fileDeleteInputContextMenu.isShowing()) {
+ fileDeleteInputFilesMenu.show(((VBox) event.getSource()).getParent().getScene().getWindow(), event.getScreenX(), event.getScreenY());
+ }
+ });
+
+ fileDeleteIterationsEntry.textProperty().addListener((observable, oldValue, newValue) -> {
+ if (!newValue.matches("[0-9]*")) {
+ fileDeleteIterationsEntry.setText(oldValue);
+ }
+ });
+
+ Thread t = new Thread(() -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (isConfig) {
+ Platform.runLater(() -> {
+ try {
+ currentConfigSettings = (HashMap) loadSettingsGUI(rootWindow.getScene().getWindow()).values().toArray()[0];
+ textKeyEntry.setText(currentConfigSettings.get("textKey"));
+ textSaltEntry.setText(currentConfigSettings.get("textSalt"));
+ textAlgorithmBox.setValue(currentConfigSettings.get("textAlgorithm"));
+
+ fileEnDecryptKeyEntry.setText(currentConfigSettings.get("fileEnDecryptKey"));
+ fileEnDecryptSaltEntry.setText(currentConfigSettings.get("fileEnDecryptSalt"));
+ fileEnDecryptAlgorithmBox.setValue(currentConfigSettings.get("fileEnDecryptAlgorithm"));
+
+ fileDeleteIterationsEntry.setText(currentConfigSettings.get("fileDeleteIterations"));
+
+ removeFileFromFileBox.setSelected(Boolean.parseBoolean(currentConfigSettings.get("removeFromFileBox")));
+ limitNumberOfThreads.setSelected(Boolean.parseBoolean(currentConfigSettings.get("limitNumberOfThreads")));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ try {
+ SecureDelete.deleteFile(config, 5, buffer);
+ isConfig = false;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ });
+ t.start();
+ }
+}
diff --git a/src/org/bytedream/cryptogx/EnDecrypt.java b/src/org/bytedream/cryptogx/EnDecrypt.java
new file mode 100644
index 0000000..bf7fbd9
--- /dev/null
+++ b/src/org/bytedream/cryptogx/EnDecrypt.java
@@ -0,0 +1,302 @@
+package org.bytedream.cryptogx;
+
+import javax.crypto.*;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class for en- / decrypt text and files
+ *
+ * @since 1.0.0
+ */
+public class EnDecrypt {
+
+ public static class AES extends Thread {
+
+ public int iterations = 65536;
+
+ private final String secretKeyFactoryAlgorithm = "PBKDF2WithHmacSHA1";
+ private int keySize = 256;
+
+ private final String key;
+ private final byte[] salt;
+
+ public AES(String key, byte[] salt) {
+ this.key = key;
+ this.salt = salt;
+ }
+
+ public AES(String key, byte[] salt, int keySize) {
+ this.key = key;
+ this.salt = salt;
+ this.keySize = keySize;
+ }
+
+ public AES(String key, byte[] salt, int iterations, int keySize) {
+ this.key = key;
+ this.salt = salt;
+ this.iterations = iterations;
+ this.keySize = keySize;
+ }
+
+ /**
+ * Creates a secret key from given (plain text) key and salt
+ *
+ * @return the secret key
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ *
+ * @since 1.0.0
+ */
+ private byte[] createSecretKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKeyFactoryAlgorithm);
+ PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), salt, iterations, keySize);
+
+ return factory.generateSecret(keySpec).getEncoded();
+ }
+
+ /**
+ * Writes {@param inputStream} to {@param outputStream}
+ *
+ * @param inputStream from which is written
+ * @param outputStream to which is written
+ * @param buffer
+ * @throws IOException
+ *
+ * @since 1.12.0
+ */
+ private void write(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException {
+ int numOfBytesRead;
+ while ((numOfBytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, numOfBytesRead);
+ }
+ outputStream.close();
+ inputStream.close();
+ }
+
+ /**
+ * Encrypts the {@param inputStream} to {@param outputStream}
+ *
+ * @param inputStream that should be encrypted
+ * @param outputStream to which the encrypted {@param inputFile} should be written to
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ *
+ * @since 1.12.0
+ */
+ public void encryptFile(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
+ Key secretKey = new SecretKeySpec(createSecretKey(), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
+ write(cipherInputStream, outputStream, buffer);
+ }
+
+ /**
+ * Encrypts all files in the {@param inputDirectory} to the {@param outputDirectory}
+ *
+ * @param inputDirectory that should be encrypted
+ * @param outputDirectory to which the encrypted {@param inputDirectory} files should be written to
+ * @param fileEnding get added to every file that gets encrypted (if the {@param fileEnding} starts and ends with
+ * a '@', the {@param fileEnding} will get removed from the file if it exists)
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ *
+ * @since 1.12.0
+ */
+ public void encryptDirectory(String inputDirectory, String outputDirectory, String fileEnding, byte[] buffer) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException {
+ AtomicBoolean remove = new AtomicBoolean(false);
+
+ if (fileEnding == null) {
+ fileEnding = "";
+ } else if (fileEnding.startsWith("@") && fileEnding.endsWith("@")) {
+ fileEnding = fileEnding.substring(1, fileEnding.length() - 1);
+ remove.set(true);
+ }
+
+ HashMap files = new HashMap<>();
+ final String finalFileEnding = fileEnding;
+ Files.walk(Paths.get(inputDirectory)).map(Path::toFile).forEach(oldFile -> {
+ String oldFilePath = oldFile.getAbsolutePath();
+ if (oldFile.isDirectory()) {
+ new File(oldFilePath.replace(inputDirectory, outputDirectory + "/")).mkdir();
+ }else if (remove.get() && oldFilePath.endsWith(finalFileEnding)) {
+ files.put(oldFile, new File(oldFilePath.substring(0, oldFilePath.lastIndexOf(finalFileEnding))
+ .replace(inputDirectory, outputDirectory + "/") + finalFileEnding));
+ } else {
+ files.put(oldFile, new File(oldFilePath.replace(inputDirectory, outputDirectory + "/") + finalFileEnding));
+ }
+ });
+
+ File newFile;
+ for (Map.Entry entry: files.entrySet()) {
+ newFile = entry.getValue();
+ encryptFile(new FileInputStream(entry.getKey()), new FileOutputStream(newFile), buffer);
+ }
+ }
+
+ /**
+ * Decrypts the {@param inputStream} to {@param outputStream}
+ *
+ * @param inputStream that should be decrypted
+ * @param outputStream to which the decrypted {@param inputFile} should be written to
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws InvalidAlgorithmParameterException
+ *
+ * @since 1.12.0
+ */
+ public void decryptFile(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException{
+ Key secretKey = new SecretKeySpec(createSecretKey(), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);
+
+ CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
+ write(inputStream, cipherOutputStream, buffer);
+ }
+
+ /**
+ * Decrypts all files in the {@param inputDirectory} to the {@param outputDirectory}
+ *
+ * @param inputDirectory that should be decrypted
+ * @param outputDirectory to which the decrypted {@param inputDirectory} files should be written to
+ * @param fileEnding get added to every file that gets decrypted (if the {@param fileEnding} starts and ends with
+ * a '@', the {@param fileEnding} will get removed from the file if it exists)
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ *
+ * @since 1.12.0
+ */
+ public void decryptDirectory(String inputDirectory, String outputDirectory, String fileEnding, byte[] buffer) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException {
+ AtomicBoolean remove = new AtomicBoolean(false);
+
+ if (fileEnding == null) {
+ fileEnding = "";
+ } else if (fileEnding.startsWith("@") && fileEnding.endsWith("@")) {
+ fileEnding = fileEnding.substring(1, fileEnding.length() - 1);
+ remove.set(true);
+ }
+
+ HashMap files = new HashMap<>();
+ final String finalFileEnding = fileEnding;
+ Files.walk(Paths.get(inputDirectory)).map(Path::toFile).forEach(oldFile -> {
+ String oldFilePath = oldFile.getAbsolutePath();
+ if (oldFile.isDirectory()) {
+ new File(oldFilePath.replace(inputDirectory, outputDirectory + "/")).mkdir();
+ }
+ else if (remove.get() && oldFilePath.endsWith(finalFileEnding)) {
+ files.put(oldFile, new File(oldFilePath.substring(0, oldFilePath.lastIndexOf(finalFileEnding))
+ .replace(inputDirectory, outputDirectory + "/") + finalFileEnding));
+ } else {
+ files.put(oldFile, new File(oldFilePath.replace(inputDirectory, outputDirectory + "/") + finalFileEnding));
+ }
+ });
+
+ File newFile;
+ for (Map.Entry entry: files.entrySet()) {
+ newFile = entry.getValue();
+ decryptFile(new FileInputStream(entry.getKey()), new FileOutputStream(newFile), buffer);
+ }
+ }
+
+ /**
+ * Encrypt {@param bytes}
+ *
+ * @param bytes that should be encrypted
+ * @return encrypted bytes
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws InvalidKeyException
+ *
+ * @since 1.0.0
+ */
+ public byte[] encrypt(byte[] bytes) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ Key secretKey = new SecretKeySpec(createSecretKey(), "AES");
+
+ Cipher encryptCipher = Cipher.getInstance("AES");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ return encryptCipher.doFinal(bytes);
+ }
+
+ /**
+ * Encrypt {@param bytes}
+ *
+ * @param string that should be encrypted
+ *
+ * @see EnDecrypt.AES#encrypt(byte[])
+ *
+ * @since 1.0.0
+ */
+ public String encrypt(String string) throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
+ return Base64.getEncoder().encodeToString(encrypt(string.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ /**
+ * Decrypt encrypted {@param bytes}
+ *
+ * @param bytes that should be decrypted
+ * @return decrypted bytes
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws InvalidKeyException
+ *
+ * @since 1.12.0
+ */
+ public byte[] decrypt(byte[] bytes) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ Key secretKey = new SecretKeySpec(createSecretKey(), "AES");
+
+ Cipher decryptCipher = Cipher.getInstance("AES");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
+ return decryptCipher.doFinal(Base64.getDecoder().decode(bytes));
+ }
+
+ /**
+ * Decrypt encrypted {@param string}
+ *
+ * @param string that should be decrypted
+ *
+ * @see EnDecrypt.AES#decrypt(byte[])
+ *
+ * @since 1.0.0
+ */
+ public String decrypt(String string) throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
+ return new String(decrypt(string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
+ }
+
+ }
+}
diff --git a/src/org/bytedream/cryptogx/Main.java b/src/org/bytedream/cryptogx/Main.java
new file mode 100644
index 0000000..437a4a3
--- /dev/null
+++ b/src/org/bytedream/cryptogx/Main.java
@@ -0,0 +1,326 @@
+/*
+ * @author bytedream
+ * @version 1.12.0
+ *
+ * Some @since
versions may be not correct, because the @since
tag got added in
+ * version 1.12.0 and I don't have all versions (1.0.0 - 1.11.0), so I cannot see when some methods were added
+
+ */
+
+package org.bytedream.cryptogx;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.image.Image;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.swing.*;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Main class
+ *
+ * @since 1.0.0
+ */
+public class Main extends Application {
+
+ protected static final int NON_PORTABLE = 1;
+ protected static final int PORTABLE = 0;
+
+ protected static final int TYPE = NON_PORTABLE;
+
+ protected final static String configDefaultTextKey = "";
+ protected final static String configDefaultTextSalt = "";
+ protected final static String configDefaultTextAlgorithm = "AES-128";
+ protected final static String configDefaultFileEnDecryptKey = "";
+ protected final static String configDefaultFileEnDecryptSalt = "";
+ protected final static String configDefaultFileEnDecryptAlgorithm = "AES-128";
+ protected final static int configDefaultFileDeleteIterations = 5;
+ protected final static String configDefaultFileOutputPath = "";
+ protected final static boolean configDefaultRemoveFileFromFileBox = false;
+ protected final static boolean configDefaultLimitNumberOfThreads = true;
+
+ private final static byte[] buffer = new byte[64];
+
+ private static Stage mainStage;
+ private double rootWindowX, rootWindowY;
+ protected static File config;
+ protected static boolean isConfig;
+
+ /**
+ * Start the GUI
+ *
+ * @param primaryStage of the GUI
+ * @throws IOException if issues with loading 'mainGUI.fxml'
+ *
+ * @since 1.0.0
+ */
+ @Override
+ public void start(Stage primaryStage) throws IOException {
+ Thread.setDefaultUncaughtExceptionHandler(Main::exceptionAlert);
+
+ mainStage = primaryStage;
+
+ Parent root = FXMLLoader.load(getClass().getResource("resources/mainGUI.fxml"));
+ primaryStage.initStyle(StageStyle.UNDECORATED);
+ primaryStage.setResizable(false);
+ primaryStage.setTitle("cryptoGX");
+ primaryStage.getIcons().add(new Image(getClass().getResource("resources/cryptoGX.png").toExternalForm()));
+ Scene scene = new Scene(root);
+ //Scene scene = new Scene(root, 900, 470);
+
+ scene.setOnMouseDragged(event -> {
+ primaryStage.setX(event.getScreenX() + rootWindowX);
+ primaryStage.setY(event.getScreenY() + rootWindowY);
+ });
+ scene.setOnMousePressed(event -> {
+ rootWindowX = scene.getX() - event.getSceneX();
+ rootWindowY = scene.getY() - event.getSceneY();
+ });
+
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ /**
+ * Enter method for the application.
+ * Can also be used to en- / decrypt text and files or secure delete files without starting GUI
+ *
+ * @param args from the command line
+ * @return
+ * @throws BadPaddingException
+ * @throws NoSuchAlgorithmException if wrong algorithm is given (command line)
+ * @throws IllegalBlockSizeException if wrong size for key is given (command line)
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException if invalid key is given (command line)
+ * @throws InvalidKeySpecException
+ * @throws IOException if files cannot be en- / decrypted or deleted correctly (command line)
+ * @throws InvalidAlgorithmParameterException if wrong algorithm parameters are given (command line)
+ *
+ * @since 1.0.0
+ */
+ public static void main(String[] args) throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IOException, InvalidAlgorithmParameterException {
+ if (Main.TYPE == Main.PORTABLE) {
+ String system = System.getProperty("os.name").toLowerCase();
+ if (system.startsWith("windows")) {
+ config = new File("C:\\Users\\" + System.getProperty("user.name") + "\\AppData\\Roaming\\cryptoGX\\cryptoGX.config");
+ File directory = new File("C:\\Users\\" + System.getProperty("user.name") + "\\AppData\\Roaming\\cryptoGX");
+ if (!directory.isDirectory()) {
+ directory.mkdir();
+ }
+ } else if (system.startsWith("linux")) {
+ config = new File(System.getProperty("user.home") + "/.cryptoGX/cryptoGX.config");
+ File directory = new File(System.getProperty("user.home") + "/.cryptoGX/");
+ if (!directory.isDirectory()) {
+ directory.mkdir();
+ }
+ } else {
+ config = new File("cryptoGX.config");
+ }
+ } else {
+ config = new File("cryptoGX.config");
+ }
+ isConfig = config.isFile();
+ if (args.length == 0) {
+ launch(args);
+ } else {
+ args[0] = args[0].replace("-", "");
+ if (args[0].toLowerCase().equals("help") || args[0].toUpperCase().equals("H")) {
+ System.out.println("Usage AES: \n\n" +
+ " Text en- / decryption\n" +
+ " encrypt: AES encrypt \n" +
+ " decrypt: AES decrypt \n\n" +
+ " File en- / decryption\n" +
+ " encrypt: AES encrypt \n" +
+ " decrypt: AES decrypt \n\n" +
+ "File secure delete: delete "); //for the argument 'default' can be used, which is 5
+ } else if (args[0].toLowerCase().equals("delete")) {
+ if (args.length > 3) {
+ System.err.println("To many arguments were given, expected 3");
+ } else if (args.length < 3) {
+ System.err.println("To few arguments were given, expected 3");
+ }
+ try {
+ if (args[1].equals("default")) {
+ args[1] = "5";
+ }
+ File deleteFile = new File(args[2]);
+ if (deleteFile.isFile()) {
+ SecureDelete.deleteFile(deleteFile, Integer.parseInt(args[1]), buffer);
+ } else if (deleteFile.isDirectory()) {
+ SecureDelete.deleteDirectory(args[2], Integer.parseInt(args[1]), buffer);
+ } else {
+ System.err.println("Couldn't find file " + args[4]);
+ System.exit(1);
+ }
+ } catch (NumberFormatException e) {
+ System.err.println(args[1] + " must be a number\n Error: " + e.getMessage());
+ }
+ } else if (args[0].toLowerCase().equals("aes")) {
+ if (args.length < 5) {
+ System.err.println("To few arguments were given");
+ System.exit(1);
+ } else if (args.length > 6) {
+ System.err.println("To many arguments were given");
+ System.exit(1);
+ }
+ EnDecrypt.AES aes;
+ if (args[2].isEmpty()) {
+ aes = new EnDecrypt.AES(args[1], new byte[16]);
+ } else {
+ aes = new EnDecrypt.AES(args[1], args[2].getBytes(StandardCharsets.UTF_8));
+ }
+ String type = args[3].toLowerCase();
+ if (args.length == 5) {
+ if (type.equals("encrypt")) {
+ System.out.println(Base64.getEncoder().encodeToString(aes.encrypt(args[4].getBytes(StandardCharsets.UTF_8))));
+ } else if (type.equals("decrypt")) {
+ System.out.println(aes.decrypt(args[4]));
+ } else {
+ System.err.println("Couldn't resolve argument " + args[3] + ", expected 'encrypt' or 'decrypt'");
+ System.exit(1);
+ }
+ } else {
+ if (type.equals("encrypt")) {
+ File inputFile = new File(args[4]);
+ if (inputFile.isFile()) {
+ aes.encryptFile(new FileInputStream(inputFile), new FileOutputStream(args[5]), new byte[64]);
+ } else if (inputFile.isDirectory()) {
+ aes.encryptDirectory(args[4], args[5], ".cryptoGX", new byte[64]);
+ } else {
+ System.err.println("Couldn't find file " + args[4]);
+ System.exit(1);
+ }
+ } else if (type.equals("decrypt")) {
+ File inputFile = new File(args[4]);
+ if (inputFile.isFile()) {
+ aes.decryptFile(new FileInputStream(inputFile), new FileOutputStream(args[5]), new byte[64]);
+ } else if (inputFile.isDirectory()) {
+ aes.decryptDirectory(args[4], args[5], "@.cryptoGX@", new byte[64]);
+ } else {
+ System.err.println("Couldn't find file " + args[4]);
+ System.exit(1);
+ }
+ } else {
+ System.err.println("Couldn't resolve argument " + args[3] + ", expected 'encrypt' or 'decrypt'");
+ System.exit(1);
+ }
+ }
+ System.exit(0);
+ }
+ }
+ }
+
+ /**
+ * "Catch" all uncatched exceptions and opens an alert window
+ *
+ * @param thread which called this method
+ * @param throwable of the thread which called the method
+ *
+ * @since 1.3.0
+ */
+ private static void exceptionAlert(Thread thread, Throwable throwable) {
+ throwable.printStackTrace();
+
+ AtomicReference exceptionAlertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference exceptionAlertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.ERROR, "Error: " + throwable, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ enDecryptError.setResizable(true);
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+ enDecryptError.getDialogPane().setContent(new Label("Error: " + throwable));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + exceptionAlertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + exceptionAlertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ exceptionAlertX.set(window.getX() - pressEvent.getSceneX());
+ exceptionAlertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+
+ /**
+ * Shows an error alert window
+ *
+ * @param message which will the alert show
+ * @param error which will show after the message
+ */
+ protected static void errorAlert(String message, String error) {
+ AtomicReference alertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference alertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.ERROR, message +
+ "\nError: " + error, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ enDecryptError.setResizable(true);
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+ enDecryptError.getDialogPane().setContent(new Label(message));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + alertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + alertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ alertX.set(window.getX() - pressEvent.getSceneX());
+ alertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+
+ /**
+ * Shows an warning alert window
+ *
+ * @param message that the alert window will show
+ *
+ * @since 1.4.0
+ */
+ protected static void warningAlert(String message) {
+ AtomicReference alertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference alertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.WARNING, message, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+ enDecryptError.getDialogPane().setContent(new Label(message));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + alertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + alertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ alertX.set(window.getX() - pressEvent.getSceneX());
+ alertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+}
diff --git a/src/org/bytedream/cryptogx/SecureDelete.java b/src/org/bytedream/cryptogx/SecureDelete.java
new file mode 100644
index 0000000..0d1ee05
--- /dev/null
+++ b/src/org/bytedream/cryptogx/SecureDelete.java
@@ -0,0 +1,90 @@
+package org.bytedream.cryptogx;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.TreeSet;
+
+/**
+ * Class for secure delete files
+ *
+ * @since 1.2.0
+ */
+public class SecureDelete {
+
+ public static void deleteDirectory(String directory, int iterations, byte[] buffer) throws IOException {
+ TreeSet directories = new TreeSet<>();
+ Files.walk(Paths.get(directory)).map(Path::toFile).forEach(directoryFile -> {
+ if (directoryFile.isDirectory()) {
+ directories.add(directoryFile);
+ } else {
+ try {
+ SecureDelete.deleteFile(directoryFile, iterations, buffer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ while (directoryFile.exists()) {
+ if (directoryFile.delete()) {
+ break;
+ }
+ }
+ }
+ });
+
+ File deleteDirectory = directories.last();
+ while (deleteDirectory != null) {
+ deleteDirectory.delete();
+
+ while (deleteDirectory.delete()) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ deleteDirectory = directories.lower(deleteDirectory);
+ }
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times line by line with random bytes (minimal size {@param minFileSize}; maximal size {@param maxFileSize}) and delete it
+ *
+ * @param file that should be deleted
+ * @param iterations how many times the file should be overwritten before it gets deleted
+ * @return if the file could be deleted
+ * @throws IOException
+ *
+ * @since 1.12.0
+ */
+ public static void deleteFile(File file, int iterations, byte[] buffer) throws IOException {
+ SecureRandom secureRandom = new SecureRandom();
+ RandomAccessFile raf = new RandomAccessFile(file, "rws");
+ for (int i=0; iClass for the user configuration / settings
+ *
+ * @since 1.12.0
+ */
+public class Settings {
+
+ private static double addSettingsGUIX, addSettingsGUIY;
+
+ private static final HashSet protectedSettingsNames = new HashSet<>(Arrays.asList("cryptoGX", "settings"));
+
+ /**
+ * Shows a GUI where the user can save settings, which can load later
+ *
+ * @param rootWindow from which this GUI will get called
+ * @param userSetting
+ * @throws IOException
+ *
+ * @since 1.11.0
+ */
+ public static void addSettingGUI(Window rootWindow, Map userSetting) throws IOException {
+ Map newSettingItems = new HashMap<>();
+
+ Stage rootStage = new Stage();
+ rootStage.initOwner(rootWindow);
+ Parent addSettingsRoot = FXMLLoader.load(Settings.class.getResource("resources/addSettingsGUI.fxml"));
+ rootStage.initStyle(StageStyle.UNDECORATED);
+ rootStage.initModality(Modality.WINDOW_MODAL);
+ rootStage.setResizable(false);
+ rootStage.setTitle("cryptoGX");
+ Scene scene = new Scene(addSettingsRoot, 320, 605);
+
+ rootStage.setScene(scene);
+
+ scene.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ scene.setOnMousePressed(event -> {
+ addSettingsGUIX = scene.getX() - event.getSceneX();
+ addSettingsGUIY = scene.getY() - event.getSceneY();
+ });
+
+ Thread thread = new Thread(() -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Platform.runLater(() -> {
+ MenuBar menuBar = (MenuBar) addSettingsRoot.lookup("#menuBar");
+ menuBar.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ menuBar.setOnMousePressed(event -> {
+ addSettingsGUIX = menuBar.getLayoutX() - event.getSceneX();
+ addSettingsGUIY = menuBar.getLayoutY() - event.getSceneY();
+ });
+
+ ImageView closeButton = (ImageView) addSettingsRoot.lookup("#closeButton");
+ closeButton.setOnMouseClicked(event -> rootStage.close());
+
+ TextField settingsNameEntry = (TextField) addSettingsRoot.lookup("#settingsNameEntry");
+
+ TextField textKeyEntry = (TextField) addSettingsRoot.lookup("#textKeyEntry");
+ textKeyEntry.setText(userSetting.get("textKey"));
+ TextField textSaltEntry = (TextField) addSettingsRoot.lookup("#textSaltEntry");
+ textSaltEntry.setText(userSetting.get("textSalt"));
+ ComboBox textAlgorithmBox = (ComboBox) addSettingsRoot.lookup("#textAlgorithmComboBox");
+ textAlgorithmBox.setItems(FXCollections.observableArrayList(Utils.algorithms.keySet()));
+ textAlgorithmBox.setValue(userSetting.get("textAlgorithm"));
+
+ TextField fileEnDecryptKeyEntry = (TextField) addSettingsRoot.lookup("#fileEnDecryptKeyEntry");
+ fileEnDecryptKeyEntry.setText(userSetting.get("fileEnDecryptKey"));
+ TextField fileEnDecryptSaltEntry = (TextField) addSettingsRoot.lookup("#fileEnDecryptSaltEntry");
+ fileEnDecryptSaltEntry.setText(userSetting.get("fileEnDecryptSalt"));
+ ComboBox fileEnDecryptAlgorithmBox = (ComboBox) addSettingsRoot.lookup("#fileEnDecryptAlgorithmComboBox");
+ fileEnDecryptAlgorithmBox.setItems(FXCollections.observableArrayList(Utils.algorithms.keySet()));
+ fileEnDecryptAlgorithmBox.setValue(userSetting.get("fileEnDecryptAlgorithm"));
+
+ TextField fileDeleteIterationEntry = (TextField) addSettingsRoot.lookup("#fileDeleteIterationsEntry");
+ fileDeleteIterationEntry.setText(userSetting.get("fileDeleteIterations"));
+ fileDeleteIterationEntry.textProperty().addListener((observable, oldValue, newValue) -> {
+ if (!newValue.matches("[0-9]*")) {
+ fileDeleteIterationEntry.setText(oldValue);
+ }
+ });
+
+ TextField fileOutputPathEntry = (TextField) addSettingsRoot.lookup("#fileOutputPathEntry");
+ fileOutputPathEntry.setText(userSetting.get("fileOutputPath"));
+ Button fileOutputPathButton = (Button) addSettingsRoot.lookup("#fileOutputPathButton");
+ fileOutputPathButton.setOnAction(event -> {
+ DirectoryChooser directoryChooser = new DirectoryChooser();
+ File directory = directoryChooser.showDialog(rootWindow.getScene().getWindow());
+ try {
+ fileOutputPathEntry.setText(directory.getAbsolutePath());
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ });
+ CheckBox removeFromFileBoxCheckBox = (CheckBox) addSettingsRoot.lookup("#removeFromFileBoxCheckBox");
+ removeFromFileBoxCheckBox.setSelected(Boolean.parseBoolean(userSetting.get("removeFromFileBox")));
+ CheckBox limitNumberOfThreadsCheckBox = (CheckBox) addSettingsRoot.lookup("#limitNumberOfThreadsCheckBox");
+ limitNumberOfThreadsCheckBox.setSelected(Boolean.parseBoolean(userSetting.get("limitNumberOfThreads")));
+
+ PasswordField hiddenPasswordEntry = (PasswordField) addSettingsRoot.lookup("#hiddenPasswordEntry");
+ TextField visiblePasswordEntry = (TextField) addSettingsRoot.lookup("#visiblePasswordEntry");
+ CheckBox showPassword = (CheckBox) addSettingsRoot.lookup("#showPassword");
+
+ showPassword.setOnAction(event -> {
+ if (showPassword.isSelected()) {
+ visiblePasswordEntry.setText(hiddenPasswordEntry.getText());
+ visiblePasswordEntry.setVisible(true);
+ hiddenPasswordEntry.setVisible(false);
+ } else {
+ hiddenPasswordEntry.setText(visiblePasswordEntry.getText());
+ hiddenPasswordEntry.setVisible(true);
+ visiblePasswordEntry.setVisible(false);
+ }
+ });
+ CheckBox encryptSettings = (CheckBox) addSettingsRoot.lookup("#encryptSettings");
+ encryptSettings.setOnAction(event -> {
+ if (encryptSettings.isSelected()) {
+ hiddenPasswordEntry.setDisable(false);
+ visiblePasswordEntry.setDisable(false);
+ showPassword.setDisable(false);
+ } else {
+ hiddenPasswordEntry.setDisable(true);
+ visiblePasswordEntry.setDisable(true);
+ showPassword.setDisable(true);
+ }
+ });
+ Button saveButton = (Button) addSettingsRoot.lookup("#saveButton");
+ saveButton.setOnAction(event -> {
+ if (settingsNameEntry.getText().trim().isEmpty()) {
+ warningAlert("Add a name for the setting");
+ } else if (protectedSettingsNames.contains(settingsNameEntry.getText())) {
+ warningAlert("Please choose another name for this setting");
+ } else if (settingsNameEntry.getText().trim().contains(" ")) {
+ warningAlert("Setting name must not contain free space");
+ } else if (encryptSettings.isSelected()) {
+ try {
+ EnDecrypt.AES encrypt;
+ if (!hiddenPasswordEntry.isDisabled() && !hiddenPasswordEntry.getText().trim().isEmpty()) {
+ encrypt = new EnDecrypt.AES(hiddenPasswordEntry.getText(), new byte[16]);
+ } else if (!visiblePasswordEntry.getText().trim().isEmpty()) {
+ encrypt = new EnDecrypt.AES(visiblePasswordEntry.getText(), new byte[16]);
+ } else {
+ throw new InvalidKeyException("The key must not be empty");
+ }
+
+ newSettingItems.put("encrypted", "true");
+
+ newSettingItems.put("textKey", encrypt.encrypt(textKeyEntry.getText()));
+ newSettingItems.put("textSalt", encrypt.encrypt(textSaltEntry.getText()));
+ newSettingItems.put("textAlgorithm", encrypt.encrypt(textAlgorithmBox.getSelectionModel().getSelectedItem().toString()));
+
+ newSettingItems.put("fileEnDecryptKey", encrypt.encrypt(fileEnDecryptKeyEntry.getText()));
+ newSettingItems.put("fileEnDecryptSalt", encrypt.encrypt(fileEnDecryptSaltEntry.getText()));
+ newSettingItems.put("fileEnDecryptAlgorithm", encrypt.encrypt(fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem().toString()));
+
+ newSettingItems.put("fileDeleteIterations", encrypt.encrypt(fileDeleteIterationEntry.getText()));
+
+ newSettingItems.put("fileOutputPath", encrypt.encrypt(fileOutputPathEntry.getText()));
+ newSettingItems.put("removeFromFileBox", encrypt.encrypt(String.valueOf(removeFromFileBoxCheckBox.isSelected())));
+ newSettingItems.put("limitNumberOfThreads", encrypt.encrypt(String.valueOf(limitNumberOfThreadsCheckBox.isSelected())));
+
+ if (!config.isFile()) {
+ try {
+ if (!config.createNewFile()) {
+ warningAlert("Couldn't create config file");
+ } else {
+ addSetting(config, settingsNameEntry.getText().trim(), newSettingItems);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ errorAlert("Couldn't create config file", e.getMessage());
+ }
+ } else {
+ addSetting(config, settingsNameEntry.getText().trim(), newSettingItems);
+ }
+
+ rootStage.close();
+ } catch (InvalidKeyException e) {
+ warningAlert("The key must not be empty");
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ }
+ } else {
+ newSettingItems.put("textKey", textKeyEntry.getText());
+ newSettingItems.put("textSalt", textSaltEntry.getText());
+ newSettingItems.put("textAlgorithm", textAlgorithmBox.getSelectionModel().getSelectedItem().toString());
+
+ newSettingItems.put("fileEnDecryptKey", fileEnDecryptKeyEntry.getText());
+ newSettingItems.put("fileEnDecryptSalt", fileEnDecryptSaltEntry.getText());
+ newSettingItems.put("fileEnDecryptAlgorithm", fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem().toString());
+
+ newSettingItems.put("fileDeleteIterations", fileDeleteIterationEntry.getText());
+
+ newSettingItems.put("fileOutputPath", fileOutputPathEntry.getText());
+ newSettingItems.put("removeFromFileBox", String.valueOf(removeFromFileBoxCheckBox.isSelected()));
+ newSettingItems.put("limitNumberOfThreads", String.valueOf(limitNumberOfThreadsCheckBox.isSelected()));
+
+ if (!config.isFile()) {
+ try {
+ if (!config.createNewFile()) {
+ warningAlert("Couldn't create config file");
+ } else {
+ addSetting(config, settingsNameEntry.getText().trim(), newSettingItems);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ errorAlert("Couldn't create config file", e.getMessage());
+ }
+ } else {
+ addSetting(config, settingsNameEntry.getText().trim(), newSettingItems);
+ }
+
+ rootStage.close();
+ }
+ });
+ });
+ });
+
+ thread.start();
+
+ rootStage.showAndWait();
+ }
+
+ /**
+ * Shows a GUI where the user can export settings to a extra file
+ *
+ * @param rootWindow from which this GUI will get called
+ * @throws IOException
+ *
+ * @since 1.11.0
+ */
+ public static void exportSettingsGUI(Window rootWindow) throws IOException {
+ Stage rootStage = new Stage();
+ rootStage.initOwner(rootWindow);
+ Parent exportSettingsRoot = FXMLLoader.load(Settings.class.getResource("resources/exportSettingsGUI.fxml"));
+ rootStage.initStyle(StageStyle.UNDECORATED);
+ rootStage.initModality(Modality.WINDOW_MODAL);
+ rootStage.setResizable(false);
+ rootStage.setTitle("cryptoGX");
+ Scene scene = new Scene(exportSettingsRoot, 254, 253);
+
+ rootStage.setScene(scene);
+
+ scene.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ scene.setOnMousePressed(event -> {
+ addSettingsGUIX = scene.getX() - event.getSceneX();
+ addSettingsGUIY = scene.getY() - event.getSceneY();
+ });
+
+ Thread thread = new Thread(() -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ MenuBar menuBar = (MenuBar) exportSettingsRoot.lookup("#menuBar");
+ menuBar.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ menuBar.setOnMousePressed(event -> {
+ addSettingsGUIX = menuBar.getLayoutX() - event.getSceneX();
+ addSettingsGUIY = menuBar.getLayoutY() - event.getSceneY();
+ });
+ ImageView closeButton = (ImageView) exportSettingsRoot.lookup("#closeButton");
+ closeButton.setOnMouseClicked(event -> rootStage.close());
+
+ VBox settingsBox = (VBox) exportSettingsRoot.lookup("#settingsBox");
+ Platform.runLater(() -> readSettings(config).keySet().forEach(s -> {
+ CheckBox newCheckBox = new CheckBox();
+ newCheckBox.setText(s);
+ settingsBox.getChildren().add(newCheckBox);
+ }));
+
+ Button exportButton = (Button) exportSettingsRoot.lookup("#exportButton");
+ exportButton.setOnAction(event -> {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Export settings");
+ fileChooser.setInitialFileName("settings.config");
+ fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Config files", "*.config"),
+ new FileChooser.ExtensionFilter("All files", "*.*"));
+ File file = fileChooser.showSaveDialog(exportSettingsRoot.getScene().getWindow());
+ if (file != null) {
+ TreeMap> writeInfos = new TreeMap<>();
+ TreeMap> settings = readSettings(config);
+ for (int i=0; iShows a GUI where the user can load saved settings
+ *
+ * @param rootWindow from which this GUI will get called
+ * @return the settings that the user has chosen
+ * @throws IOException
+ *
+ * @since 1.11.0
+ */
+ public static TreeMap> loadSettingsGUI(Window rootWindow) throws IOException {
+ Button[] outerLoadButton = new Button[1];
+ HashMap setting = new HashMap<>();
+ TreeMap> settingItems = readSettings(config);
+ TreeMap> returnItems = new TreeMap<>();
+
+ Stage rootStage = new Stage();
+ rootStage.initOwner(rootWindow);
+ AnchorPane loadSettingsRoot = FXMLLoader.load(Settings.class.getResource("resources/loadSettingsGUI.fxml"));
+ rootStage.initStyle(StageStyle.UNDECORATED);
+ rootStage.initModality(Modality.WINDOW_MODAL);
+ rootStage.setResizable(false);
+ rootStage.setTitle("cryptoGX");
+ rootStage.getIcons().add(new Image(Settings.class.getResource("resources/cryptoGX.png").toExternalForm()));
+ Scene scene = new Scene(loadSettingsRoot, 242, 235);
+
+ scene.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ scene.setOnMousePressed(event -> {
+ addSettingsGUIX = scene.getX() - event.getSceneX();
+ addSettingsGUIY = scene.getY() - event.getSceneY();
+ });
+
+ scene.setOnKeyReleased(event -> {
+ if (event.getCode() == KeyCode.ENTER) {
+ outerLoadButton[0].fire();
+ }
+ });
+
+ Thread thread = new Thread(() -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Platform.runLater(() -> {
+ MenuBar menuBar = (MenuBar) loadSettingsRoot.lookup("#menuBar");
+ menuBar.setOnMouseDragged(event -> {
+ rootStage.setX(event.getScreenX() + addSettingsGUIX);
+ rootStage.setY(event.getScreenY() + addSettingsGUIY);
+ });
+ menuBar.setOnMousePressed(event -> {
+ addSettingsGUIX = menuBar.getLayoutX() - event.getSceneX();
+ addSettingsGUIY = menuBar.getLayoutY() - event.getSceneY();
+ });
+
+ ImageView closeButton = (ImageView) loadSettingsRoot.lookup("#closeButton");
+ if (settingItems.isEmpty()) {
+ rootStage.close();
+ }
+
+ closeButton.setOnMouseClicked(event -> {
+ setting.put("textKey", configDefaultTextKey);
+ setting.put("textSalt", configDefaultTextSalt);
+ setting.put("textAlgorithm", configDefaultTextAlgorithm);
+
+ setting.put("fileEnDecryptKey", configDefaultFileEnDecryptKey);
+ setting.put("fileEnDecryptSalt", configDefaultFileEnDecryptSalt);
+ setting.put("fileEnDecryptAlgorithm", configDefaultFileEnDecryptAlgorithm);
+
+ setting.put("fileDeleteIterations", String.valueOf(configDefaultFileDeleteIterations));
+
+ setting.put("fileOutputPath", configDefaultFileOutputPath);
+ setting.put("removeFromFileBox", String.valueOf(configDefaultRemoveFileFromFileBox));
+ setting.put("limitNumberOfThreads", String.valueOf(configDefaultLimitNumberOfThreads));
+
+ returnItems.put("default", setting);
+
+ rootStage.close();
+ });
+
+ PasswordField keyHideEntry = (PasswordField) loadSettingsRoot.lookup("#passwordEntryHide");
+ TextField keyShowEntry = (TextField) loadSettingsRoot.lookup("#passwordEntryShow");
+
+ CheckBox showPassword = (CheckBox) loadSettingsRoot.lookup("#showPassword");
+ showPassword.setOnAction(event -> {
+ if (showPassword.isSelected()) {
+ keyShowEntry.setText(keyHideEntry.getText());
+ keyShowEntry.setVisible(true);
+ keyHideEntry.setVisible(false);
+ } else {
+ keyHideEntry.setText(keyShowEntry.getText());
+ keyHideEntry.setVisible(true);
+ keyShowEntry.setVisible(false);
+ }
+ });
+
+ ComboBox settingsBox = (ComboBox) loadSettingsRoot.lookup("#settingsBox");
+ settingsBox.setItems(FXCollections.observableArrayList(settingItems.keySet()));
+ settingsBox.setValue(settingItems.firstKey());
+ if (!Boolean.parseBoolean(settingItems.firstEntry().getValue().get("encrypted").trim())) {
+ keyHideEntry.clear();
+ keyHideEntry.setDisable(true);
+ keyShowEntry.setDisable(true);
+ showPassword.setDisable(true);
+ }
+ settingsBox.setOnAction(event -> {
+ try {
+ if (!Boolean.parseBoolean(settingItems.get(settingsBox.getSelectionModel().getSelectedItem().toString()).get("encrypted").trim())) {
+ keyHideEntry.clear();
+ keyHideEntry.setDisable(true);
+ keyShowEntry.clear();
+ keyShowEntry.setDisable(true);
+ showPassword.setDisable(true);
+ } else {
+ keyHideEntry.clear();
+ keyHideEntry.setDisable(false);
+ keyShowEntry.clear();
+ keyShowEntry.setDisable(false);
+ showPassword.setDisable(false);
+ }
+ } catch (NullPointerException e) {
+ //get called when delete button is pressed
+ }
+ });
+
+ Button loadButton = (Button) loadSettingsRoot.lookup("#loadButton");
+ loadButton.setOnAction(event -> {
+ String settingName = settingsBox.getSelectionModel().getSelectedItem().toString();
+ Map selectedSetting = settingItems.get(settingName);
+ if (keyHideEntry.isDisabled() && showPassword.isDisabled() && showPassword.isDisabled()) {
+ setting.put("textKey", selectedSetting.get("textKey"));
+ setting.put("textSalt", selectedSetting.get("textSalt"));
+ setting.put("textAlgorithm", selectedSetting.get("textAlgorithm"));
+
+ setting.put("fileEnDecryptKey", selectedSetting.get("fileEnDecryptKey"));
+ setting.put("fileEnDecryptSalt", selectedSetting.get("fileEnDecryptSalt"));
+ setting.put("fileEnDecryptAlgorithm", selectedSetting.get("fileEnDecryptAlgorithm"));
+
+ setting.put("fileDeleteIterations", selectedSetting.get("fileDeleteIterations"));
+
+ setting.put("fileOutputPath", selectedSetting.get("fileOutputPath"));
+ setting.put("removeFromFileBox", selectedSetting.get("removeFromFileBox"));
+ setting.put("limitNumberOfThreads", selectedSetting.get("limitNumberOfThreads"));
+
+ returnItems.put(settingsBox.getSelectionModel().getSelectedItem().toString(), setting);
+
+ rootStage.close();
+ } else {
+ EnDecrypt.AES decryptSetting;
+ if (keyHideEntry.isVisible()) {
+ decryptSetting = new EnDecrypt.AES(keyHideEntry.getText(), new byte[16]);
+ } else {
+ decryptSetting = new EnDecrypt.AES(keyShowEntry.getText(), new byte[16]);
+ }
+ try {
+ Map selectedEncryptedSetting = settingItems.get(settingName);
+ setting.put("textKey", decryptSetting.decrypt(selectedEncryptedSetting.get("textKey")));
+ setting.put("textSalt", decryptSetting.decrypt(selectedEncryptedSetting.get("textSalt")));
+ setting.put("textAlgorithm", decryptSetting.decrypt(selectedEncryptedSetting.get("textAlgorithm")));
+
+ setting.put("fileEnDecryptKey", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptKey")));
+ setting.put("fileEnDecryptSalt", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptSalt")));
+ setting.put("fileEnDecryptAlgorithm", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptAlgorithm")));
+
+ setting.put("fileDeleteIterations", String.valueOf(Integer.parseInt(decryptSetting.decrypt(selectedEncryptedSetting.get("fileDeleteIterations")))));
+
+ setting.put("fileOutputPath", decryptSetting.decrypt(selectedEncryptedSetting.get("fileOutputPath")));
+ setting.put("removeFromFileBox", decryptSetting.decrypt(selectedEncryptedSetting.get("removeFromFileBox")));
+ setting.put("limitNumberOfThreads", decryptSetting.decrypt(selectedEncryptedSetting.get("limitNumberOfThreads")));
+
+ returnItems.put(settingsBox.getSelectionModel().getSelectedItem().toString(), setting);
+
+ rootStage.close();
+ } catch (InvalidKeyException e) {
+ warningAlert("Wrong key is given");
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) {
+ e.printStackTrace();
+ warningAlert("Wrong key is given or the config wasn't\nsaved correctly");
+ }
+ }
+ });
+ outerLoadButton[0] = loadButton;
+
+ Button deleteButton = (Button) loadSettingsRoot.lookup("#deleteButton");
+ deleteButton.setOnAction(event -> {
+ AtomicReference deleteQuestionX = new AtomicReference<>((double) 0);
+ AtomicReference deleteQuestionY = new AtomicReference<>((double) 0);
+ Alert deleteQuestion = new Alert(Alert.AlertType.CONFIRMATION, "Delete " + settingsBox.getSelectionModel().getSelectedItem().toString() + "?", ButtonType.OK, ButtonType.CANCEL);
+ deleteQuestion.initStyle(StageStyle.UNDECORATED);
+ deleteQuestion.setTitle("Confirmation");
+ ((Stage) deleteQuestion.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Settings.class.getResource("resources/cryptoGX.png").toExternalForm()));
+
+ Scene window = deleteQuestion.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ deleteQuestion.setX(dragEvent.getScreenX() + deleteQuestionX.get());
+ deleteQuestion.setY(dragEvent.getScreenY() + deleteQuestionY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ deleteQuestionX.set(window.getX() - pressEvent.getSceneX());
+ deleteQuestionY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ Optional result = deleteQuestion.showAndWait();
+ if (result.get() == ButtonType.OK) {
+ if (settingItems.size() - 1 <= 0) {
+ for (int i = 0; i < 100; i++) {
+ if (config.isFile()) {
+ try {
+ SecureDelete.deleteFile(config, 5, new byte[64]);
+ isConfig = false;
+ rootStage.close();
+ break;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ rootStage.close();
+ } else if (deleteSetting(config, settingsBox.getSelectionModel().getSelectedItem().toString())) {
+ settingItems.remove(settingsBox.getSelectionModel().getSelectedItem().toString());
+
+ settingsBox.setItems(FXCollections.observableArrayList(settingItems.keySet()));
+ settingsBox.setValue(settingItems.firstKey());
+ } else {
+ warningAlert("Couldn't delete setting '" + settingsBox.getSelectionModel().getSelectedItem().toString() + "'");
+ }
+ }
+ });
+ });
+ });
+
+ thread.start();
+
+ rootStage.setScene(scene);
+ rootStage.showAndWait();
+
+ return returnItems;
+ }
+
+ /**
+ * Shows a GUI where the user can save the current settings
+ *
+ * @param settingName name of the new setting
+ * @param newSetting is the new setting key value pair
+ *
+ * @since 1.12.0
+ */
+ public static void addSetting(File file, String settingName, Map newSetting) {
+ TreeMap> settings = readSettings(file);
+ settings.put(settingName, newSetting);
+ writeSettings(file, settings);
+ }
+
+ /**
+ * Deletes a saved setting
+ *
+ * @param settingName of the setting
+ * @return if the setting could be found
+ *
+ * @since 1.12.0
+ */
+ public static boolean deleteSetting(File file, String settingName) {
+ StringBuilder newConfig = new StringBuilder();
+ boolean delete = false;
+ boolean found = false;
+
+ try {
+ BufferedReader configReader = new BufferedReader(new FileReader(file));
+
+ String line;
+
+ while ((line = configReader.readLine()) != null) {
+ line = line.trim();
+
+ if (line.startsWith("[") && line.endsWith("]")) {
+ if (line.replace("[", "").replace("]", "").split(" ")[0].equals(settingName)) {
+ delete = true;
+ found = true;
+ } else if (delete) {
+ delete = false;
+ newConfig.append(line).append("\n");
+ } else {
+ newConfig.append(line).append("\n");
+ }
+ } else if (!delete) {
+ newConfig.append(line).append("\n");
+ }
+ }
+
+ configReader.close();
+
+ BufferedWriter configFile = new BufferedWriter(new FileWriter(file));
+ configFile.write(newConfig.toString());
+ configFile.newLine();
+
+ configFile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return found;
+ }
+
+ /**
+ * Reads all settings saved in a file>
+ *
+ * @param file from which the settings should be read from
+ * @return the settings
+ *
+ * @since 1.12.0
+ */
+ public static TreeMap> readSettings(File file) {
+ TreeMap> returnMap = new TreeMap<>();
+ String settingName = null;
+ Map settingValues = new HashMap<>();
+
+ try {
+ BufferedReader configReader = new BufferedReader(new FileReader(file));
+
+ String line;
+
+ while ((line = configReader.readLine()) != null) {
+
+ if (line.isEmpty()) {
+ continue;
+ } else if (line.startsWith("[") && line.endsWith("]")) {
+ if (settingName != null) {
+ returnMap.put(settingName, settingValues);
+ settingValues = new HashMap<>();
+ }
+ String[] newSetting = line.replace("[", "").replace("]", "").split(" ");
+ settingName = newSetting[0].trim();
+ String[] encoded = newSetting[1].split("=");
+ settingValues.put("encrypted", encoded[1]);
+ } else {
+ String[] keyValue = line.split("=");
+ try {
+ settingValues.put(keyValue[0], keyValue[1]);
+ } catch (IndexOutOfBoundsException e) {
+ settingValues.put(keyValue[0], "");
+ }
+ }
+ }
+
+ if (settingName != null) {
+ returnMap.put(settingName, settingValues);
+ }
+
+ configReader.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ warningAlert("Couldn't find file '" + file.getAbsolutePath() + "'"); // this should never raise
+ } catch (IOException e) {
+ e.printStackTrace();
+ errorAlert("An IO Exception occurred", e.getMessage());
+ }
+
+ return returnMap;
+ }
+
+ /**
+ * Writes settings (could be more than one) to a file
+ *
+ * @param file where the settings should be written in
+ * @param settings of the user
+ *
+ * @since 1.12.0
+ */
+ public static void writeSettings(File file, TreeMap> settings) {
+ try {
+ BufferedWriter configWriter = new BufferedWriter(new FileWriter(file));
+
+ for (Map.Entry> settingElement: settings.entrySet()) {
+ configWriter.write("[" + settingElement.getKey() + " encrypted=" + Boolean.parseBoolean(settingElement.getValue().get("encrypted")) + "]");
+ configWriter.newLine();
+ for (Map.Entry entry : settingElement.getValue().entrySet()) {
+ String key = entry.getKey();
+ if (!key.equals("encrypted")) {
+ configWriter.write(entry.getKey() + "=" + entry.getValue());
+ configWriter.newLine();
+ }
+ }
+ }
+ configWriter.newLine();
+
+ configWriter.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ errorAlert("An error occurred while saving the settings", e.getMessage());
+ }
+ }
+
+}
diff --git a/src/org/bytedream/cryptogx/Utils.java b/src/org/bytedream/cryptogx/Utils.java
new file mode 100644
index 0000000..8b4f8e0
--- /dev/null
+++ b/src/org/bytedream/cryptogx/Utils.java
@@ -0,0 +1,51 @@
+package org.bytedream.cryptogx;
+
+import java.util.TreeMap;
+
+/**
+ * Support class
+ *
+ * @since 1.3.0
+ */
+public class Utils {
+
+ public static TreeMap algorithms = allAlgorithms();
+
+ /**
+ * Get all available algorithms
+ *
+ * @return all available algorithms
+ *
+ * @since 1.12.0
+ */
+ private static TreeMap allAlgorithms() {
+ TreeMap return_map = new TreeMap<>();
+
+ int[] aesKeySizes = {128, 192, 256};
+
+ for (int i: aesKeySizes) {
+ return_map.put("AES-" + i, "AES");
+ }
+
+ return return_map;
+ }
+
+ /**
+ * Checks if any character in {@param characters} appears in {@param string}
+ *
+ * @param characters that should be searched in {@param string}
+ * @param string that should be searched for the characters
+ * @return if any character in {@param characters} appears in {@param string}
+ *
+ * @since 1.3.0
+ */
+ public static boolean hasAnyCharacter(CharSequence characters, String string) {
+ for (char c: characters.toString().toCharArray()) {
+ if (string.indexOf(c) != -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/org/bytedream/cryptogx/resources/addSettingsGUI.fxml b/src/org/bytedream/cryptogx/resources/addSettingsGUI.fxml
new file mode 100644
index 0000000..235a089
--- /dev/null
+++ b/src/org/bytedream/cryptogx/resources/addSettingsGUI.fxml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/bytedream/cryptogx/resources/close.png b/src/org/bytedream/cryptogx/resources/close.png
new file mode 100644
index 0000000..1113eaf
Binary files /dev/null and b/src/org/bytedream/cryptogx/resources/close.png differ
diff --git a/src/org/bytedream/cryptogx/resources/cryptoGX.png b/src/org/bytedream/cryptogx/resources/cryptoGX.png
new file mode 100644
index 0000000..f84ad55
Binary files /dev/null and b/src/org/bytedream/cryptogx/resources/cryptoGX.png differ
diff --git a/src/org/bytedream/cryptogx/resources/exportSettingsGUI.fxml b/src/org/bytedream/cryptogx/resources/exportSettingsGUI.fxml
new file mode 100644
index 0000000..e0c0a88
--- /dev/null
+++ b/src/org/bytedream/cryptogx/resources/exportSettingsGUI.fxml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/bytedream/cryptogx/resources/loadSettingsGUI.fxml b/src/org/bytedream/cryptogx/resources/loadSettingsGUI.fxml
new file mode 100644
index 0000000..45fcfd5
--- /dev/null
+++ b/src/org/bytedream/cryptogx/resources/loadSettingsGUI.fxml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/bytedream/cryptogx/resources/loading.gif b/src/org/bytedream/cryptogx/resources/loading.gif
new file mode 100644
index 0000000..a3ba16a
Binary files /dev/null and b/src/org/bytedream/cryptogx/resources/loading.gif differ
diff --git a/src/org/bytedream/cryptogx/resources/mainGUI.fxml b/src/org/bytedream/cryptogx/resources/mainGUI.fxml
new file mode 100644
index 0000000..6c786fd
--- /dev/null
+++ b/src/org/bytedream/cryptogx/resources/mainGUI.fxml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/bytedream/cryptogx/resources/minimize.png b/src/org/bytedream/cryptogx/resources/minimize.png
new file mode 100644
index 0000000..928735e
Binary files /dev/null and b/src/org/bytedream/cryptogx/resources/minimize.png differ