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.
This commit is contained in:
Tillmann Gaida
2015-01-23 11:46:53 +01:00
parent 8cc445a12a
commit 0a671aa9bc
8 changed files with 117 additions and 8 deletions

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

@@ -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();
}

View File

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

View File

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

View File

@@ -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("汉语"));
}
}