From 0a671aa9bc627309307310d1020f4b6ba4abfbfb Mon Sep 17 00:00:00 2001 From: Tillmann Gaida Date: Fri, 23 Jan 2015 11:46:53 +0100 Subject: [PATCH] Addition of a name to the context path of the WebDAV servlet. The name will then appear as the name of the network drive on Windows. The name is "normalized" down to characters, which are certain to be accepted. I added a field to the unlock controller, which normalizes the name as you type. --- .../org/cryptomator/webdav/WebDavServer.java | 12 +++- .../org/cryptomator/ui/UnlockController.java | 18 ++++++ .../org/cryptomator/ui/model/Directory.java | 55 ++++++++++++++++++- .../ui/model/DirectoryDeserializer.java | 3 + .../ui/model/DirectorySerializer.java | 1 + main/ui/src/main/resources/fxml/unlock.fxml | 14 +++-- .../main/resources/localization.properties | 1 + .../cryptomator/ui/model/DirectoryTest.java | 21 +++++++ 8 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 main/ui/src/test/java/org/cryptomator/ui/model/DirectoryTest.java diff --git a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java index d7371a7f4..d70daf00a 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java +++ b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java @@ -15,6 +15,7 @@ import java.util.UUID; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import org.apache.commons.lang3.StringUtils; import org.cryptomator.crypto.Cryptor; import org.cryptomator.webdav.jackrabbit.WebDavServlet; import org.eclipse.jetty.server.Connector; @@ -81,11 +82,18 @@ public final class WebDavServer { /** * @param workDir Path of encrypted folder. * @param cryptor A fully initialized cryptor instance ready to en- or decrypt streams. + * @param name The name of the folder. Must be non-empty and only contain any of _ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 * @return servlet */ - public ServletLifeCycleAdapter createServlet(final Path workDir, final boolean checkFileIntegrity, final Cryptor cryptor) { + public ServletLifeCycleAdapter createServlet(final Path workDir, final boolean checkFileIntegrity, final Cryptor cryptor, String name) { try { - final URI uri = new URI(null, null, localConnector.getHost(), localConnector.getLocalPort(), "/" + UUID.randomUUID().toString(), null, null); + if(StringUtils.isEmpty(name)) { + throw new IllegalArgumentException("name empty"); + } + if(!StringUtils.containsOnly(name, "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")) { + throw new IllegalArgumentException("name contains illegal characters: " + name); + } + final URI uri = new URI(null, null, localConnector.getHost(), localConnector.getLocalPort(), "/" + UUID.randomUUID().toString() + "/" + name, null, null); final ServletContextHandler servletContext = new ServletContextHandler(servletCollection, uri.getRawPath(), ServletContextHandler.SESSIONS); final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), checkFileIntegrity, cryptor); diff --git a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java index 3cefca716..f4acfc28b 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java @@ -28,6 +28,7 @@ import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.TextField; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -39,6 +40,7 @@ import org.cryptomator.ui.controls.SecPasswordField; import org.cryptomator.ui.model.Directory; import org.cryptomator.ui.util.FXThreads; import org.cryptomator.ui.util.MasterKeyFilter; +import org.cryptomator.webdav.WebDavServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,6 +61,9 @@ public class UnlockController implements Initializable { @FXML private CheckBox checkIntegrity; + @FXML + private TextField mountName; + @FXML private Button unlockButton; @@ -73,6 +78,7 @@ public class UnlockController implements Initializable { this.rb = rb; usernameBox.valueProperty().addListener(this::didChooseUsername); + mountName.textProperty().addListener(this::didTypeMountName); } // **************************************** @@ -167,6 +173,17 @@ public class UnlockController implements Initializable { } } + private void didTypeMountName(ObservableValue property, String oldValue, String newValue) { + try { + directory.setMountName(newValue); + if (!newValue.equals(directory.getMountName())) { + mountName.setText(directory.getMountName()); + } + } catch (IllegalArgumentException e) { + mountName.setText(directory.getMountName()); + } + } + /* Getter/Setter */ public Directory getDirectory() { @@ -177,6 +194,7 @@ public class UnlockController implements Initializable { this.directory = directory; this.findExistingUsernames(); this.checkIntegrity.setSelected(directory.shouldVerifyFileIntegrity()); + this.mountName.setText(directory.getMountName()); } public UnlockListener getListener() { diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java b/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java index 78a130df1..dddc469a9 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; +import java.text.Normalizer; +import java.text.Normalizer.Form; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -36,6 +38,7 @@ public class Directory implements Serializable { private final Runnable shutdownTask = new ShutdownTask(); private final Path path; private boolean verifyFileIntegrity; + private String mountName = "Cryptomator"; private ServletLifeCycleAdapter webDavServlet; private WebDavMount webDavMount; @@ -45,6 +48,11 @@ public class Directory implements Serializable { } this.path = path; + try { + setMountName(getName()); + } catch (IllegalArgumentException e) { + + } } public boolean containsMasterKey() throws IOException { @@ -55,7 +63,7 @@ public class Directory implements Serializable { if (webDavServlet != null && webDavServlet.isRunning()) { return false; } - webDavServlet = WebDavServer.getInstance().createServlet(path, verifyFileIntegrity, cryptor); + webDavServlet = WebDavServer.getInstance().createServlet(path, verifyFileIntegrity, cryptor, getMountName()); if (webDavServlet.start()) { MainApplication.addShutdownTask(shutdownTask); return true; @@ -140,6 +148,51 @@ public class Directory implements Serializable { this.unlocked.set(unlocked); } + public String getMountName() { + return mountName; + } + + /** + * Tries to form a similar string using the regular latin alphabet. + * + * @param string + * @return a string composed of a-z, A-Z, 0-9, and _. + */ + public static String normalize(String string) { + String normalized = Normalizer.normalize(string, Form.NFD); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < normalized.length(); i++) { + char c = normalized.charAt(i); + if (Character.isWhitespace(c)) { + if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') { + builder.append('_'); + } + } else if (c < 127 && Character.isLetterOrDigit(c)) { + builder.append(c); + } else if (c < 127) { + if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') { + builder.append('_'); + } + } + } + return builder.toString(); + } + + /** + * sets the mount name while normalizing it + * + * @param mountName + * @throws IllegalArgumentException + * if the name is empty after normalization + */ + public void setMountName(String mountName) throws IllegalArgumentException { + mountName = normalize(mountName); + if (StringUtils.isEmpty(mountName)) { + throw new IllegalArgumentException("mount name is empty"); + } + this.mountName = mountName; + } + /* hashcode/equals */ @Override diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java b/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java index 9caa74c84..1d69ec997 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java @@ -20,6 +20,9 @@ public class DirectoryDeserializer extends JsonDeserializer { final Directory dir = new Directory(path); final boolean verifyFileIntegrity = node.has("checkIntegrity") ? node.get("checkIntegrity").asBoolean() : false; dir.setVerifyFileIntegrity(verifyFileIntegrity); + if (node.has("mountName")) { + dir.setMountName(node.get("mountName").asText()); + } return dir; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java b/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java index ad64952ae..e641a4928 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java @@ -14,6 +14,7 @@ public class DirectorySerializer extends JsonSerializer { jgen.writeStartObject(); jgen.writeStringField("path", value.getPath().toString()); jgen.writeBooleanField("checkIntegrity", value.shouldVerifyFileIntegrity()); + jgen.writeStringField("mountName", value.getMountName().toString()); jgen.writeEndObject(); } diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml index 9f3e788dd..6019be0df 100644 --- a/main/ui/src/main/resources/fxml/unlock.fxml +++ b/main/ui/src/main/resources/fxml/unlock.fxml @@ -43,13 +43,17 @@ -