- Version 0.1.0

- Automounting (OS X only atm)
- Automatic TCP ports
- Added icon
This commit is contained in:
Sebastian Stenzel 2014-11-30 00:10:06 +01:00
parent be38432e73
commit c322b0488e
18 changed files with 202 additions and 175 deletions

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.1.0</version>
</parent>
<artifactId>core</artifactId>
<name>Cryptomator core I/O module</name>
@ -28,7 +28,6 @@
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Logging -->

View File

@ -22,6 +22,7 @@ public final class WebDAVServer {
private static final Logger LOG = LoggerFactory.getLogger(WebDAVServer.class);
private static final WebDAVServer INSTANCE = new WebDAVServer();
private static final String LOCALHOST = "127.0.0.1";
private final Server server = new Server();
private WebDAVServer() {
@ -32,11 +33,14 @@ public final class WebDAVServer {
return INSTANCE;
}
public boolean start(final String workDir, final int port, final Cryptor cryptor) {
/**
* @param workDir Path of encrypted folder.
* @param cryptor A fully initialized cryptor instance ready to en- or decrypt streams.
* @return port, on which the server did start
*/
public int start(final String workDir, final Cryptor cryptor) {
final ServerConnector connector = new ServerConnector(server);
connector.setHost("127.0.0.1");
connector.setPort(port);
server.setConnectors(new Connector[] {connector});
connector.setHost(LOCALHOST);
final String contextPath = "/";
@ -46,12 +50,13 @@ public final class WebDAVServer {
server.setHandler(context);
try {
server.setConnectors(new Connector[] {connector});
server.start();
} catch (Exception ex) {
LOG.error("Server couldn't be started", ex);
}
return server.isStarted();
return connector.getLocalPort();
}
public boolean isRunning() {
@ -68,7 +73,7 @@ public final class WebDAVServer {
}
private ServletHolder getMiltonServletHolder(final String workDir, final String contextPath, final Cryptor cryptor) {
final ServletHolder result = new ServletHolder("OCE-WebDAV-Servlet", new WebDavServlet(cryptor));
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor));
result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir);
result.setInitParameter(WebDavServlet.CFG_HTTP_ROOT, contextPath);
return result;

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.1.0</version>
</parent>
<artifactId>crypto-aes</artifactId>
<name>Cryptomator cryptographic module (AES)</name>
@ -22,7 +22,6 @@
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Logging -->

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.1.0</version>
</parent>
<artifactId>crypto-api</artifactId>
<name>Cryptomator cryptographic module API</name>

View File

@ -11,12 +11,22 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.1.0</version>
<packaging>pom</packaging>
<name>Cryptomator</name>
<organization>
<name>cryptomator.org</name>
<url>http://cryptomator.org</url>
</organization>
<developers>
<developer>
<name>Sebastian Stenzel</name>
<email>sebastian.stenzel@gmail.com</email>
<timezone>+1</timezone>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -31,15 +41,30 @@
<commons-codec.version>1.9</commons-codec.version>
</properties>
<developers>
<developer>
<name>Sebastian Stenzel</name>
<email>sebastian.stenzel@gmail.com</email>
</developer>
</developers>
<dependencyManagement>
<dependencies>
<!-- modules -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-aes</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>ui</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>

View File

@ -1,16 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2014 Sebastian Stenzel This file is licensed under the terms of the MIT license. See the LICENSE.txt file for more info. Contributors: Sebastian Stenzel - initial API and implementation -->
<!--
Copyright (c) 2014 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - initial API and implementation
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.1.0</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>
<properties>
<javafx.application.name>Cryptomator</javafx.application.name>
<exec.mainClass>org.cryptomator.ui.MainApplication</exec.mainClass>
<javafx.tools.ant.jar>${java.home}/../lib/ant-javafx.jar</javafx.tools.ant.jar>
</properties>
@ -19,12 +27,10 @@
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-aes</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- JSON -->
@ -91,7 +97,7 @@
<configuration>
<target xmlns:fx="javafx:com.sun.javafx.tools.ant">
<taskdef uri="javafx:com.sun.javafx.tools.ant" resource="com/sun/javafx/tools/ant/antlib.xml" classpath="${javafx.tools.ant.jar}" />
<fx:application id="fxApp" version="${project.version}" name="${project.name}" mainClass="${exec.mainClass}" />
<fx:application id="fxApp" version="${project.version}" name="${javafx.application.name}" mainClass="${exec.mainClass}" />
<fx:jar destfile="${project.build.directory}/${project.build.finalName}">
<fx:application refid="fxApp" />
@ -102,9 +108,9 @@
</fx:jar>
<fx:deploy outdir="${project.build.directory}/dist" outfile="${project.build.finalName}" nativeBundles="all">
<fx:info title="Cloud Encryptor" vendor="cloudencryptor.org">
<!-- todo provide .ico and .icns files for osx/win -->
<fx:icon href="shortcut.ico" width="32" height="32" depth="8" />
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="MIT">
<!-- todo provide .ico files for win -->
<fx:icon href="${project.build.outputDirectory}/logo.icns" width="512" height="512" />
</fx:info>
<fx:platform basedir="" javafx="2.2+" j2se="8.0" />
<fx:application refid="fxApp" />

View File

@ -43,6 +43,8 @@ import org.cryptomator.crypto.exceptions.WrongPasswordException;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.MasterKeyFilter;
import org.cryptomator.ui.util.WebDavMounter;
import org.cryptomator.ui.util.WebDavMounter.CommandFailedException;
import org.cryptomator.webdav.WebDAVServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -197,21 +199,29 @@ public class AccessController implements Initializable {
}
private void tryStart() {
try {
final Settings settings = Settings.load();
if (WebDAVServer.getInstance().start(settings.getWebdavWorkDir(), settings.getPort(), cryptor)) {
startServerButton.setText(localization.getString("access.button.stopServer"));
passwordField.setDisable(true);
final Settings settings = Settings.load();
final int webdavPort = WebDAVServer.getInstance().start(settings.getWebdavWorkDir(), cryptor);
if (webdavPort > 0) {
startServerButton.setText(localization.getString("access.button.stopServer"));
passwordField.setDisable(true);
try {
WebDavMounter.mount(webdavPort);
} catch (CommandFailedException e) {
messageLabel.setText(String.format(localization.getString("access.messageLabel.mountFailed"), webdavPort));
LOG.error("Mounting WebDAV share failed.", e);
}
} catch (NumberFormatException ex) {
LOG.error("Invalid port", ex);
}
}
private void tryStop() {
if (WebDAVServer.getInstance().stop()) {
startServerButton.setText(localization.getString("access.button.startServer"));
passwordField.setDisable(false);
try {
WebDavMounter.unmount(5);
if (WebDAVServer.getInstance().stop()) {
startServerButton.setText(localization.getString("access.button.startServer"));
passwordField.setDisable(false);
}
} catch (CommandFailedException e) {
LOG.warn("Unmounting WebDAV share failed.", e);
}
}

View File

@ -1,78 +0,0 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.ui;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import org.apache.commons.lang3.CharUtils;
import org.cryptomator.ui.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AdvancedController implements Initializable {
private static final Logger LOG = LoggerFactory.getLogger(AdvancedController.class);
@FXML
private GridPane rootGridPane;
@FXML
private TextField portTextField;
@Override
public void initialize(URL url, ResourceBundle rb) {
portTextField.setText(String.valueOf(Settings.load().getPort()));
portTextField.addEventFilter(KeyEvent.KEY_TYPED, new NumericKeyTypeEventFilter());
portTextField.focusedProperty().addListener(new PortTextFieldFocusListener());
}
/**
* Consumes key events, if typed key is not 0-9.
*/
private static final class NumericKeyTypeEventFilter implements EventHandler<KeyEvent> {
@Override
public void handle(KeyEvent t) {
if (t.getCharacter() == null || t.getCharacter().length() == 0) {
return;
}
char c = t.getCharacter().charAt(0);
if (!CharUtils.isAsciiNumeric(c)) {
t.consume();
}
}
}
/**
* Saves port settings, when textfield loses focus.
*/
private class PortTextFieldFocusListener implements ChangeListener<Boolean> {
@Override
public void changed(ObservableValue<? extends Boolean> property, Boolean wasFocused, Boolean isFocused) {
final Settings settings = Settings.load();
try {
int port = Integer.valueOf(portTextField.getText());
settings.setPort(port);
} catch (NumberFormatException ex) {
LOG.warn("Invalid port " + portTextField.getText());
portTextField.setText(String.valueOf(settings.getPort()));
}
}
}
}

View File

@ -18,10 +18,16 @@ import javafx.scene.Scene;
import javafx.stage.Stage;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.WebDavMounter;
import org.cryptomator.ui.util.WebDavMounter.CommandFailedException;
import org.cryptomator.webdav.WebDAVServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainApplication extends Application {
private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
public static void main(String[] args) {
launch(args);
}
@ -40,6 +46,11 @@ public class MainApplication extends Application {
@Override
public void stop() throws Exception {
try {
WebDavMounter.unmount(5);
} catch (CommandFailedException e) {
LOG.warn("Unmounting WebDAV share failed.", e);
}
WebDAVServer.getInstance().stop();
Settings.save();
super.stop();

View File

@ -10,38 +10,42 @@ package org.cryptomator.ui;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
public class MainController {
@FXML
private ToggleGroup toolbarButtonGroup;
@FXML
private VBox rootVBox;
@FXML
private Pane initializePanel;
@FXML
private Pane accessPanel;
@FXML
private Pane advancedPanel;
@FXML
protected void showInitializePane(ActionEvent event) {
showPanel(initializePanel);
}
@FXML
protected void showAccessPane(ActionEvent event) {
showPanel(accessPanel);
}
@FXML
protected void showAdvancedPane(ActionEvent event) {
showPanel(advancedPanel);
}
private void showPanel(Pane panel) {
rootVBox.getChildren().remove(1);
rootVBox.getChildren().add(panel);

View File

@ -1,3 +1,11 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.ui.controls;
import javafx.beans.value.ChangeListener;

View File

@ -18,6 +18,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -35,20 +36,18 @@ public class Settings implements Serializable {
private static Settings INSTANCE = null;
static {
final String home = System.getProperty("user.home", ".");
final String appdata = System.getenv("APPDATA");
final String os = System.getProperty("os.name").toLowerCase();
final FileSystem fs = FileSystems.getDefault();
if (os.contains("win") && appdata != null) {
if (SystemUtils.IS_OS_WINDOWS && appdata != null) {
SETTINGS_DIR = fs.getPath(appdata, "opencloudencryptor");
} else if (os.contains("win") && appdata == null) {
SETTINGS_DIR = fs.getPath(home, ".opencloudencryptor");
} else if (os.contains("mac")) {
SETTINGS_DIR = fs.getPath(home, "Library/Application Support/opencloudencryptor");
} else if (SystemUtils.IS_OS_WINDOWS && appdata == null) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".opencloudencryptor");
} else if (SystemUtils.IS_OS_MAC_OSX) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/opencloudencryptor");
} else {
// (os.contains("solaris") || os.contains("sunos") || os.contains("linux") || os.contains("unix"))
SETTINGS_DIR = fs.getPath(home, ".opencloudencryptor");
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".opencloudencryptor");
}
}
@ -113,10 +112,12 @@ public class Settings implements Serializable {
this.username = username;
}
@Deprecated
public int getPort() {
return port;
}
@Deprecated
public void setPort(int port) {
this.port = port;
}

View File

@ -1,3 +1,11 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.ui.util;
import java.io.IOException;

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.ui.util;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class WebDavMounter {
private static final Logger LOG = LoggerFactory.getLogger(WebDavMounter.class);
private static final int CMD_DEFAULT_TIMEOUT = 1;
private WebDavMounter() {
throw new IllegalStateException("not instantiable.");
}
public static void mount(int localPort) throws CommandFailedException {
if (SystemUtils.IS_OS_MAC_OSX) {
exec("mkdir /Volumes/Cryptomator", CMD_DEFAULT_TIMEOUT);
exec("mount_webdav -S -v Cryptomator localhost:" + localPort + " /Volumes/Cryptomator", CMD_DEFAULT_TIMEOUT);
exec("open /Volumes/Cryptomator", CMD_DEFAULT_TIMEOUT);
}
}
public static void unmount(int timeout) throws CommandFailedException {
if (SystemUtils.IS_OS_MAC_OSX) {
exec("umount /Volumes/Cryptomator", timeout);
}
}
private static void exec(String cmd, int timoutSeconds) throws CommandFailedException {
try {
final Process proc = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
if (proc.waitFor(timoutSeconds, TimeUnit.SECONDS)) {
proc.destroy();
}
if (proc.exitValue() != 0) {
throw new CommandFailedException(IOUtils.toString(proc.getErrorStream()));
}
} catch (IOException | InterruptedException | IllegalThreadStateException e) {
LOG.error("Command execution failed.", e);
throw new CommandFailedException(e);
}
}
public static class CommandFailedException extends Exception {
private static final long serialVersionUID = 5784853630182321479L;
private CommandFailedException(String message) {
super(message);
}
private CommandFailedException(Throwable cause) {
super(cause);
}
}
}

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2014 Sebastian Stenzel
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - initial API and implementation
-->
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<GridPane fx:id="rootGridPane" fx:controller="org.cryptomator.ui.AdvancedController" xmlns:fx="http://javafx.com/fxml" styleClass="root" gridLinesVisible="false" vgap="5" hgap="5" prefWidth="480">
<stylesheets>
<URL value="@panels.css" />
</stylesheets>
<padding>
<Insets top="10" right="10" bottom="10" left="10" />
</padding>
<columnConstraints>
<ColumnConstraints minWidth="150" maxWidth="150" hgrow="NEVER" />
<ColumnConstraints minWidth="250" hgrow="ALWAYS" />
</columnConstraints>
<children>
<!-- Row 0 -->
<Label GridPane.rowIndex="0" GridPane.columnIndex="0" text="%advanced.label.port" GridPane.halignment="RIGHT" />
<TextField fx:id="portTextField" GridPane.rowIndex="0" GridPane.columnIndex="1" />
</children>
</GridPane>

View File

@ -9,7 +9,6 @@
# main.fxml
toolbarbutton.initialize=Initialize Vault
toolbarbutton.access=Access Vault
toolbarbutton.advanced=Advanced Settings
# initialize.fxml
initialize.label.workDir=New vault location
@ -32,6 +31,4 @@ access.messageLabel.wrongPassword=Wrong password.
access.messageLabel.invalidStorageLocation=Vault directory invalid.
access.messageLabel.decryptionFailed=Decryption failed.
access.messageLabel.unsupportedKeyLengthInstallJCE=Decryption failed. Please install Oracle JCE.
# advanced.fxml
advanced.label.port=WebDAV Port
access.messageLabel.mountFailed=Mounting WebDAV share (Port %d) failed.

Binary file not shown.

View File

@ -21,7 +21,6 @@
<fx:define>
<fx:include fx:id="initializePanel" source="initialize.fxml" />
<fx:include fx:id="accessPanel" source="access.fxml" />
<fx:include fx:id="advancedPanel" source="advanced.fxml" />
</fx:define>
<children>
@ -32,7 +31,6 @@
</fx:define>
<ToggleButton text="%toolbarbutton.initialize" toggleGroup="$toolbarButtonGroup" onAction="#showInitializePane" />
<ToggleButton text="%toolbarbutton.access" toggleGroup="$toolbarButtonGroup" onAction="#showAccessPane" selected="true" />
<ToggleButton text="%toolbarbutton.advanced" toggleGroup="$toolbarButtonGroup" onAction="#showAdvancedPane" />
</items>
</ToolBar>
<fx:reference source="accessPanel"/>