From 4a02bf529d374ac76124d9d6d89dc3d58e452f2d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 29 Aug 2019 22:20:27 +0200 Subject: [PATCH] Add readme file to vault storage directory --- .../CreateNewVaultPasswordController.java | 15 ++++- .../ui/addvaultwizard/ReadmeGenerator.java | 62 +++++++++++++++++++ .../main/resources/i18n/strings.properties | 5 ++ .../addvaultwizard/ReadMeGeneratorTest.java | 46 ++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java create mode 100644 main/ui/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 898192046..47d4a5653 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -30,9 +30,13 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ResourceBundle; @AddVaultWizardScoped @@ -50,6 +54,7 @@ public class CreateNewVaultPasswordController implements FxController { private final VaultFactory vaultFactory; private final ResourceBundle resourceBundle; private final PasswordStrengthUtil strengthRater; + private final ReadmeGenerator readmeGenerator; private final IntegerProperty passwordStrength; public Button finishButton; @@ -63,7 +68,7 @@ public class CreateNewVaultPasswordController implements FxController { public CheckBox finalConfirmationCheckbox; @Inject - CreateNewVaultPasswordController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, StringProperty vaultName, ObjectProperty vaultPath, ObservableList vaults, @AddVaultWizard ObjectProperty vault, VaultFactory vaultFactory, ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) { + CreateNewVaultPasswordController(@AddVaultWizard Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, StringProperty vaultName, ObjectProperty vaultPath, ObservableList vaults, @AddVaultWizard ObjectProperty vault, VaultFactory vaultFactory, ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ReadmeGenerator readmeGenerator) { this.window = window; this.chooseLocationScene = chooseLocationScene; this.successScene = successScene; @@ -74,6 +79,7 @@ public class CreateNewVaultPasswordController implements FxController { this.vaultFactory = vaultFactory; this.resourceBundle = resourceBundle; this.strengthRater = strengthRater; + this.readmeGenerator = readmeGenerator; this.passwordStrength = new SimpleIntegerProperty(-1); } @@ -120,6 +126,13 @@ public class CreateNewVaultPasswordController implements FxController { try { newVault.create(passwordField.getCharacters()); LOG.info("Created new vault at path {}", vaultPath.get()); + String readmeFileName = resourceBundle.getString("addvault.new.readme.storageLocation.fileName"); + Path readmeFile = vaultPath.get().resolve(readmeFileName); + try (WritableByteChannel ch = Files.newByteChannel(readmeFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { + ByteBuffer buf = StandardCharsets.US_ASCII.encode(readmeGenerator.createVaultStorageLocationReadmeRtf()); + ch.write(buf); + } + LOG.info("Created readme file {}", readmeFile); vault.set(newVault); vaults.add(newVault); window.setScene(successScene.get()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java new file mode 100644 index 000000000..fe432a4b1 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java @@ -0,0 +1,62 @@ +package org.cryptomator.ui.addvaultwizard; + +import javax.inject.Inject; +import java.util.List; +import java.util.ResourceBundle; + +@AddVaultWizardScoped +public class ReadmeGenerator { + + // specs: https://web.archive.org/web/20190708132914/http://www.kleinlercher.at/tools/Windows_Protocols/Word2007RTFSpec9.pdf + private static final String RTF_HEADER = "{\\rtf1\\fbidis\\ansi\\uc0\\fs32\n"; + private static final String RTF_FOOTER = "}"; + private static final String HELP_URL = "{\\field{\\*\\fldinst HYPERLINK \"http://www.google.com/\"}{\\fldrslt google.com}}"; + + private final ResourceBundle resourceBundle; + + @Inject + public ReadmeGenerator(ResourceBundle resourceBundle){ + this.resourceBundle = resourceBundle; + } + + public String createVaultStorageLocationReadmeRtf() { + return createDocument(List.of( // + resourceBundle.getString("addvault.new.readme.storageLocation.1"), // + resourceBundle.getString("addvault.new.readme.storageLocation.2"), // + resourceBundle.getString("addvault.new.readme.storageLocation.3"), // + String.format(resourceBundle.getString("addvault.new.readme.storageLocation.4"), HELP_URL) // + )); + } + + // visible for testing + String createDocument(Iterable paragraphs) { + StringBuilder sb = new StringBuilder(RTF_HEADER); + for (String p : paragraphs) { + sb.append("\\par {\\sa80 "); + appendEscaped(sb, p); + sb.append("}\n"); + } + sb.append(RTF_FOOTER); + return sb.toString(); + } + + // visible for testing + String escapeNonAsciiChars(CharSequence input) { + StringBuilder sb = new StringBuilder(); + appendEscaped(sb, input); + return sb.toString(); + } + + private void appendEscaped(StringBuilder sb, CharSequence input) { + input.chars().forEachOrdered(c -> { + if (c < 128) { + sb.append((char) c); + } else if (c < 0xFFFF) { + sb.append("\\u").append(c); + } + }); + } + + + +} diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties index 8ccecab36..99eee930a 100644 --- a/main/ui/src/main/resources/i18n/strings.properties +++ b/main/ui/src/main/resources/i18n/strings.properties @@ -38,6 +38,11 @@ addvaultwizard.new.reenterPassword=Confirm the password addvaultwizard.new.passwordsMatch=Passwords match! addvaultwizard.new.passwordsDoNotMatch=Passwords do not match addvaultwizard.new.finalConfirmation=I understand that I will not be able to recover my data if I forget my password +addvault.new.readme.storageLocation.fileName=WHAT IS THIS DIRECTORY.rtf +addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT FILES ⚠️ +addvault.new.readme.storageLocation.2=This is your vault's storage location. {\\b DO NOT} alter any files within this directory. +addvault.new.readme.storageLocation.3=If you want to encrypt files using Cryptomator, unlock the vault and use the provided drive. +addvault.new.readme.storageLocation.4=If you need help, try %s. ## Existing addvaultwizard.existing.instruction=Choose the "masterkey.cryptomator" file of your existing vault. addvaultwizard.existing.chooseBtn=Choose… diff --git a/main/ui/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java b/main/ui/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java new file mode 100644 index 000000000..709fde0f0 --- /dev/null +++ b/main/ui/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java @@ -0,0 +1,46 @@ +package org.cryptomator.ui.addvaultwizard; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.List; + +public class ReadMeGeneratorTest { + + @ParameterizedTest + @CsvSource({ // + "test,test", // + "t\u00E4st,t\\u228st", // + "t\uD83D\uDE09st,t\\u55357\\u56841st", // + }) + public void testEscapeNonAsciiChars(String input, String expectedResult) { + ReadmeGenerator readmeGenerator = new ReadmeGenerator(null); + + String actualResult = readmeGenerator.escapeNonAsciiChars(input); + + Assertions.assertEquals(expectedResult, actualResult); + } + + @Test + public void testCreateDocument() { + ReadmeGenerator readmeGenerator = new ReadmeGenerator(null); + Iterable paragraphs = List.of( // + "Dear User,", // + "\\b please don't touch the \"d\" directory.", // + "Thank you for your cooperation \uD83D\uDE09" // + ); + + String result = readmeGenerator.createDocument(paragraphs); + + MatcherAssert.assertThat(result, CoreMatchers.startsWith("{\\rtf1\\fbidis\\ansi\\uc0\\fs32")); + MatcherAssert.assertThat(result, CoreMatchers.containsString("\\par {\\sa80 Dear User,}")); + MatcherAssert.assertThat(result, CoreMatchers.containsString("\\par {\\sa80 \\b please don't touch the \"d\" directory.}")); + MatcherAssert.assertThat(result, CoreMatchers.containsString("\\par {\\sa80 Thank you for your cooperation \\u55357\\u56841}")); + MatcherAssert.assertThat(result, CoreMatchers.endsWith("}")); + } + +}