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 @@ -