Show warning dialog, if MAC check failed.

This commit is contained in:
Sebastian Stenzel
2015-03-09 09:56:25 +01:00
parent 1f3b91f187
commit 0994e7bb39
33 changed files with 446 additions and 81 deletions
@@ -11,6 +11,7 @@ package org.cryptomator.webdav;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -83,11 +84,13 @@ public final class WebDavServer {
/**
* @param workDir Path of encrypted folder.
* @param cryptor A fully initialized cryptor instance ready to en- or decrypt streams.
* @param failingMacCollection A (observable, thread-safe) collection, to which the names of resources are written, whose MAC
* authentication fails.
* @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 Cryptor cryptor, String name) {
public ServletLifeCycleAdapter createServlet(final Path workDir, final Cryptor cryptor, final Collection<String> failingMacCollection, final String name) {
try {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name empty");
@@ -98,7 +101,7 @@ public final class WebDavServer {
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(), cryptor);
final ServletHolder servlet = getWebDavServletHolder(workDir.toString(), cryptor, failingMacCollection);
servletContext.addServlet(servlet, "/*");
servletCollection.mapContexts();
@@ -110,8 +113,8 @@ public final class WebDavServer {
}
}
private ServletHolder getWebDavServletHolder(final String workDir, final Cryptor cryptor) {
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor));
private ServletHolder getWebDavServletHolder(final String workDir, final Cryptor cryptor, final Collection<String> failingMacCollection) {
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor, failingMacCollection));
result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir);
return result;
}
@@ -123,7 +126,7 @@ public final class WebDavServer {
/**
* Exposes implementation-specific methods to other modules.
*/
public class ServletLifeCycleAdapter {
public class ServletLifeCycleAdapter implements AutoCloseable {
private final LifeCycle lifecycle;
private final URI servletUri;
@@ -161,6 +164,11 @@ public final class WebDavServer {
return servletUri;
}
@Override
public void close() throws Exception {
this.stop();
}
}
}
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
@@ -0,0 +1,19 @@
package org.cryptomator.webdav.jackrabbit;
import java.util.Collection;
class CryptoWarningHandler {
private final Collection<String> resourcesWithInvalidMac;
public CryptoWarningHandler(Collection<String> resourcesWithInvalidMac) {
this.resourcesWithInvalidMac = resourcesWithInvalidMac;
}
public void macAuthFailed(String resourceName) {
if (!resourcesWithInvalidMac.contains(resourceName)) {
resourcesWithInvalidMac.add(resourceName);
}
}
}
@@ -23,20 +23,17 @@ import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.webdav.jackrabbit.resources.EncryptedDir;
import org.cryptomator.webdav.jackrabbit.resources.EncryptedFile;
import org.cryptomator.webdav.jackrabbit.resources.EncryptedFilePart;
import org.cryptomator.webdav.jackrabbit.resources.NonExistingNode;
import org.cryptomator.webdav.jackrabbit.resources.ResourcePathUtils;
import org.eclipse.jetty.http.HttpHeader;
class DavResourceFactoryImpl implements DavResourceFactory {
private final LockManager lockManager = new SimpleLockManager();
private final Cryptor cryptor;
private final CryptoWarningHandler cryptoWarningHandler;
DavResourceFactoryImpl(Cryptor cryptor) {
DavResourceFactoryImpl(Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
this.cryptor = cryptor;
this.cryptoWarningHandler = cryptoWarningHandler;
}
@Override
@@ -70,11 +67,11 @@ class DavResourceFactoryImpl implements DavResourceFactory {
}
private EncryptedFile createFilePart(DavResourceLocator locator, DavSession session, DavServletRequest request) {
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor);
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor, cryptoWarningHandler);
}
private EncryptedFile createFile(DavResourceLocator locator, DavSession session) {
return new EncryptedFile(this, locator, session, lockManager, cryptor);
return new EncryptedFile(this, locator, session, lockManager, cryptor, cryptoWarningHandler);
}
private EncryptedDir createDirectory(DavResourceLocator locator, DavSession session) {
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
@@ -42,7 +42,7 @@ import org.cryptomator.webdav.exceptions.IORuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EncryptedDir extends AbstractEncryptedNode {
class EncryptedDir extends AbstractEncryptedNode {
private static final Logger LOG = LoggerFactory.getLogger(EncryptedDir.class);
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.io.EOFException;
import java.io.IOException;
@@ -36,12 +36,15 @@ import org.eclipse.jetty.http.HttpHeaderValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EncryptedFile extends AbstractEncryptedNode {
class EncryptedFile extends AbstractEncryptedNode {
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFile.class);
public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
private final CryptoWarningHandler cryptoWarningHandler;
public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
super(factory, locator, session, lockManager, cryptor);
this.cryptoWarningHandler = cryptoWarningHandler;
}
@Override
@@ -81,7 +84,7 @@ public class EncryptedFile extends AbstractEncryptedNode {
} catch (EOFException e) {
LOG.warn("Unexpected end of stream (possibly client hung up).");
} catch (MacAuthenticationFailedException e) {
LOG.warn("MAC authentication failed, file content {} might be compromised.", getLocator().getResourcePath());
cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
} catch (DecryptFailedException e) {
throw new IOException("Error decrypting file " + path.toString(), e);
}
@@ -1,4 +1,4 @@
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.io.EOFException;
import java.io.IOException;
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
*
* @see {@link https://tools.ietf.org/html/rfc7233#section-4}
*/
public class EncryptedFilePart extends EncryptedFile {
class EncryptedFilePart extends EncryptedFile {
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFilePart.class);
private static final String BYTE_UNIT_PREFIX = "bytes=";
@@ -49,8 +49,8 @@ public class EncryptedFilePart extends EncryptedFile {
private final Set<Pair<Long, Long>> requestedContentRanges = new HashSet<Pair<Long, Long>>();
public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor) {
super(factory, locator, session, lockManager, cryptor);
public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
super(factory, locator, session, lockManager, cryptor, cryptoWarningHandler);
final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
if (rangeHeader == null) {
throw new IllegalArgumentException("HTTP request doesn't contain a range header");
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
@@ -1,4 +1,4 @@
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.io.IOException;
@@ -21,7 +21,7 @@ import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.cryptomator.crypto.Cryptor;
public class NonExistingNode extends AbstractEncryptedNode {
class NonExistingNode extends AbstractEncryptedNode {
public NonExistingNode(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
super(factory, locator, session, lockManager, cryptor);
@@ -6,7 +6,7 @@
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.webdav.jackrabbit.resources;
package org.cryptomator.webdav.jackrabbit;
import java.nio.file.FileSystems;
import java.nio.file.Path;
@@ -14,7 +14,7 @@ import java.nio.file.Path;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceLocator;
public final class ResourcePathUtils {
final class ResourcePathUtils {
private ResourcePathUtils() {
throw new IllegalStateException("not instantiable");
@@ -8,6 +8,8 @@
******************************************************************************/
package org.cryptomator.webdav.jackrabbit;
import java.util.Collection;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -27,10 +29,12 @@ public class WebDavServlet extends AbstractWebdavServlet {
private DavLocatorFactory davLocatorFactory;
private DavResourceFactory davResourceFactory;
private final Cryptor cryptor;
private final CryptoWarningHandler cryptoWarningHandler;
public WebDavServlet(final Cryptor cryptor) {
public WebDavServlet(final Cryptor cryptor, final Collection<String> failingMacCollection) {
super();
this.cryptor = cryptor;
this.cryptoWarningHandler = new CryptoWarningHandler(failingMacCollection);
}
@Override
@@ -42,7 +46,7 @@ public class WebDavServlet extends AbstractWebdavServlet {
final String fsRoot = config.getInitParameter(CFG_FS_ROOT);
this.davLocatorFactory = new DavLocatorFactoryImpl(fsRoot, cryptor);
this.davResourceFactory = new DavResourceFactoryImpl(cryptor);
this.davResourceFactory = new DavResourceFactoryImpl(cryptor, cryptoWarningHandler);
}
@Override
@@ -53,11 +53,7 @@ public class MainApplication extends Application {
}
private static Injector getInjector() {
try {
return Guice.createInjector(new MainModule());
} catch (Exception e) {
throw e;
}
return Guice.createInjector(new MainModule());
}
public MainApplication(Injector injector) {
@@ -0,0 +1,33 @@
package org.cryptomator.ui.controllers;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
public class MacWarningsController {
@FXML
private ListView<String> warningsList;
private Stage stage;
@FXML
private void hideWindow(ActionEvent event) {
stage.hide();
}
public void setMacWarnings(ObservableList<String> macWarnings) {
this.warningsList.setItems(macWarnings);
}
public Stage getStage() {
return stage;
}
public void setStage(Stage stage) {
this.stage = stage;
}
}
@@ -13,21 +13,25 @@ import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.SetChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
@@ -47,6 +51,7 @@ import org.cryptomator.ui.controls.DirectoryListCell;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.model.VaultFactory;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.util.ObservableSetAggregator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -79,6 +84,9 @@ public class MainController implements Initializable, InitializationListener, Un
private final ControllerFactory controllerFactory;
private final Settings settings;
private final VaultFactory vaultFactoy;
private final ObservableList<String> aggregatedMacWarnings;
private final SetChangeListener<String> macWarningsAggregator;
private final AtomicBoolean macWarningsWindowVisible;
private ResourceBundle rb;
@@ -88,6 +96,9 @@ public class MainController implements Initializable, InitializationListener, Un
this.controllerFactory = controllerFactory;
this.settings = settings;
this.vaultFactoy = vaultFactoy;
this.aggregatedMacWarnings = FXCollections.observableList(new ArrayList<>());
this.macWarningsAggregator = new ObservableSetAggregator<>(this.aggregatedMacWarnings);
this.macWarningsWindowVisible = new AtomicBoolean();
}
@Override
@@ -98,6 +109,8 @@ public class MainController implements Initializable, InitializationListener, Un
vaultList.setItems(items);
vaultList.setCellFactory(this::createDirecoryListCell);
vaultList.getSelectionModel().getSelectedItems().addListener(this::selectedVaultDidChange);
aggregatedMacWarnings.addListener(this::macWarningsDidChange);
}
@FXML
@@ -216,6 +229,12 @@ public class MainController implements Initializable, InitializationListener, Un
showChangePasswordView(selectedVault);
}
private void macWarningsDidChange(ListChangeListener.Change<? extends String> change) {
if (aggregatedMacWarnings.size() > 0) {
Platform.runLater(this::showMacWarningsWindow);
}
}
// ****************************************
// Subcontroller for right panel
// ****************************************
@@ -270,6 +289,7 @@ public class MainController implements Initializable, InitializationListener, Un
@Override
public void didUnlock(UnlockController ctrl) {
ctrl.getVault().getNamesOfResourcesWithInvalidMac().addListener(this.macWarningsAggregator);
showUnlockedView(ctrl.getVault());
Platform.setImplicitExit(false);
}
@@ -282,6 +302,7 @@ public class MainController implements Initializable, InitializationListener, Un
@Override
public void didLock(UnlockedController ctrl) {
ctrl.getVault().getNamesOfResourcesWithInvalidMac().removeListener(this.macWarningsAggregator);
showUnlockView(ctrl.getVault());
if (getUnlockedDirectories().isEmpty()) {
Platform.setImplicitExit(true);
@@ -299,6 +320,36 @@ public class MainController implements Initializable, InitializationListener, Un
showUnlockView(ctrl.getVault());
}
private void showMacWarningsWindow() {
if (macWarningsWindowVisible.getAndSet(true) == false) {
try {
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/mac_warnings.fxml"), rb);
loader.setControllerFactory(controllerFactory);
final Parent root = loader.load();
final Stage stage = new Stage();
stage.setTitle(rb.getString("macWarnings.windowTitle"));
stage.setScene(new Scene(root));
stage.sizeToScene();
stage.setResizable(false);
stage.setOnHidden(this::onHideMacWarningsWindow);
final MacWarningsController ctrl = loader.getController();
ctrl.setMacWarnings(this.aggregatedMacWarnings);
ctrl.setStage(stage);
stage.show();
} catch (IOException e) {
throw new IllegalStateException("Failed to load fxml file.", e);
}
}
}
private void onHideMacWarningsWindow(WindowEvent event) {
macWarningsWindowVisible.set(false);
aggregatedMacWarnings.clear();
}
/* Convenience */
public Collection<Vault> getDirectories() {
@@ -10,11 +10,14 @@ import java.util.Optional;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.ui.util.DeferredClosable;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.FXThreads;
import org.cryptomator.ui.util.mount.CommandFailedException;
import org.cryptomator.ui.util.mount.WebDavMount;
import org.cryptomator.ui.util.mount.WebDavMounter;
@@ -38,6 +41,7 @@ public class Vault implements Serializable {
private final WebDavMounter mounter;
private final DeferredCloser closer;
private final ObjectProperty<Boolean> unlocked = new SimpleObjectProperty<Boolean>(this, "unlocked", Boolean.FALSE);
private final ObservableSet<String> namesOfResourcesWithInvalidMac = FXThreads.observableSetOnMainThread(FXCollections.observableSet());
private String mountName;
private DeferredClosable<ServletLifeCycleAdapter> webDavServlet = DeferredClosable.empty();
@@ -70,13 +74,14 @@ public class Vault implements Serializable {
}
public synchronized boolean startServer() {
namesOfResourcesWithInvalidMac.clear();
Optional<ServletLifeCycleAdapter> o = webDavServlet.get();
if (o.isPresent() && o.get().isRunning()) {
return false;
}
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, getMountName());
ServletLifeCycleAdapter servlet = server.createServlet(path, cryptor, namesOfResourcesWithInvalidMac, mountName);
if (servlet.start()) {
webDavServlet = closer.closeLater(servlet, ServletLifeCycleAdapter::stop);
webDavServlet = closer.closeLater(servlet);
return true;
}
return false;
@@ -86,6 +91,7 @@ public class Vault implements Serializable {
unmount();
webDavServlet.close();
cryptor.swipeSensitiveData();
namesOfResourcesWithInvalidMac.clear();
}
public boolean mount() {
@@ -94,7 +100,7 @@ public class Vault implements Serializable {
return false;
}
try {
webDavMount = closer.closeLater(mounter.mount(o.get().getServletUri(), getMountName()), WebDavMount::unmount);
webDavMount = closer.closeLater(mounter.mount(o.get().getServletUri(), mountName));
return true;
} catch (CommandFailedException e) {
LOG.warn("mount failed", e);
@@ -139,6 +145,10 @@ public class Vault implements Serializable {
return mountName;
}
public ObservableSet<String> getNamesOfResourcesWithInvalidMac() {
return namesOfResourcesWithInvalidMac;
}
/**
* Tries to form a similar string using the regular latin alphabet.
*
@@ -15,6 +15,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javafx.application.Platform;
import javafx.collections.ObservableSet;
/**
* Use this utility class to spawn background tasks and wait for them to finish. <br/>
@@ -118,4 +119,8 @@ public final class FXThreads {
void taskFailed(Throwable t);
}
public static <E> ObservableSet<E> observableSetOnMainThread(ObservableSet<E> set) {
return new ObservableSetOnMainThread<E>(set);
}
}
@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2014 cryptomator.org
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial implementation
******************************************************************************/
package org.cryptomator.ui.util;
import java.util.Collection;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
/**
* From the moment on, this aggregator is added as an observer to one or many {@link ObservableSet}s, change-events will be passed through
* to the given aggregation.
*/
public class ObservableSetAggregator<E> implements SetChangeListener<E> {
private final Collection<E> aggregation;
/**
* @param aggregation Set to which elements from observed subsets shall be added.
*/
public ObservableSetAggregator(final Collection<E> aggregation) {
this.aggregation = aggregation;
}
@Override
public void onChanged(Change<? extends E> change) {
if (change.getSet() == aggregation) {
// break cycle if aggregator observes aggregation
return;
}
if (change.wasAdded()) {
aggregation.add(change.getElementAdded());
} else if (change.wasRemoved()) {
aggregation.remove(change.getElementRemoved());
}
}
}
@@ -0,0 +1,163 @@
package org.cryptomator.ui.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.collections.SetChangeListener.Change;
class ObservableSetOnMainThread<E> implements ObservableSet<E> {
private final ObservableSet<E> set;
private final Collection<InvalidationListener> invalidationListeners;
private final Collection<SetChangeListener<? super E>> setChangeListeners;
public ObservableSetOnMainThread(ObservableSet<E> set) {
this.set = set;
this.invalidationListeners = new HashSet<>();
this.setChangeListeners = new HashSet<>();
this.set.addListener(this::invalidated);
this.set.addListener(this::onChanged);
}
@Override
public int size() {
return set.size();
}
@Override
public boolean isEmpty() {
return set.isEmpty();
}
@Override
public boolean contains(Object o) {
return set.contains(o);
}
@Override
public Iterator<E> iterator() {
return set.iterator();
}
@Override
public Object[] toArray() {
return set.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return set.toArray(a);
}
@Override
public boolean add(E e) {
return set.add(e);
}
@Override
public boolean remove(Object o) {
return set.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return set.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
@Override
public void clear() {
set.clear();
}
private void invalidated(Observable observable) {
Platform.runLater(() -> {
for (InvalidationListener listener : invalidationListeners) {
listener.invalidated(this);
}
});
}
@Override
public void addListener(InvalidationListener listener) {
invalidationListeners.add(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
invalidationListeners.remove(listener);
}
private void onChanged(Change<? extends E> change) {
final Change<? extends E> c = new SetChange(this, change.getElementAdded(), change.getElementRemoved());
Platform.runLater(() -> {
for (SetChangeListener<? super E> listener : setChangeListeners) {
listener.onChanged(c);
}
});
}
@Override
public void addListener(SetChangeListener<? super E> listener) {
setChangeListeners.add(listener);
}
@Override
public void removeListener(SetChangeListener<? super E> listener) {
setChangeListeners.add(listener);
}
private class SetChange extends SetChangeListener.Change<E> {
private final E added;
private final E removed;
public SetChange(ObservableSet<E> set, E added, E removed) {
super(set);
this.added = added;
this.removed = removed;
}
@Override
public boolean wasAdded() {
return added != null;
}
@Override
public boolean wasRemoved() {
return removed != null;
}
@Override
public E getElementAdded() {
return added;
}
@Override
public E getElementRemoved() {
return removed;
}
}
}
@@ -0,0 +1,10 @@
package org.cryptomator.ui.util.mount;
abstract class AbstractWebDavMount implements WebDavMount {
@Override
public void close() throws Exception {
this.unmount();
}
}
@@ -30,7 +30,7 @@ final class FallbackWebDavMounter implements WebDavMounterStrategy {
@Override
public WebDavMount mount(URI uri, String name) {
displayMountInstructions();
return new WebDavMount() {
return new AbstractWebDavMount() {
@Override
public void unmount() {
displayUnmountInstructions();
@@ -48,7 +48,7 @@ final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy {
"gvfs-mount -u \"dav:$DAV_SSP\"")
.addEnv("DAV_SSP", uri.getRawSchemeSpecificPart());
mountScript.execute();
return new WebDavMount() {
return new AbstractWebDavMount() {
@Override
public void unmount() throws CommandFailedException {
unmountScript.execute();
@@ -41,7 +41,7 @@ final class MacOsXWebDavMounter implements WebDavMounterStrategy {
"umount $MOUNT_PATH")
.addEnv("MOUNT_PATH", path);
mountScript.execute();
return new WebDavMount() {
return new AbstractWebDavMount() {
@Override
public void unmount() throws CommandFailedException {
unmountScript.execute();
@@ -8,13 +8,12 @@
******************************************************************************/
package org.cryptomator.ui.util.mount;
/**
* A mounted webdav share.
*
* @author Markus Kreusch
*/
public interface WebDavMount {
public interface WebDavMount extends AutoCloseable {
/**
* Unmounts this {@code WebDavMount}.
@@ -22,5 +21,5 @@ public interface WebDavMount {
* @throws CommandFailedException if the unmount operation fails
*/
void unmount() throws CommandFailedException;
}
@@ -54,7 +54,7 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy {
final CommandResult mountResult = mountScript.execute(30, TimeUnit.SECONDS);
final String driveLetter = getDriveLetter(mountResult.getStdOut());
final Script unmountScript = fromLines("net use " + driveLetter + " /delete").addEnv("DRIVE_LETTER", driveLetter);
return new WebDavMount() {
return new AbstractWebDavMount() {
@Override
public void unmount() throws CommandFailedException {
unmountScript.execute();
@@ -7,16 +7,16 @@
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.*?>
<?import java.net.URL?>
<?import java.lang.String?>
<?import org.cryptomator.ui.controls.SecPasswordField?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Button?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.ChangePasswordController" xmlns:fx="http://javafx.com/fxml">
@@ -7,16 +7,14 @@
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.*?>
<?import org.cryptomator.ui.controls.*?>
<?import java.net.URL?>
<?import javafx.scene.layout.HBox?>
<?import org.cryptomator.ui.controls.SecPasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.InitializeController" xmlns:fx="http://javafx.com/fxml">
<padding>
@@ -0,0 +1,28 @@
<?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.URL?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" prefHeight="225.0" prefWidth="400.0" spacing="12.0" fx:controller="org.cryptomator.ui.controllers.MacWarningsController" xmlns:fx="http://javafx.com/fxml">
<padding><Insets top="12.0" right="12.0" bottom="12.0" left="12.0"/></padding>
<children>
<Label text="%macWarnings.message" />
<ListView fx:id="warningsList" VBox.vgrow="ALWAYS" focusTraversable="false" />
<Button text="%macWarnings.okButton" defaultButton="true" prefWidth="150.0" onAction="#hideWindow" />
</children>
</VBox>
@@ -16,7 +16,6 @@
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.Separator?>
<?import javafx.geometry.Insets?>
<HBox fx:id="rootPane" prefHeight="440.0" prefWidth="640.0" fx:controller="org.cryptomator.ui.controllers.MainController" xmlns:fx="http://javafx.com/fxml">
+6 -6
View File
@@ -7,17 +7,17 @@
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.*?>
<?import java.net.URL?>
<?import java.lang.String?>
<?import org.cryptomator.ui.controls.SecPasswordField?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.TextField?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.UnlockController" xmlns:fx="http://javafx.com/fxml">
<padding>
@@ -7,16 +7,15 @@
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.*?>
<?import java.net.URL?>
<?import java.lang.String?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.ColumnConstraints?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.controllers.UnlockedController" xmlns:fx="http://javafx.com/fxml">
<padding>
+3 -5
View File
@@ -7,16 +7,14 @@
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.*?>
<?import java.net.URL?>
<?import java.lang.String?>
<?import javafx.scene.shape.Arc?>
<?import javafx.scene.shape.QuadCurve?>
<?import javafx.scene.shape.Path?>
<?import javafx.scene.shape.Line?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Label?>
<AnchorPane xmlns:fx="http://javafx.com/fxml">
@@ -15,18 +15,15 @@ main.directoryList.contextMenu.changePassword=Change password
main.addDirectory.contextMenu.new=Create new vault
main.addDirectory.contextMenu.open=Add existing vault
# welcome.fxml
welcome.welcomeLabel=Welcome to Cryptomator
welcome.addButtonInstructionLabel=Start by adding a new vault :-)
# initialize.fxml
initialize.label.password=Password
initialize.label.retypePassword=Retype password
initialize.button.ok=Create vault
# unlock.fxml
unlock.label.password=Password
unlock.label.mountName=Drive name
@@ -50,6 +47,10 @@ changePassword.infoMessage.success=Password changed.
unlocked.button.lock=Lock vault
unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
# mac_warnings.fxml
macWarnings.windowTitle=Mac authentication failed
macWarnings.message=The following files may be corrupted
macWarnings.okButton=Dismiss warning
# tray icon
tray.menu.open=Open