Browse Source

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.

Tillmann Gaida 10 years ago
parent
commit
0a671aa9bc

+ 10 - 2
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);

+ 18 - 0
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<? extends String> 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() {

+ 54 - 1
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

+ 3 - 0
main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java

@@ -20,6 +20,9 @@ public class DirectoryDeserializer extends JsonDeserializer<Directory> {
 		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;
 	}
 

+ 1 - 0
main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java

@@ -14,6 +14,7 @@ public class DirectorySerializer extends JsonSerializer<Directory> {
 		jgen.writeStartObject();
 		jgen.writeStringField("path", value.getPath().toString());
 		jgen.writeBooleanField("checkIntegrity", value.shouldVerifyFileIntegrity());
+		jgen.writeStringField("mountName", value.getMountName().toString());
 		jgen.writeEndObject();
 	}
 

+ 9 - 5
main/ui/src/main/resources/fxml/unlock.fxml

@@ -43,13 +43,17 @@
 		<CheckBox fx:id="checkIntegrity" wrapText="true" text="%unlock.checkbox.checkIntegrity" GridPane.rowIndex="2" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" />
 		
 		<!-- Row 3 -->
-		<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickUnlockButton"/>
+		<Label text="%unlock.label.mountName" GridPane.rowIndex="3" GridPane.columnIndex="0" />
+		<TextField fx:id="mountName" GridPane.rowIndex="3" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" />
 		
-		<!-- Row 4-->
-		<ProgressIndicator progress="-1" fx:id="progressIndicator" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER" visible="false"/>
+		<!-- Row 4 -->
+		<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickUnlockButton"/>
 		
-		<!-- Row 5 -->
-		<Label fx:id="messageLabel" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" />
+		<!-- Row 5-->
+		<ProgressIndicator progress="-1" fx:id="progressIndicator" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER" visible="false"/>
+		
+		<!-- Row 6 -->
+		<Label fx:id="messageLabel" GridPane.rowIndex="6" GridPane.columnIndex="0" GridPane.columnSpan="2" />
 	</children>
 </GridPane>
 

+ 1 - 0
main/ui/src/main/resources/localization.properties

@@ -33,6 +33,7 @@ initialize.alert.directoryIsNotEmpty.content=All existing files inside this dire
 unlock.label.username=Username
 unlock.label.password=Password
 unlock.label.checkIntegrity=File integrity
+unlock.label.mountName=Drive name
 unlock.checkbox.checkIntegrity=Verify checksums (slower, but detects manipulation)
 unlock.button.unlock=Unlock vault
 unlock.errorMessage.wrongPassword=Wrong password.

+ 21 - 0
main/ui/src/test/java/org/cryptomator/ui/model/DirectoryTest.java

@@ -0,0 +1,21 @@
+package org.cryptomator.ui.model;
+
+import static org.junit.Assert.*;
+
+import org.cryptomator.ui.model.Directory;
+import org.junit.Test;
+
+public class DirectoryTest {
+	@Test
+	public void testNormalize() throws Exception {
+		assertEquals("_", Directory.normalize(" "));
+
+		assertEquals("a", Directory.normalize("ä"));
+
+		assertEquals("C", Directory.normalize("Ĉ"));
+
+		assertEquals("_", Directory.normalize(":"));
+
+		assertEquals("", Directory.normalize("汉语"));
+	}
+}