mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 18:51:26 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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("汉语"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user