diff --git a/README.md b/README.md
index dccec1091..4e29763ba 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[](https://cryptomator.org/)
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
-[](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
+[](https://snyk.io/test/github/cryptomator/cryptomator)
[](https://www.codacy.com/gh/cryptomator/cryptomator/dashboard)
[](http://twitter.com/Cryptomator)
[](https://translate.cryptomator.org/)
@@ -28,14 +28,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
### Silver Sponsors
-
-
-
-
-
-
-
-
- [Jameson Lopp](https://www.lopp.net/)
---
@@ -56,7 +48,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
- File names get encrypted
- Folder structure gets obfuscated
- Use as many vaults in your Dropbox as you want, each having individual passwords
-- Two thousand commits for the security of your data!! :tada:
+- Three thousand commits for the security of your data!! :tada:
### Privacy
@@ -80,20 +72,20 @@ For more information on the security details visit [cryptomator.org](https://doc
### Dependencies
-* JDK 16 (e.g. adoptopenjdk)
+* JDK 17 (e.g. temurin)
* Maven 3
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
### Run Maven
```
-cd main
-mvn clean install -Prelease,windows
-# or mvn clean install -Prelease,mac
-# or mvn clean install -Prelease,linux
+mvn clean install
+# or mvn clean install -Pwindows
+# or mvn clean install -Pmac
+# or mvn clean install -Plinux
```
-This will build all the jars and bundle them together with their OS-specific dependencies under `main/buildkit/target`. This can now be used to build native packages.
+This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages.
### Start Cryptomator
diff --git a/dist/linux/appimage/.gitignore b/dist/linux/appimage/.gitignore
new file mode 100644
index 000000000..1ed40c771
--- /dev/null
+++ b/dist/linux/appimage/.gitignore
@@ -0,0 +1,4 @@
+# created during build
+Cryptomator.AppDir
+*.AppImage
+*.AppImage.zsync
\ No newline at end of file
diff --git a/dist/linux/appimage/build.sh b/dist/linux/appimage/build.sh
new file mode 100755
index 000000000..1d0017ea4
--- /dev/null
+++ b/dist/linux/appimage/build.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+cd $(dirname $0)
+REVISION_NO=`git rev-list --count HEAD`
+
+# check preconditions
+if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
+command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
+command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
+
+VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
+
+# compile
+mvn -B -f ../../../pom.xml clean package -DskipTests -Plinux
+cp ../../../target/cryptomator-*.jar ../../../target/mods
+
+# add runtime
+${JAVA_HOME}/bin/jlink \
+ --output runtime \
+ --module-path "${JAVA_HOME}/jmods" \
+ --add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility \
+ --no-header-files \
+ --no-man-pages \
+ --strip-debug \
+ --compress=1
+
+# create app dir
+${JAVA_HOME}/bin/jpackage \
+ --verbose \
+ --type app-image \
+ --runtime-image runtime \
+ --input ../../../target/libs \
+ --module-path ../../../target/mods \
+ --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
+ --dest . \
+ --name Cryptomator \
+ --vendor "Skymatic GmbH" \
+ --copyright "(C) 2016 - 2021 Skymatic GmbH" \
+ --java-options "-Xss5m" \
+ --java-options "-Xmx256m" \
+ --app-version "${VERSION}.${REVISION_NO}" \
+ --java-options "-Dfile.encoding=\"utf-8\"" \
+ --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
+ --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
+ --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
+ --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
+ --java-options "-Dcryptomator.showTrayIcon=false" \
+ --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
+ --resource-dir ../resources
+
+# transform AppDir
+mv Cryptomator Cryptomator.AppDir
+cp -r resources/AppDir/* Cryptomator.AppDir/
+chmod +x Cryptomator.AppDir/lib/runtime/bin/java
+envsubst '${REVISION_NO}' < resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
+ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
+ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/Cryptomator.svg
+ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
+ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
+ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
+
+# extract jffi
+JFFI_NATIVE_JAR=`ls Cryptomator.AppDir/lib/app | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
+${JAVA_HOME}/bin/jar -xf Cryptomator.AppDir/lib/app/${JFFI_NATIVE_JAR} /jni/x86_64-Linux/
+mv jni/x86_64-Linux/* Cryptomator.AppDir/lib/app/libjffi.so
+rm -r jni/x86_64-Linux
+
+# load AppImageTool
+curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o /tmp/appimagetool.AppImage
+chmod +x /tmp/appimagetool.AppImage
+
+# create AppImage
+/tmp/appimagetool.AppImage \
+ Cryptomator.AppDir \
+ cryptomator-SNAPSHOT-x86_64.AppImage \
+ -u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
diff --git a/dist/linux/appimage/resources/AppDir/bin/cryptomator.sh b/dist/linux/appimage/resources/AppDir/bin/cryptomator.sh
new file mode 100755
index 000000000..82c3e01ca
--- /dev/null
+++ b/dist/linux/appimage/resources/AppDir/bin/cryptomator.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+cd $(dirname $0)
+
+# determine GTK version
+GTK2_PRESENT=1 # initially false
+GTK3_PRESENT=0 # initially true
+if command -v dpkg &> /dev/null; then # do stuff for debian based things
+ GTK2_PRESENT=`dpkg -l libgtk* | grep -e '\^ii' | grep -e 'libgtk2-*' &> /dev/null; echo $?`
+ GTK3_PRESENT=`dpkg -l libgtk* | grep -e '\^ii' | grep -e 'libgtk-3-*' &> /dev/null; echo $?`
+elif command -v rpm &> /dev/null; then # do stuff for rpm based things (including yum/dnf)
+ GTK2_PRESENT=`rpm -qa | grep -e '\^gtk2-[0-9][0-9]*' &> /dev/null; echo $?`
+ GTK3_PRESENT=`rpm -qa | grep -e '\^gtk3-[0-9][0-9]*' &> /dev/null; echo $?`
+elif command -v pacman &> /dev/null; then # don't forget arch
+ GTK2_PRESENT=`pacman -Qi gtk2 &> /dev/null; echo $?`
+ GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
+fi
+
+if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
+ GTK_FLAG="-Djdk.gtk.version=2"
+fi
+
+# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
+export LD_PRELOAD=lib/app/libjffi.so
+
+# start Cryptomator
+./lib/runtime/bin/java \
+ -p "lib/app/mods" \
+ -cp "lib/app/*" \
+ -Dfile.encoding="utf-8" \
+ -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
+ -Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" \
+ -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
+ -Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json" \
+ -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
+ -Dcryptomator.buildNumber="appimage-${REVISION_NO}" \
+ -Dcryptomator.appVersion="${SEMVER_STR}" \
+ $GTK_FLAG \
+ -Xss5m \
+ -Xmx256m \
+ -m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
diff --git a/dist/linux/appimage/resources/AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop b/dist/linux/appimage/resources/AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
new file mode 100644
index 000000000..3e1b34830
--- /dev/null
+++ b/dist/linux/appimage/resources/AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=Cryptomator
+Comment=Cloud Storage Encryption Utility
+Exec=cryptomator %F
+Icon=org.cryptomator.Cryptomator
+Terminal=false
+Type=Application
+Categories=Utility;Security;FileTools;
+StartupWMClass=org.cryptomator.launcher.Cryptomator
+MimeType=application/vnd.cryptomator.encrypted;application/x-vnd.cryptomator.vault-metadata;
diff --git a/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
new file mode 100644
index 000000000..e356c35c6
Binary files /dev/null and b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png differ
diff --git a/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
new file mode 100644
index 000000000..9c8635111
Binary files /dev/null and b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png differ
diff --git a/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
new file mode 100644
index 000000000..b2e12a3c3
--- /dev/null
+++ b/dist/linux/appimage/resources/AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dist/linux/appimage/resources/AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml b/dist/linux/appimage/resources/AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
new file mode 100644
index 000000000..ad4af6c70
--- /dev/null
+++ b/dist/linux/appimage/resources/AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
@@ -0,0 +1,69 @@
+
+
+
+ org.cryptomator.Cryptomator
+ FSFAP
+ GPL-3.0-or-later
+ Cryptomator
+ Multi-platform client-side encryption tool optimized for cloud storages
+
+
+ Cryptomator offers multi-platform transparent client-side encryption of your files in the cloud.
+
+
+ Features:
+
+
Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
+
Open Source means: No backdoors, control is better than trust
+
Client-side: No accounts, no data shared with any online service
+
Totally transparent: Just work on the virtual drive as if it were a USB flash drive
+
AES encryption with 256-bit key length
+
File names get encrypted
+
Folder structure gets obfuscated
+
Use as many vaults in your Dropbox as you want, each having individual passwords
+
One thousand commits for the security of your data!! :tada:
+
+
+
+ Privacy:
+
+
256-bit keys (unlimited strength policy bundled with native binaries)
+
Scrypt key derivation
+
Cryptographically secure random numbers for salts, IVs and the masterkey of course
+
Sensitive data is wiped from the heap asap
+
Lightweight: Complexity kills security
+
+
+
+ Consistency:
+
+
HMAC over file contents to recognize changed ciphertext before decryption
+
I/O operations are transactional and atomic, if the filesystems support it
+
Each file contains all information needed for decryption (except for the key of course), no common metadata means no Single Point of Failure
+
+
+
+
+ Office
+ Security
+ FileTools
+ Java
+
+ http://cryptomator.org
+ https://github.com/cryptomator/cryptomator/issues
+ https://community.cryptomator.org/c/kb/faq
+ https://community.cryptomator.org/
+ https://cryptomator.org/
+
+ none
+ none
+ none
+ none
+ mild
+
+ Cryptomator
+
+ cryptomator
+
+ org.cryptomator.Cryptomator.desktop
+
diff --git a/dist/linux/debian/changelog b/dist/linux/debian/changelog
new file mode 100644
index 000000000..84a9756f9
--- /dev/null
+++ b/dist/linux/debian/changelog
@@ -0,0 +1,5 @@
+cryptomator (${PPA_VERSION}) focal; urgency=low
+
+ * Full changelog can be found on https://github.com/cryptomator/cryptomator/releases
+
+ -- Cryptobot ${RFC2822_TIMESTAMP}
diff --git a/dist/linux/debian/compat b/dist/linux/debian/compat
new file mode 100644
index 000000000..f599e28b8
--- /dev/null
+++ b/dist/linux/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/dist/linux/debian/control b/dist/linux/debian/control
new file mode 100644
index 000000000..ef06120b3
--- /dev/null
+++ b/dist/linux/debian/control
@@ -0,0 +1,23 @@
+Source: cryptomator
+Maintainer: Cryptobot
+Section: utils
+Priority: optional
+Build-Depends: debhelper (>=10), openjdk-17-jdk
+Standards-Version: 4.5.0
+Homepage: https://cryptomator.org
+Vcs-Git: https://github.com/cryptomator/cryptomator.git
+Vcs-browser: https://github.com/cryptomator/cryptomator
+
+Package: cryptomator
+Architecture: any
+Section: utils
+Priority: optional
+Depends: ${shlibs:Depends}, ${misc:Depends}, libfuse2, xdg-utils, libjffi-jni
+Recommends: gvfs-backends, gvfs-fuse, gnome-keyring
+XB-AppName: Cryptomator
+XB-Category: Utility;Security;FileTools;
+Homepage: https://cryptomator.org
+Description: Multi-platform client-side encryption of your cloud files.
+ Cryptomator provides free client-side AES encryption for your cloud files.
+ Create encrypted vaults, which get mounted as virtual volumes. Whatever
+ you save on one of these volumes will end up encrypted inside your vault.
diff --git a/dist/linux/debian/copyright b/dist/linux/debian/copyright
new file mode 100644
index 000000000..ba6980bb8
--- /dev/null
+++ b/dist/linux/debian/copyright
@@ -0,0 +1,39 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: cryptomator
+Upstream-Contact: Cryptomator
+Source: https://cryptomator.org
+
+Files: *
+Copyright: 2016-2021 Skymatic GmbH
+License: GPL-3+
+
+Files: debian/org.cryptomator.Cryptomator.appdata.xml
+Copyright: 2016-2021 Skymatic GmbH
+License: FSFAP
+
+License: GPL-3+
+ This program is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation, either
+ version 3 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this program. If not, see
+ .
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 3 can be found in the file
+ `/usr/share/common-licenses/GPL-3'.
+
+License: FSFAP
+ Copying and distribution of this file, with or without modification, are
+ permitted in any medium without royalty provided the copyright notice and
+ this notice are preserved. This file is offered as-is, without any
+ warranty.
\ No newline at end of file
diff --git a/dist/linux/debian/cryptomator-vault.xml b/dist/linux/debian/cryptomator-vault.xml
new file mode 100644
index 000000000..eeb4bc537
--- /dev/null
+++ b/dist/linux/debian/cryptomator-vault.xml
@@ -0,0 +1,7 @@
+
+
+
+ Cryptomator Vault Metadata
+
+
+
diff --git a/dist/linux/debian/cryptomator.install b/dist/linux/debian/cryptomator.install
new file mode 100644
index 000000000..0d5e0b31c
--- /dev/null
+++ b/dist/linux/debian/cryptomator.install
@@ -0,0 +1,7 @@
+cryptomator usr/lib
+debian/cryptomator.sh usr/lib/cryptomator/bin
+debian/org.cryptomator.Cryptomator.desktop usr/share/applications
+debian/org.cryptomator.Cryptomator.svg usr/share/icons/hicolor/scalable/apps
+debian/org.cryptomator.Cryptomator.png usr/share/icons/hicolor/512x512/apps
+debian/org.cryptomator.Cryptomator.appdata.xml usr/share/metainfo
+debian/cryptomator-vault.xml usr/share/mime/packages
\ No newline at end of file
diff --git a/dist/linux/debian/cryptomator.links b/dist/linux/debian/cryptomator.links
new file mode 100644
index 000000000..8cc4cd0d4
--- /dev/null
+++ b/dist/linux/debian/cryptomator.links
@@ -0,0 +1 @@
+usr/lib/cryptomator/bin/cryptomator.sh usr/bin/cryptomator
diff --git a/dist/linux/debian/cryptomator.sh b/dist/linux/debian/cryptomator.sh
new file mode 100644
index 000000000..b8cea3cfe
--- /dev/null
+++ b/dist/linux/debian/cryptomator.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# fix for https://github.com/cryptomator/cryptomator/issues/1370
+export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/jni/libjffi-1.2.so
+
+/usr/lib/cryptomator/bin/cryptomator
\ No newline at end of file
diff --git a/dist/linux/debian/org.cryptomator.Cryptomator.appdata.xml b/dist/linux/debian/org.cryptomator.Cryptomator.appdata.xml
new file mode 100644
index 000000000..ad4af6c70
--- /dev/null
+++ b/dist/linux/debian/org.cryptomator.Cryptomator.appdata.xml
@@ -0,0 +1,69 @@
+
+
+
+ org.cryptomator.Cryptomator
+ FSFAP
+ GPL-3.0-or-later
+ Cryptomator
+ Multi-platform client-side encryption tool optimized for cloud storages
+
+
+ Cryptomator offers multi-platform transparent client-side encryption of your files in the cloud.
+
+
+ Features:
+
+
Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
+
Open Source means: No backdoors, control is better than trust
+
Client-side: No accounts, no data shared with any online service
+
Totally transparent: Just work on the virtual drive as if it were a USB flash drive
+
AES encryption with 256-bit key length
+
File names get encrypted
+
Folder structure gets obfuscated
+
Use as many vaults in your Dropbox as you want, each having individual passwords
+
One thousand commits for the security of your data!! :tada:
+
+
+
+ Privacy:
+
+
256-bit keys (unlimited strength policy bundled with native binaries)
+
Scrypt key derivation
+
Cryptographically secure random numbers for salts, IVs and the masterkey of course
+
Sensitive data is wiped from the heap asap
+
Lightweight: Complexity kills security
+
+
+
+ Consistency:
+
+
HMAC over file contents to recognize changed ciphertext before decryption
+
I/O operations are transactional and atomic, if the filesystems support it
+
Each file contains all information needed for decryption (except for the key of course), no common metadata means no Single Point of Failure
+
+
+
+
+ Office
+ Security
+ FileTools
+ Java
+
+ http://cryptomator.org
+ https://github.com/cryptomator/cryptomator/issues
+ https://community.cryptomator.org/c/kb/faq
+ https://community.cryptomator.org/
+ https://cryptomator.org/
+
+ none
+ none
+ none
+ none
+ mild
+
+ Cryptomator
+
+ cryptomator
+
+ org.cryptomator.Cryptomator.desktop
+
diff --git a/dist/linux/debian/org.cryptomator.Cryptomator.desktop b/dist/linux/debian/org.cryptomator.Cryptomator.desktop
new file mode 100644
index 000000000..d8a5925bd
--- /dev/null
+++ b/dist/linux/debian/org.cryptomator.Cryptomator.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=Cryptomator
+Version=${VERSION_STR}
+Comment=Cloud Storage Encryption Utility
+Exec=/usr/bin/cryptomator %f
+Icon=org.cryptomator.Cryptomator
+Terminal=false
+Type=Application
+Categories=Utility;Security;FileTools;
+StartupWMClass=org.cryptomator.launcher.Cryptomator
+MimeType=application/vnd.cryptomator.encrypted;application/x-vnd.cryptomator.vault-metadata;
\ No newline at end of file
diff --git a/dist/linux/debian/org.cryptomator.Cryptomator.png b/dist/linux/debian/org.cryptomator.Cryptomator.png
new file mode 100644
index 000000000..9c8635111
Binary files /dev/null and b/dist/linux/debian/org.cryptomator.Cryptomator.png differ
diff --git a/dist/linux/debian/org.cryptomator.Cryptomator.svg b/dist/linux/debian/org.cryptomator.Cryptomator.svg
new file mode 100644
index 000000000..19d80d49c
--- /dev/null
+++ b/dist/linux/debian/org.cryptomator.Cryptomator.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dist/linux/debian/postinst b/dist/linux/debian/postinst
new file mode 100644
index 000000000..2af574c6b
--- /dev/null
+++ b/dist/linux/debian/postinst
@@ -0,0 +1,44 @@
+#!/bin/sh
+# postinst script for Cryptomator
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * `configure'
+# * `abort-upgrade'
+# * `abort-remove' `in-favour'
+#
+# * `abort-remove'
+# * `abort-deconfigure' `in-favour'
+# `removing'
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ configure)
+ echo Adding shortcut to the menu
+ if [ ! -d "/usr/share/desktop-directories" ]; then
+ mkdir -p /usr/share/desktop-directories
+ fi
+ xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
+ xdg-mime install /usr/share/mime/packages/cryptomator-vault.xml
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/dist/linux/debian/prerm b/dist/linux/debian/prerm
new file mode 100644
index 000000000..cace6816e
--- /dev/null
+++ b/dist/linux/debian/prerm
@@ -0,0 +1,42 @@
+#!/bin/sh
+# prerm script for Cryptomator
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * `remove'
+# * `upgrade'
+# * `failed-upgrade'
+# * `remove' `in-favour'
+# * `deconfigure' `in-favour'
+# `removing'
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ echo Removing shortcut
+
+ xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
+ xdg-mime uninstall /usr/share/mime/packages/cryptomator-vault.xml
+ ;;
+
+ failed-upgrade)
+ ;;
+
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/dist/linux/debian/rules b/dist/linux/debian/rules
new file mode 100755
index 000000000..b36819f8b
--- /dev/null
+++ b/dist/linux/debian/rules
@@ -0,0 +1,53 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@
+
+override_dh_auto_clean:
+ rm -rf runtime
+ rm -rf cryptomator
+ rm -rf debian/cryptomator
+
+override_dh_auto_build:
+ jlink \
+ --output runtime \
+ --add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility \
+ --no-header-files \
+ --no-man-pages \
+ --strip-debug \
+ --compress=2
+ jpackage \
+ --type app-image \
+ --runtime-image runtime \
+ --input libs \
+ --module-path mods \
+ --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
+ --dest . \
+ --name cryptomator \
+ --vendor "Skymatic GmbH" \
+ --copyright "(C) 2016 - 2021 Skymatic GmbH" \
+ --java-options "-Xss5m" \
+ --java-options "-Xmx256m" \
+ --java-options "-Dfile.encoding=\"utf-8\"" \
+ --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
+ --java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
+ --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
+ --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
+ --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
+ --java-options "-Dcryptomator.showTrayIcon=false" \
+ --java-options "-Dcryptomator.buildNumber=\"ppa-${REVISION_NUM}\"" \
+ --java-options "-Dcryptomator.appVersion=\"${VERSION_STR}\"" \
+ --app-version "${VERSION_NUM}.${REVISION_NUM}" \
+ --resource-dir resources \
+ --verbose
+
+override_dh_fixperms:
+ dh_fixperms
+ chmod +x debian/cryptomator/usr/lib/cryptomator/bin/cryptomator.sh
+
+# override_dh_strip:
+ # no-op
diff --git a/dist/linux/debian/source/format b/dist/linux/debian/source/format
new file mode 100644
index 000000000..163aaf8d8
--- /dev/null
+++ b/dist/linux/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/dist/linux/debian/source/include-binaries b/dist/linux/debian/source/include-binaries
new file mode 100644
index 000000000..adc7cabd8
--- /dev/null
+++ b/dist/linux/debian/source/include-binaries
@@ -0,0 +1,2 @@
+debian/org.cryptomator.Cryptomator.png
+resources/cryptomator.png
diff --git a/main/buildkit/src/main/resources/launcher-linux.sh b/dist/linux/launcher.sh
old mode 100644
new mode 100755
similarity index 66%
rename from main/buildkit/src/main/resources/launcher-linux.sh
rename to dist/linux/launcher.sh
index 6ceb933b0..1980edc13
--- a/main/buildkit/src/main/resources/launcher-linux.sh
+++ b/dist/linux/launcher.sh
@@ -1,12 +1,13 @@
#!/bin/sh
cd $(dirname $0)
java \
+ -p "mods" \
-cp "libs/*" \
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" \
- -Dcryptomator.ipcPortPath="~/.config/Cryptomator/ipcPort.bin" \
+ -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
-Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
-Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
-Djdk.gtk.version=2 \
-Xss2m \
-Xmx512m \
- org.cryptomator.launcher.Cryptomator
+ -m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
diff --git a/dist/linux/resources/cryptomator.png b/dist/linux/resources/cryptomator.png
new file mode 100644
index 000000000..e356c35c6
Binary files /dev/null and b/dist/linux/resources/cryptomator.png differ
diff --git a/dist/mac/Cryptomator.entitlements b/dist/mac/Cryptomator.entitlements
new file mode 100644
index 000000000..00f46d649
--- /dev/null
+++ b/dist/mac/Cryptomator.entitlements
@@ -0,0 +1,16 @@
+
+
+
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-executable-page-protection
+
+ com.apple.security.cs.disable-library-validation
+
+
+
diff --git a/dist/mac/dmg/.gitignore b/dist/mac/dmg/.gitignore
new file mode 100644
index 000000000..738927994
--- /dev/null
+++ b/dist/mac/dmg/.gitignore
@@ -0,0 +1,3 @@
+# created during build
+runtime/
+*.app/
diff --git a/dist/mac/dmg/build.sh b/dist/mac/dmg/build.sh
new file mode 100755
index 000000000..6051e6f4c
--- /dev/null
+++ b/dist/mac/dmg/build.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+# parse options
+usage() { echo "Usage: $0 [-s ]" 1>&2; exit 1; }
+while getopts ":s:" o; do
+ case "${o}" in
+ s)
+ CODESIGN_IDENTITY=${OPTARG}
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+# prepare working dir and variables
+cd $(dirname $0)
+rm -rf runtime *.app
+REVISION_NO=`git rev-list --count HEAD`
+VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
+
+# check preconditions
+if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
+command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
+if [ -n "${CODESIGN_IDENTITY}" ]; then
+ command -v codesign >/dev/null 2>&1 || { echo >&2 "codesign not found. Fix by 'xcode-select --install'."; exit 1; }
+ if [[ ! `security find-identity -v -p codesigning | grep -w "${CODESIGN_IDENTITY}"` ]]; then echo "Given codesign identity is invalid."; exit 1; fi
+fi
+
+# compile
+mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
+cp ../../../target/cryptomator-*.jar ../../../target/mods
+
+# add runtime
+${JAVA_HOME}/bin/jlink \
+ --output runtime \
+ --module-path "${JAVA_HOME}/jmods" \
+ --add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility \
+ --no-header-files \
+ --no-man-pages \
+ --strip-debug \
+ --compress=1
+
+# create app dir
+${JAVA_HOME}/bin/jpackage \
+ --verbose \
+ --type app-image \
+ --runtime-image runtime \
+ --input ../../../target/libs \
+ --module-path ../../../target/mods \
+ --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
+ --dest . \
+ --name Cryptomator \
+ --vendor "Skymatic GmbH" \
+ --copyright "(C) 2016 - 2021 Skymatic GmbH" \
+ --java-options "-Xss5m" \
+ --java-options "-Xmx256m" \
+ --java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
+ --app-version "${VERSION_NO}" \
+ --java-options "-Dfile.encoding=\"utf-8\"" \
+ --java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\"" \
+ --java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" \
+ --java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" \
+ --java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" \
+ --java-options "-Dcryptomator.showTrayIcon=true" \
+ --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
+ --mac-package-identifier org.cryptomator \
+ --resource-dir ../resources
+
+# transform app dir
+cp ../resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
+sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
+sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
+
+# codesign
+if [ -n "${CODESIGN_IDENTITY}" ]; then
+ find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
+ for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
+ if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
+ JAR_FILENAME=$(basename ${JAR_PATH})
+ OUTPUT_PATH=${JAR_PATH%.*}
+ echo "Codesigning libs in ${JAR_FILENAME}..."
+ unzip -q ${JAR_PATH} -d ${OUTPUT_PATH}
+ find ${OUTPUT_PATH} -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
+ find ${OUTPUT_PATH} -name '*.jnilib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
+ rm ${JAR_PATH}
+ pushd ${OUTPUT_PATH} > /dev/null
+ zip -qr ../${JAR_FILENAME} *
+ popd > /dev/null
+ rm -r ${OUTPUT_PATH}
+ fi
+ done
+ echo "Codesigning Cryptomator.app..."
+ codesign --force --deep --entitlements ../Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
+fi
diff --git a/dist/mac/dmg/resources/Cryptomator-Volume.icns b/dist/mac/dmg/resources/Cryptomator-Volume.icns
new file mode 100644
index 000000000..92c362d56
Binary files /dev/null and b/dist/mac/dmg/resources/Cryptomator-Volume.icns differ
diff --git a/dist/mac/dmg/resources/Cryptomator-background.tiff b/dist/mac/dmg/resources/Cryptomator-background.tiff
new file mode 100644
index 000000000..c93c9a82d
Binary files /dev/null and b/dist/mac/dmg/resources/Cryptomator-background.tiff differ
diff --git a/dist/mac/dmg/resources/license.rtf b/dist/mac/dmg/resources/license.rtf
new file mode 100644
index 000000000..ae2b7ece4
--- /dev/null
+++ b/dist/mac/dmg/resources/license.rtf
@@ -0,0 +1,100 @@
+{\rtf1\ansi\ansicpg1252\cocoartf2512
+\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+{\*\expandedcolortbl;;}
+\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
+\deftab720
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
+
+\f0\b\fs24 \cf0 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.
+\f1\b0 \
+\
+
+\f0\b \'a9 2016 \'96 2021 Skymatic GmbH
+\f1\b0 \
+\
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
+\
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
+\
+You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
+\
+
+\f0\b Cryptomator uses 49 third-party dependencies under the following licenses:
+\f1\b0 \
+ Apache License v2.0:\
+ - jffi (com.github.jnr:jffi:1.2.23 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jffi"}}{\fldrslt http://github.com/jnr/jffi}})\
+ - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm}})\
+ - jnr-constants (com.github.jnr:jnr-constants:0.9.15 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-constants"}}{\fldrslt http://github.com/jnr/jnr-constants}})\
+ - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-ffi"}}{\fldrslt http://github.com/jnr/jnr-ffi}})\
+ - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - {\field{\*\fldinst{HYPERLINK "http://findbugs.sourceforge.net/"}}{\fldrslt http://findbugs.sourceforge.net/}})\
+ - Gson (com.google.code.gson:gson:2.8.6 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/gson/gson"}}{\fldrslt https://github.com/google/gson/gson}})\
+ - Dagger (com.google.dagger:dagger:2.29.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/dagger"}}{\fldrslt https://github.com/google/dagger}})\
+ - error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation}} )\
+ - Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/failureaccess"}}{\fldrslt https://github.com/google/guava/failureaccess}})\
+ - Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/guava"}}{\fldrslt https://github.com/google/guava/guava}})\
+ - Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/listenablefuture"}}{\fldrslt https://github.com/google/guava/listenablefuture}})\
+ - J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/j2objc/"}}{\fldrslt https://github.com/google/j2objc/}})\
+ - Apache Commons CLI (commons-cli:commons-cli:1.4 - {\field{\*\fldinst{HYPERLINK "http://commons.apache.org/proper/commons-cli/"}}{\fldrslt http://commons.apache.org/proper/commons-cli/}})\
+ - javax.inject (javax.inject:javax.inject:1 - {\field{\*\fldinst{HYPERLINK "http://code.google.com/p/atinject/"}}{\fldrslt http://code.google.com/p/atinject/}})\
+ - Java Native Access (net.java.dev.jna:jna:5.6.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
+ - Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
+ - Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - {\field{\*\fldinst{HYPERLINK "https://commons.apache.org/proper/commons-lang/"}}{\fldrslt https://commons.apache.org/proper/commons-lang/}})\
+ - Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - {\field{\*\fldinst{HYPERLINK "http://hc.apache.org/httpcomponents-core-ga"}}{\fldrslt http://hc.apache.org/httpcomponents-core-ga}})\
+ - Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - {\field{\*\fldinst{HYPERLINK "http://jackrabbit.apache.org/jackrabbit-webdav/"}}{\fldrslt http://jackrabbit.apache.org/jackrabbit-webdav/}})\
+ - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
+ - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
+ - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
+ - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
+ - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
+ - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
+ - Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
+ - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
+ - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
+ BSD:\
+ - asm (org.ow2.asm:asm:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
+ - asm-analysis (org.ow2.asm:asm-analysis:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
+ - asm-commons (org.ow2.asm:asm-commons:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
+ - asm-tree (org.ow2.asm:asm-tree:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
+ - asm-util (org.ow2.asm:asm-util:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
+ Eclipse Public License - Version 1.0:\
+ - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
+ - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
+ - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
+ - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
+ - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
+ - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
+ - Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
+ - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
+ - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
+ Eclipse Public License - v 2.0:\
+ - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
+ GPLv2:\
+ - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
+ GPLv2+CE:\
+ - Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - {\field{\*\fldinst{HYPERLINK "http://servlet-spec.java.net"}}{\fldrslt http://servlet-spec.java.net}})\
+ - javafx-base (org.openjfx:javafx-base:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-base/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-base/}})\
+ - javafx-controls (org.openjfx:javafx-controls:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-controls/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-controls/}})\
+ - javafx-fxml (org.openjfx:javafx-fxml:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-fxml/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-fxml/}})\
+ - javafx-graphics (org.openjfx:javafx-graphics:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-graphics/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-graphics/}})\
+ LGPL 2.1:\
+ - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
+ - Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)\
+ - Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
+ MIT License:\
+ - java jwt (com.auth0:java-jwt:3.12.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/auth0/java-jwt"}}{\fldrslt https://github.com/auth0/java-jwt}})\
+ - jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-x86asm"}}{\fldrslt http://github.com/jnr/jnr-x86asm}})\
+ - jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined)\
+ - zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/nulab/zxcvbn4j"}}{\fldrslt https://github.com/nulab/zxcvbn4j}})\
+ - Checker Qual (org.checkerframework:checker-qual:3.5.0 - {\field{\*\fldinst{HYPERLINK "https://checkerframework.org"}}{\fldrslt https://checkerframework.org}})\
+ - SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - {\field{\*\fldinst{HYPERLINK "http://www.slf4j.org"}}{\fldrslt http://www.slf4j.org}})\
+ The BSD 2-Clause License:\
+ - EasyBind (com.tobiasdiez:easybind:2.1.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/tobiasdiez/EasyBind"}}{\fldrslt https://github.com/tobiasdiez/EasyBind}})\
+\
+
+\f0\b Cryptomator uses other third-party assets under the following licenses:
+\f1\b0 \
+ SIL OFL 1.1 License:\
+ - Font Awesome 5.12.0 ({\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt https://fontawesome.com/}})\
+\
+}
\ No newline at end of file
diff --git a/dist/mac/dmg/resources/macFUSE.webloc b/dist/mac/dmg/resources/macFUSE.webloc
new file mode 100644
index 000000000..f149ff5b6
--- /dev/null
+++ b/dist/mac/dmg/resources/macFUSE.webloc
@@ -0,0 +1,8 @@
+
+
+
+
+ URL
+ https://osxfuse.github.io/
+
+
diff --git a/main/buildkit/src/main/resources/launcher-mac.sh b/dist/mac/launcher.sh
old mode 100644
new mode 100755
similarity index 61%
rename from main/buildkit/src/main/resources/launcher-mac.sh
rename to dist/mac/launcher.sh
index 2c06efab2..d8cc30b45
--- a/main/buildkit/src/main/resources/launcher-mac.sh
+++ b/dist/mac/launcher.sh
@@ -1,11 +1,12 @@
#!/bin/sh
cd $(dirname $0)
java \
+ -p "mods" \
-cp "libs/*" \
-Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" \
- -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator/ipcPort.bin" \
+ -Dcryptomator.ipcSocketPath="~/Library/Application Support/Cryptomator/ipc.socket" \
-Dcryptomator.logDir="~/Library/Logs/Cryptomator" \
-Dcryptomator.mountPointsDir="/Volumes" \
-Xss20m \
-Xmx512m \
- org.cryptomator.launcher.Cryptomator
\ No newline at end of file
+ -m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
\ No newline at end of file
diff --git a/dist/mac/resources/Cryptomator-Vault.icns b/dist/mac/resources/Cryptomator-Vault.icns
new file mode 100644
index 000000000..4bcd4c9ba
Binary files /dev/null and b/dist/mac/resources/Cryptomator-Vault.icns differ
diff --git a/dist/mac/resources/Cryptomator.icns b/dist/mac/resources/Cryptomator.icns
new file mode 100644
index 000000000..25da5b5be
Binary files /dev/null and b/dist/mac/resources/Cryptomator.icns differ
diff --git a/dist/mac/resources/Info.plist b/dist/mac/resources/Info.plist
new file mode 100644
index 000000000..5ac5938e6
--- /dev/null
+++ b/dist/mac/resources/Info.plist
@@ -0,0 +1,120 @@
+
+
+
+
+ LSMinimumSystemVersion
+ 10.13.0
+ CFBundleDevelopmentRegion
+ English
+ CFBundleAllowMixedLocalizations
+
+ CFBundleExecutable
+ Cryptomator
+ CFBundleIconFile
+ Cryptomator.icns
+ CFBundleIdentifier
+ org.cryptomator
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Cryptomator
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ ###BUNDLE_SHORT_VERSION_STRING###
+ CFBundleSignature
+ ????
+ LSApplicationCategoryType
+ public.app-category.utilities
+ CFBundleVersion
+ ###BUNDLE_VERSION###
+ NSHumanReadableCopyright
+ cryptomator.org
+ NSHighResolutionCapable
+ true
+ NSUbiquitousContainers
+
+ iCloud.com.setolabs.Cryptomator
+
+ NSUbiquitousContainerIsDocumentScopePublic
+
+ NSUbiquitousContainerName
+ Cryptomator
+ NSUbiquitousContainerSupportedFolderLevels
+ Any
+
+
+
+ CFBundleDocumentTypes
+
+
+ LSItemContentTypes
+
+ org.cryptomator.vault-metadata
+
+ CFBundleTypeIconFile
+ Cryptomator-Vault.icns
+ CFBundleTypeName
+ Cryptomator Vault Metadata
+ LSHandlerRank
+ Owner
+ CFBundleTypeRole
+ Editor
+
+
+ UTExportedTypeDeclarations
+
+
+ UTTypeIdentifier
+ org.cryptomator.vault-metadata
+ UTTypeConformsTo
+
+ public.utf8-plain-text
+ public.json
+
+ UTTypeDescription
+ Cryptomator Vault Metadata
+ UTTypeIconFile
+ Cryptomator-Vault.icns
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ cryptomator
+
+ public.mime-type
+
+ application/vnd.cryptomator.vault
+
+
+
+
+ UTTypeIdentifier
+ org.cryptomator.encrypted-data
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeDescription
+ Cryptomator Encrypted Data
+ UTTypeIconFile
+ Cryptomator-Vault.icns
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ c9r
+ c9s
+
+ public.mime-type
+
+ application/vnd.cryptomator.encrypted
+
+
+
+
+
+ NSSupportsAutomaticGraphicsSwitching
+
+
+
diff --git a/dist/win/contrib/cryptomator.bat b/dist/win/contrib/cryptomator.bat
new file mode 100644
index 000000000..73c65d07f
--- /dev/null
+++ b/dist/win/contrib/cryptomator.bat
@@ -0,0 +1,12 @@
+@echo off
+java ^
+ -p "app/mods" ^
+ -cp "app/*" ^
+ -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" ^
+ -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator/ipc.socket" ^
+ -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" ^
+ -Dcryptomator.mountPointsDir="~/Cryptomator" ^
+ -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" ^
+ -Xss20m ^
+ -Xmx512m ^
+ -m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
\ No newline at end of file
diff --git a/dist/win/contrib/dokan1.dll b/dist/win/contrib/dokan1.dll
new file mode 100755
index 000000000..badc12b2a
Binary files /dev/null and b/dist/win/contrib/dokan1.dll differ
diff --git a/dist/win/contrib/jnidispatch.dll b/dist/win/contrib/jnidispatch.dll
new file mode 100644
index 000000000..10d49b2d3
Binary files /dev/null and b/dist/win/contrib/jnidispatch.dll differ
diff --git a/main/buildkit/src/main/resources/launcher-win.bat b/dist/win/launcher.bat
similarity index 67%
rename from main/buildkit/src/main/resources/launcher-win.bat
rename to dist/win/launcher.bat
index d385d7e28..751a6e4fa 100644
--- a/main/buildkit/src/main/resources/launcher-win.bat
+++ b/dist/win/launcher.bat
@@ -1,11 +1,12 @@
@echo off
java ^
+ -p "mods" ^
-cp "libs/*" ^
-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" ^
- -Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" ^
+ -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator/ipc.socket" ^
-Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" ^
-Dcryptomator.mountPointsDir="~/Cryptomator" ^
-Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" ^
-Xss20m ^
-Xmx512m ^
- org.cryptomator.launcher.Cryptomator
\ No newline at end of file
+ -m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
\ No newline at end of file
diff --git a/dist/win/resources/Cryptomator-Vault.ico b/dist/win/resources/Cryptomator-Vault.ico
new file mode 100644
index 000000000..662089969
Binary files /dev/null and b/dist/win/resources/Cryptomator-Vault.ico differ
diff --git a/dist/win/resources/Cryptomator.ico b/dist/win/resources/Cryptomator.ico
new file mode 100644
index 000000000..7d1d8be88
Binary files /dev/null and b/dist/win/resources/Cryptomator.ico differ
diff --git a/dist/win/resources/FAvaultFile.properties b/dist/win/resources/FAvaultFile.properties
new file mode 100644
index 000000000..4d0284e69
--- /dev/null
+++ b/dist/win/resources/FAvaultFile.properties
@@ -0,0 +1,4 @@
+mime-type=application/vnd.cryptomator.vault
+extension=cryptomator
+description=Cryptomator Vault File
+icon=resources/Cryptomator-Vault.ico
\ No newline at end of file
diff --git a/dist/win/resources/background.bmp b/dist/win/resources/background.bmp
new file mode 100644
index 000000000..19b1511ed
Binary files /dev/null and b/dist/win/resources/background.bmp differ
diff --git a/dist/win/resources/banner.bmp b/dist/win/resources/banner.bmp
new file mode 100644
index 000000000..64381ac18
Binary files /dev/null and b/dist/win/resources/banner.bmp differ
diff --git a/dist/win/resources/customWizard.wxi b/dist/win/resources/customWizard.wxi
new file mode 100644
index 000000000..432681246
--- /dev/null
+++ b/dist/win/resources/customWizard.wxi
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ "1"]]>
+
+
+ 1
+
+ NOT Installed
+ Installed AND PATCH
+
+ 1
+ LicenseAccepted = "1"
+
+ 1
+ 1
+ NOT WIXUI_DONTVALIDATEPATH
+ "1"]]>
+ WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"
+ 1
+ 1
+
+ NOT Installed
+ Installed AND NOT PATCH
+ Installed AND PATCH
+
+ 1
+
+ 1
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dist/win/resources/license.rtf b/dist/win/resources/license.rtf
new file mode 100644
index 000000000..6782bebab
--- /dev/null
+++ b/dist/win/resources/license.rtf
@@ -0,0 +1,85 @@
+{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
+{\colortbl ;\red0\green0\blue255;}
+{\*\generator Riched20 10.0.17134}\viewkind4\uc1
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
+\par
+\b\'a9 2016 \endash 2021 Skymatic GmbH\b0\par
+\par
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
+\par
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
+\par
+You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
+\par
+\b Cryptomator uses 40 third-party dependencies under the following licenses:\b0\par
+\tab Apache License v2.0:\par
+\tab\tab - jffi (com.github.jnr:jffi:1.2.23 - {{\field{\*\fldinst{HYPERLINK http://github.com/jnr/jffi }}{\fldrslt{http://github.com/jnr/jffi\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - {{\field{\*\fldinst{HYPERLINK http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm }}{\fldrslt{http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - jnr-constants (com.github.jnr:jnr-constants:0.9.15 - {{\field{\*\fldinst{HYPERLINK http://github.com/jnr/jnr-constants }}{\fldrslt{http://github.com/jnr/jnr-constants\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - {{\field{\*\fldinst{HYPERLINK http://github.com/jnr/jnr-ffi }}{\fldrslt{http://github.com/jnr/jnr-ffi\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Gson (com.google.code.gson:gson:2.8.7 - {{\field{\*\fldinst{HYPERLINK https://github.com/google/gson/gson }}{\fldrslt{https://github.com/google/gson/gson\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Dagger (com.google.dagger:dagger:2.38.1 - {{\field{\*\fldinst{HYPERLINK https://github.com/google/dagger }}{\fldrslt{https://github.com/google/dagger\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - {{\field{\*\fldinst{HYPERLINK https://github.com/google/guava/failureaccess }}{\fldrslt{https://github.com/google/guava/failureaccess\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Guava: Google Core Libraries for Java (com.google.guava:guava:30.1.1-jre - {{\field{\*\fldinst{HYPERLINK https://github.com/google/guava/guava }}{\fldrslt{https://github.com/google/guava/guava\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Apache Commons CLI (commons-cli:commons-cli:1.4 - {{\field{\*\fldinst{HYPERLINK http://commons.apache.org/proper/commons-cli/ }}{\fldrslt{http://commons.apache.org/proper/commons-cli/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - javax.inject (javax.inject:javax.inject:1 - {{\field{\*\fldinst{HYPERLINK http://code.google.com/p/atinject/ }}{\fldrslt{http://code.google.com/p/atinject/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Java Native Access (net.java.dev.jna:jna:5.7.0 - {{\field{\*\fldinst{HYPERLINK https://github.com/java-native-access/jna }}{\fldrslt{https://github.com/java-native-access/jna\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Java Native Access Platform (net.java.dev.jna:jna-platform:5.7.0 - {{\field{\*\fldinst{HYPERLINK https://github.com/java-native-access/jna }}{\fldrslt{https://github.com/java-native-access/jna\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Apache Commons Lang (org.apache.commons:commons-lang3:3.12.0 - {{\field{\*\fldinst{HYPERLINK https://commons.apache.org/proper/commons-lang/ }}{\fldrslt{https://commons.apache.org/proper/commons-lang/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.14 - {{\field{\*\fldinst{HYPERLINK http://hc.apache.org/httpcomponents-core-ga }}{\fldrslt{http://hc.apache.org/httpcomponents-core-ga\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.5 - {{\field{\*\fldinst{HYPERLINK http://jackrabbit.apache.org/jackrabbit-webdav/ }}{\fldrslt{http://jackrabbit.apache.org/jackrabbit-webdav/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-http }}{\fldrslt{https://eclipse.org/jetty/jetty-http\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-io }}{\fldrslt{https://eclipse.org/jetty/jetty-io\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-security }}{\fldrslt{https://eclipse.org/jetty/jetty-security\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-server }}{\fldrslt{https://eclipse.org/jetty/jetty-server\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-servlet }}{\fldrslt{https://eclipse.org/jetty/jetty-servlet\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-util }}{\fldrslt{https://eclipse.org/jetty/jetty-util\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-servlet-api }}{\fldrslt{https://eclipse.org/jetty/jetty-servlet-api\ul0\cf0}}}}\f0\fs16 )\par
+\tab BSD:\par
+\tab\tab - asm (org.ow2.asm:asm:7.1 - {{\field{\*\fldinst{HYPERLINK http://asm.ow2.org/ }}{\fldrslt{http://asm.ow2.org/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - asm-analysis (org.ow2.asm:asm-analysis:7.1 - {{\field{\*\fldinst{HYPERLINK http://asm.ow2.org/ }}{\fldrslt{http://asm.ow2.org/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - asm-commons (org.ow2.asm:asm-commons:7.1 - {{\field{\*\fldinst{HYPERLINK http://asm.ow2.org/ }}{\fldrslt{http://asm.ow2.org/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - asm-tree (org.ow2.asm:asm-tree:7.1 - {{\field{\*\fldinst{HYPERLINK http://asm.ow2.org/ }}{\fldrslt{http://asm.ow2.org/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - asm-util (org.ow2.asm:asm-util:7.1 - {{\field{\*\fldinst{HYPERLINK http://asm.ow2.org/ }}{\fldrslt{http://asm.ow2.org/\ul0\cf0}}}}\f0\fs16 )\par
+\tab Eclipse Public License - Version 1.0:\par
+\tab\tab - Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-servlet-api }}{\fldrslt{https://eclipse.org/jetty/jetty-servlet-api\ul0\cf0}}}}\f0\fs16 )\par
+\tab Eclipse Public License - Version 2.0:\par
+\tab\tab - Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-http }}{\fldrslt{https://eclipse.org/jetty/jetty-http\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-io }}{\fldrslt{https://eclipse.org/jetty/jetty-io\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-security }}{\fldrslt{https://eclipse.org/jetty/jetty-security\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-server }}{\fldrslt{https://eclipse.org/jetty/jetty-server\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-servlet }}{\fldrslt{https://eclipse.org/jetty/jetty-servlet\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.6 - {{\field{\*\fldinst{HYPERLINK https://eclipse.org/jetty/jetty-util }}{\fldrslt{https://eclipse.org/jetty/jetty-util\ul0\cf0}}}}\f0\fs16 )\par
+\tab Eclipse Public License - v 1.0:\par
+\tab\tab - Logback Classic Module (ch.qos.logback:logback-classic:1.2.3 - {{\field{\*\fldinst{HYPERLINK http://logback.qos.ch/logback-classic }}{\fldrslt{http://logback.qos.ch/logback-classic\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Logback Core Module (ch.qos.logback:logback-core:1.2.3 - {{\field{\*\fldinst{HYPERLINK http://logback.qos.ch/logback-core }}{\fldrslt{http://logback.qos.ch/logback-core\ul0\cf0}}}}\f0\fs16 )\par
+\tab Eclipse Public License - v 2.0:\par
+\tab\tab - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {{\field{\*\fldinst{HYPERLINK http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix }}{\fldrslt{http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix\ul0\cf0}}}}\f0\fs16 )\par
+\tab GNU Lesser General Public License:\par
+\tab\tab - Logback Classic Module (ch.qos.logback:logback-classic:1.2.3 - {{\field{\*\fldinst{HYPERLINK http://logback.qos.ch/logback-classic }}{\fldrslt{http://logback.qos.ch/logback-classic\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Logback Core Module (ch.qos.logback:logback-core:1.2.3 - {{\field{\*\fldinst{HYPERLINK http://logback.qos.ch/logback-core }}{\fldrslt{http://logback.qos.ch/logback-core\ul0\cf0}}}}\f0\fs16 )\par
+\tab GPLv2:\par
+\tab\tab - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {{\field{\*\fldinst{HYPERLINK http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix }}{\fldrslt{http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix\ul0\cf0}}}}\f0\fs16 )\par
+\tab GPLv2+CE:\par
+\tab\tab - javafx-base (org.openjfx:javafx-base:16 - {{\field{\*\fldinst{HYPERLINK https://openjdk.java.net/projects/openjfx/javafx-base/ }}{\fldrslt{https://openjdk.java.net/projects/openjfx/javafx-base/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - javafx-controls (org.openjfx:javafx-controls:16 - {{\field{\*\fldinst{HYPERLINK https://openjdk.java.net/projects/openjfx/javafx-controls/ }}{\fldrslt{https://openjdk.java.net/projects/openjfx/javafx-controls/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - javafx-fxml (org.openjfx:javafx-fxml:16 - {{\field{\*\fldinst{HYPERLINK https://openjdk.java.net/projects/openjfx/javafx-fxml/ }}{\fldrslt{https://openjdk.java.net/projects/openjfx/javafx-fxml/\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - javafx-graphics (org.openjfx:javafx-graphics:16 - {{\field{\*\fldinst{HYPERLINK https://openjdk.java.net/projects/openjfx/javafx-graphics/ }}{\fldrslt{https://openjdk.java.net/projects/openjfx/javafx-graphics/\ul0\cf0}}}}\f0\fs16 )\par
+\tab LGPL 2.1:\par
+\tab\tab - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {{\field{\*\fldinst{HYPERLINK http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix }}{\fldrslt{http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Java Native Access (net.java.dev.jna:jna:5.7.0 - {{\field{\*\fldinst{HYPERLINK https://github.com/java-native-access/jna }}{\fldrslt{https://github.com/java-native-access/jna\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - Java Native Access Platform (net.java.dev.jna:jna-platform:5.7.0 - {{\field{\*\fldinst{HYPERLINK https://github.com/java-native-access/jna }}{\fldrslt{https://github.com/java-native-access/jna\ul0\cf0}}}}\f0\fs16 )\par
+\tab MIT License:\par
+\tab\tab - java jwt (com.auth0:java-jwt:3.18.1 - {{\field{\*\fldinst{HYPERLINK https://github.com/auth0/java-jwt }}{\fldrslt{https://github.com/auth0/java-jwt\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - {{\field{\*\fldinst{HYPERLINK http://github.com/jnr/jnr-x86asm }}{\fldrslt{http://github.com/jnr/jnr-x86asm\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - jnr-fuse (com.github.serceman:jnr-fuse:0.5.5 - {{\field{\*\fldinst{HYPERLINK https://github.com/SerCeMan/jnr-fuse }}{\fldrslt{https://github.com/SerCeMan/jnr-fuse\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - zxcvbn4j (com.nulab-inc:zxcvbn:1.5.2 - {{\field{\*\fldinst{HYPERLINK https://github.com/nulab/zxcvbn4j }}{\fldrslt{https://github.com/nulab/zxcvbn4j\ul0\cf0}}}}\f0\fs16 )\par
+\tab\tab - SLF4J API Module (org.slf4j:slf4j-api:1.7.31 - {{\field{\*\fldinst{HYPERLINK http://www.slf4j.org }}{\fldrslt{http://www.slf4j.org\ul0\cf0}}}}\f0\fs16 )\par
+\tab The BSD 2-Clause License:\par
+\tab\tab - EasyBind (com.tobiasdiez:easybind:2.2 - {{\field{\*\fldinst{HYPERLINK https://github.com/tobiasdiez/EasyBind }}{\fldrslt{https://github.com/tobiasdiez/EasyBind\ul0\cf0}}}}\f0\fs16 )\par
+\par
+\b Cryptomator uses other third-party assets under the following licenses:\b0\par
+\tab SIL OFL 1.1 License:\par
+\tab\tab - Font Awesome 5.12.0 ({{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
+}
+
\ No newline at end of file
diff --git a/dist/win/resources/main.wxs b/dist/win/resources/main.wxs
new file mode 100644
index 000000000..c1384c2cf
--- /dev/null
+++ b/dist/win/resources/main.wxs
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ INSTALLDIR_VALID="0"
+ INSTALLDIR_VALID="1"
+
+
+
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not Installed
+
+ JP_UPGRADABLE_FOUND
+
+
+ JP_DOWNGRADABLE_FOUND
+
+
+
+
+
+
+
+
+
+
diff --git a/main/buildkit/assembly-linux.xml b/main/buildkit/assembly-linux.xml
deleted file mode 100644
index 1e16da9e2..000000000
--- a/main/buildkit/assembly-linux.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- tarball
- false
-
- zip
-
-
-
- target/
-
- version.txt
-
- libs
-
-
- target/
-
- LICENSE.txt
-
-
-
-
- target/
-
- launcher-linux.sh
-
-
- 0755
-
-
- target/libs
-
- *.jar
-
- libs
-
-
-
\ No newline at end of file
diff --git a/main/buildkit/assembly-mac.xml b/main/buildkit/assembly-mac.xml
deleted file mode 100644
index 4a01f4067..000000000
--- a/main/buildkit/assembly-mac.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- tarball
- false
-
- zip
-
-
-
- target/
-
- version.txt
-
- libs
-
-
- target/
-
- LICENSE.txt
-
-
-
-
- target/
-
- launcher-mac.sh
-
-
- 0755
-
-
- target/libs
-
- *.jar
-
- libs
-
-
-
\ No newline at end of file
diff --git a/main/buildkit/assembly-win.xml b/main/buildkit/assembly-win.xml
deleted file mode 100644
index 0297f3ec4..000000000
--- a/main/buildkit/assembly-win.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- tarball
- false
-
- zip
-
-
-
- target/
-
- version.txt
-
- libs
-
-
- target/
-
- LICENSE.txt
-
-
-
-
- target/
-
- launcher-win.bat
-
-
- 0755
-
-
- target/libs
-
- *.jar
-
- libs
-
-
-
\ No newline at end of file
diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml
deleted file mode 100644
index bbbc9ddf5..000000000
--- a/main/buildkit/pom.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
- 4.0.0
-
- org.cryptomator
- main
- 1.5.19
-
- buildkit
- pom
- Cryptomator Build Kit
- Builds a package that can be built with Ant locally
-
-
-
- org.cryptomator
- launcher
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-resources-plugin
-
-
- copy-resources
- prepare-package
-
- copy-resources
-
-
- ${project.build.directory}
-
-
- ${project.basedir}/src/main/resources
-
- version.txt
- ffi-version.txt
- launcher-mac.sh
- launcher-linux.sh
- launcher-win.bat
- LICENSE.txt
-
- true
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
- prepare-package
-
- copy-dependencies
-
-
- runtime
- ${project.build.directory}/libs
- linux,mac,win
-
-
-
-
-
-
-
-
-
- linux
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- assemble-linux
- package
-
- single
-
-
-
- assembly-linux.xml
-
- false
- buildkit-linux
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-linux-libs
- prepare-package
-
- copy-dependencies
-
-
- ${project.build.directory}/libs
- org.openjfx
- linux
-
-
-
-
-
-
-
-
-
- mac
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- assemble-mac
- package
-
- single
-
-
-
- assembly-mac.xml
-
- false
- buildkit-mac
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-mac-libs
- prepare-package
-
- copy-dependencies
-
-
- ${project.build.directory}/libs
- org.openjfx
- mac
-
-
-
-
-
-
-
-
-
- windows
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- assemble-win
- package
-
- single
-
-
-
- assembly-win.xml
-
- false
- buildkit-win
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-win-libs
- prepare-package
-
- copy-dependencies
-
-
- ${project.build.directory}/libs
- org.openjfx
- win
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/main/buildkit/src/main/resources/LICENSE.txt b/main/buildkit/src/main/resources/LICENSE.txt
deleted file mode 100644
index 20d40b6bc..000000000
--- a/main/buildkit/src/main/resources/LICENSE.txt
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
\ No newline at end of file
diff --git a/main/commons/pom.xml b/main/commons/pom.xml
deleted file mode 100644
index cfbb5deb5..000000000
--- a/main/commons/pom.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
- 4.0.0
-
- org.cryptomator
- main
- 1.5.19
-
- commons
- Cryptomator Commons
- Shared utilities
-
-
-
- org.cryptomator
- cryptofs
-
-
- org.cryptomator
- fuse-nio-adapter
-
-
- org.cryptomator
- dokany-nio-adapter
-
-
- org.cryptomator
- webdav-nio-adapter
-
-
- org.cryptomator
- integrations-api
-
-
-
-
- org.openjfx
- javafx-base
-
-
- org.openjfx
- javafx-graphics
-
-
-
-
- com.tobiasdiez
- easybind
-
-
-
-
- com.auth0
- java-jwt
-
-
-
-
- com.google.guava
- guava
-
-
- com.google.code.gson
- gson
-
-
-
-
- org.apache.commons
- commons-lang3
-
-
-
-
- com.google.dagger
- dagger
-
-
-
-
- org.slf4j
- slf4j-simple
- test
-
-
-
diff --git a/main/commons/src/main/java/org/cryptomator/common/Constants.java b/main/commons/src/main/java/org/cryptomator/common/Constants.java
deleted file mode 100644
index 432cd08e5..000000000
--- a/main/commons/src/main/java/org/cryptomator/common/Constants.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.cryptomator.common;
-
-public interface Constants {
-
- String MASTERKEY_FILENAME = "masterkey.cryptomator";
-
-}
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java b/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java
deleted file mode 100644
index 65f869a12..000000000
--- a/main/commons/src/main/java/org/cryptomator/common/settings/KeychainBackend.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.cryptomator.common.settings;
-
-public enum KeychainBackend {
- GNOME("org.cryptomator.linux.keychain.SecretServiceKeychainAccess"),
- KDE("org.cryptomator.linux.keychain.KDEWalletKeychainAccess"),
- MAC_SYSTEM_KEYCHAIN("org.cryptomator.macos.keychain.MacSystemKeychainAccess"),
- WIN_SYSTEM_KEYCHAIN("org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess");
-
- private final String providerClass;
-
- KeychainBackend(String providerClass) {
- this.providerClass = providerClass;
- }
-
- public String getProviderClass() {
- return providerClass;
- }
-
-}
diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml
deleted file mode 100644
index 12cde71db..000000000
--- a/main/launcher/pom.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
- 4.0.0
-
- org.cryptomator
- main
- 1.5.19
-
- launcher
- Cryptomator Launcher
-
-
-
- org.cryptomator
- commons
-
-
- org.cryptomator
- ui
-
-
-
-
- com.google.guava
- guava
-
-
- org.apache.commons
- commons-lang3
-
-
-
-
- com.google.dagger
- dagger
-
-
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
-
\ No newline at end of file
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java b/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java
deleted file mode 100644
index 112050d26..000000000
--- a/main/launcher/src/main/java/org/cryptomator/launcher/IpcFactory.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the accompanying LICENSE file.
- *******************************************************************************/
-package org.cryptomator.launcher;
-
-import com.google.common.io.MoreFiles;
-import org.cryptomator.common.Environment;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.rmi.NotBoundException;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
-import java.rmi.server.RMIClientSocketFactory;
-import java.rmi.server.RMIServerSocketFactory;
-import java.rmi.server.RMISocketFactory;
-import java.rmi.server.UnicastRemoteObject;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/**
- * First running application on a machine opens a server socket. Further processes will connect as clients.
- */
-@Singleton
-class IpcFactory {
-
- private static final Logger LOG = LoggerFactory.getLogger(IpcFactory.class);
- private static final String RMI_NAME = "Cryptomator";
-
- private final List portFilePaths;
- private final IpcProtocolImpl ipcHandler;
-
- @Inject
- public IpcFactory(Environment env, IpcProtocolImpl ipcHandler) {
- this.portFilePaths = env.getIpcPortPath().collect(Collectors.toUnmodifiableList());
- this.ipcHandler = ipcHandler;
- }
-
- public IpcEndpoint create() {
- if (portFilePaths.isEmpty()) {
- LOG.warn("No IPC port file path specified.");
- return new SelfEndpoint(ipcHandler);
- } else {
- System.setProperty("java.rmi.server.hostname", "localhost");
- return attemptClientConnection().or(this::createServerEndpoint).orElseGet(() -> new SelfEndpoint(ipcHandler));
- }
- }
-
- private Optional attemptClientConnection() {
- for (Path portFilePath : portFilePaths) {
- try {
- int port = readPort(portFilePath);
- LOG.debug("[Client] Connecting to port {}...", port);
- Registry registry = LocateRegistry.getRegistry("localhost", port, new ClientSocketFactory());
- IpcProtocol remoteInterface = (IpcProtocol) registry.lookup(RMI_NAME);
- return Optional.of(new ClientEndpoint(remoteInterface));
- } catch (NotBoundException | IOException e) {
- LOG.debug("[Client] Failed to connect.");
- // continue with next portFilePath...
- }
- }
- return Optional.empty();
- }
-
- private int readPort(Path portFilePath) throws IOException {
- try (ReadableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.READ)) {
- LOG.debug("[Client] Reading IPC port from {}", portFilePath);
- ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
- if (ch.read(buf) == Integer.BYTES) {
- buf.flip();
- return buf.getInt();
- } else {
- throw new IOException("Invalid IPC port file.");
- }
- }
- }
-
- private Optional createServerEndpoint() {
- assert !portFilePaths.isEmpty();
- Path portFilePath = portFilePaths.get(0);
- try {
- ServerSocket socket = new ServerSocket(0, Byte.MAX_VALUE, InetAddress.getByName("localhost"));
- RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory();
- SingletonServerSocketFactory ssf = new SingletonServerSocketFactory(socket);
- Registry registry = LocateRegistry.createRegistry(0, csf, ssf);
- UnicastRemoteObject.exportObject(ipcHandler, 0);
- registry.rebind(RMI_NAME, ipcHandler);
- writePort(portFilePath, socket.getLocalPort());
- return Optional.of(new ServerEndpoint(ipcHandler, socket, registry, portFilePath));
- } catch (IOException e) {
- LOG.warn("[Server] Failed to create IPC server.", e);
- return Optional.empty();
- }
- }
-
- private void writePort(Path portFilePath, int port) throws IOException {
- ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
- buf.putInt(port);
- buf.flip();
- MoreFiles.createParentDirectories(portFilePath);
- try (WritableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
- if (ch.write(buf) != Integer.BYTES) {
- throw new IOException("Did not write expected number of bytes.");
- }
- }
- LOG.debug("[Server] Wrote IPC port {} to {}", port, portFilePath);
- }
-
- interface IpcEndpoint extends Closeable {
-
- boolean isConnectedToRemote();
-
- IpcProtocol getRemote();
-
- }
-
- static class SelfEndpoint implements IpcEndpoint {
-
- protected final IpcProtocol remoteObject;
-
- SelfEndpoint(IpcProtocol remoteObject) {
- this.remoteObject = remoteObject;
- }
-
- @Override
- public boolean isConnectedToRemote() {
- return false;
- }
-
- @Override
- public IpcProtocol getRemote() {
- return remoteObject;
- }
-
- @Override
- public void close() {
- // no-op
- }
- }
-
- static class ClientEndpoint implements IpcEndpoint {
-
- private final IpcProtocol remoteInterface;
-
- public ClientEndpoint(IpcProtocol remoteInterface) {
- this.remoteInterface = remoteInterface;
- }
-
- public IpcProtocol getRemote() {
- return remoteInterface;
- }
-
- @Override
- public boolean isConnectedToRemote() {
- return true;
- }
-
- @Override
- public void close() {
- // no-op
- }
-
- }
-
- class ServerEndpoint extends SelfEndpoint {
-
- private final ServerSocket socket;
- private final Registry registry;
- private final Path portFilePath;
-
- private ServerEndpoint(IpcProtocol remoteObject, ServerSocket socket, Registry registry, Path portFilePath) {
- super(remoteObject);
- this.socket = socket;
- this.registry = registry;
- this.portFilePath = portFilePath;
- }
-
- @Override
- public void close() {
- try {
- registry.unbind(RMI_NAME);
- UnicastRemoteObject.unexportObject(remoteObject, true);
- socket.close();
- Files.deleteIfExists(portFilePath);
- LOG.debug("[Server] Shut down");
- } catch (NotBoundException | IOException e) {
- LOG.warn("[Server] Error shutting down:", e);
- }
- }
-
- }
-
- /**
- * Always returns the same pre-constructed server socket.
- */
- private static class SingletonServerSocketFactory implements RMIServerSocketFactory {
-
- private final ServerSocket socket;
-
- public SingletonServerSocketFactory(ServerSocket socket) {
- this.socket = socket;
- }
-
- @Override
- public synchronized ServerSocket createServerSocket(int port) throws IOException {
- if (port != 0) {
- throw new IllegalArgumentException("This factory doesn't support specific ports.");
- }
- return this.socket;
- }
-
- }
-
- /**
- * Creates client sockets with short timeouts.
- */
- private static class ClientSocketFactory implements RMIClientSocketFactory {
-
- @Override
- public Socket createSocket(String host, int port) throws IOException {
- return new SocketWithFixedTimeout(host, port, 1000);
- }
-
- }
-
- private static class SocketWithFixedTimeout extends Socket {
-
- public SocketWithFixedTimeout(String host, int port, int timeoutInMs) throws UnknownHostException, IOException {
- super(host, port);
- super.setSoTimeout(timeoutInMs);
- }
-
- @Override
- public synchronized void setSoTimeout(int timeout) throws SocketException {
- // do nothing, timeout is fixed
- }
-
- }
-
-}
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java b/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java
deleted file mode 100644
index 3e0596d77..000000000
--- a/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocol.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the accompanying LICENSE file.
- *******************************************************************************/
-package org.cryptomator.launcher;
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-
-interface IpcProtocol extends Remote {
-
- void revealRunningApp() throws RemoteException;
-
- void handleLaunchArgs(String... args) throws RemoteException;
-
-}
\ No newline at end of file
diff --git a/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java b/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java
deleted file mode 100644
index aeb9fbeb3..000000000
--- a/main/launcher/src/test/java/org/cryptomator/launcher/IpcFactoryTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the accompanying LICENSE file.
- *******************************************************************************/
-package org.cryptomator.launcher;
-
-import org.cryptomator.common.Environment;
-import org.cryptomator.launcher.IpcFactory.IpcEndpoint;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Mockito;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.stream.Stream;
-
-public class IpcFactoryTest {
-
- private Environment environment = Mockito.mock(Environment.class);
- private IpcProtocolImpl protocolHandler = Mockito.mock(IpcProtocolImpl.class);
-
- @Test
- @DisplayName("Wihout IPC port files")
- public void testNoIpcWithoutPortFile() throws IOException {
- IpcFactory inTest = new IpcFactory(environment, protocolHandler);
-
- Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.empty());
- try (IpcEndpoint endpoint1 = inTest.create()) {
- Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint1.getClass());
- Assertions.assertFalse(endpoint1.isConnectedToRemote());
- Assertions.assertSame(protocolHandler, endpoint1.getRemote());
- try (IpcEndpoint endpoint2 = inTest.create()) {
- Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint2.getClass());
- Assertions.assertNotSame(endpoint1, endpoint2);
- Assertions.assertFalse(endpoint2.isConnectedToRemote());
- Assertions.assertSame(protocolHandler, endpoint2.getRemote());
- }
- }
- }
-
- @Test
- @DisplayName("Start server and client with port shared via file")
- public void testInterProcessCommunication(@TempDir Path tmpDir) throws IOException {
- Path portFile = tmpDir.resolve("testPortFile");
- Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.of(portFile));
- IpcFactory inTest = new IpcFactory(environment, protocolHandler);
-
- Assertions.assertFalse(Files.exists(portFile));
- try (IpcEndpoint endpoint1 = inTest.create()) {
- Assertions.assertEquals(IpcFactory.ServerEndpoint.class, endpoint1.getClass());
- Assertions.assertFalse(endpoint1.isConnectedToRemote());
- Assertions.assertTrue(Files.exists(portFile));
- Assertions.assertSame(protocolHandler, endpoint1.getRemote());
- Mockito.verifyZeroInteractions(protocolHandler);
- try (IpcEndpoint endpoint2 = inTest.create()) {
- Assertions.assertEquals(IpcFactory.ClientEndpoint.class, endpoint2.getClass());
- Assertions.assertNotSame(endpoint1, endpoint2);
- Assertions.assertTrue(endpoint2.isConnectedToRemote());
- Assertions.assertNotSame(protocolHandler, endpoint2.getRemote());
- Mockito.verifyZeroInteractions(protocolHandler);
- endpoint2.getRemote().handleLaunchArgs(new String[]{"foo"});
- Mockito.verify(protocolHandler).handleLaunchArgs(new String[]{"foo"});
- }
- }
- }
-
-}
diff --git a/main/pom.xml b/main/pom.xml
deleted file mode 100644
index 39d8879ec..000000000
--- a/main/pom.xml
+++ /dev/null
@@ -1,445 +0,0 @@
-
-
- 4.0.0
- org.cryptomator
- main
- 1.5.19
- pom
- Cryptomator
-
-
- cryptomator.org
- https://cryptomator.org
-
-
-
-
- Sebastian Stenzel
- sebastian.stenzel@gmail.com
- +1
-
-
-
-
- UTF-8
- 16
-
-
- 1.9.15
- 1.0.0-beta2
- 1.0.0-beta2
- 1.0.0-beta2
- 1.0.0-beta1
- 1.3.2
- 1.3.2
- 1.2.6
-
-
- 16
- 3.11
- 3.13.0
- 2.1.0
- 30.0-jre
- 2.32
- 2.8.6
- 1.7.30
- 1.2.3
-
-
- 5.7.0
- 3.6.0
- 2.2
-
-
-
-
-
-
- org.cryptomator
- commons
- ${project.version}
-
-
- org.cryptomator
- ui
- ${project.version}
-
-
- org.cryptomator
- launcher
- ${project.version}
-
-
-
-
- org.cryptomator
- cryptofs
- ${cryptomator.cryptofs.version}
-
-
- org.cryptomator
- fuse-nio-adapter
- ${cryptomator.fuse.version}
-
-
- org.cryptomator
- dokany-nio-adapter
- ${cryptomator.dokany.version}
-
-
- org.cryptomator
- webdav-nio-adapter
- ${cryptomator.webdav.version}
-
-
- org.cryptomator
- integrations-api
- ${cryptomator.integrations.version}
-
-
- org.cryptomator
- integrations-win
- ${cryptomator.integrations.win.version}
-
-
- org.cryptomator
- integrations-mac
- ${cryptomator.integrations.mac.version}
-
-
- org.cryptomator
- integrations-linux
- ${cryptomator.integrations.linux.version}
-
-
-
-
- org.openjfx
- javafx-base
- ${javafx.version}
-
-
- org.openjfx
- javafx-graphics
- ${javafx.version}
-
-
- org.openjfx
- javafx-controls
- ${javafx.version}
-
-
- org.openjfx
- javafx-fxml
- ${javafx.version}
-
-
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
- org.slf4j
- slf4j-simple
- ${slf4j.version}
-
-
- ch.qos.logback
- logback-core
- ${logback.version}
-
-
- ch.qos.logback
- logback-classic
- ${logback.version}
-
-
-
-
- org.apache.commons
- commons-lang3
- ${commons-lang3.version}
-
-
-
-
- com.auth0
- java-jwt
- ${jwt.version}
-
-
-
-
- com.tobiasdiez
- easybind
- ${easybind.version}
-
-
-
-
- com.google.guava
- guava
- ${guava.version}
-
-
- com.google.dagger
- dagger
- ${dagger.version}
-
-
- com.google.code.gson
- gson
- ${gson.version}
-
-
-
-
- org.junit.jupiter
- junit-jupiter
- ${junit.jupiter.version}
- test
-
-
- org.mockito
- mockito-core
- ${mockito.version}
-
-
- org.hamcrest
- hamcrest
- ${hamcrest.version}
-
-
- org.openjfx
- javafx-swing
- ${javafx.version}
- test
-
-
-
-
-
-
-
-
- org.slf4j
- slf4j-api
-
-
- org.junit.jupiter
- junit-jupiter
- test
-
-
- org.hamcrest
- hamcrest
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
- commons
- ui
- launcher
-
-
-
-
- release
-
- buildkit
-
-
-
- coverage
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
-
-
-
- mac
-
-
- mac
-
-
- idea.version
-
-
-
-
- org.cryptomator
- integrations-mac
-
-
-
-
- linux
-
-
- unix
- Linux
-
-
- idea.version
-
-
-
-
- org.cryptomator
- integrations-linux
-
-
-
-
- windows
-
-
- windows
-
-
- idea.version
-
-
-
-
- org.cryptomator
- integrations-win
-
-
-
-
- dependency-check
-
-
-
- org.owasp
- dependency-check-maven
- 6.0.3
-
- 24
- 0
- true
- true
- suppression.xml
-
-
-
-
- check
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.1
-
-
- org.apache.maven.plugins
- maven-resources-plugin
- 3.2.0
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- 3.1.2
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
- 3.3.0
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.22.2
-
-
- org.codehaus.mojo
- license-maven-plugin
- 2.0.0
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 3.2.0
-
-
-
-
- true
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.6
-
-
- prepare-agent
-
- prepare-agent
-
-
-
- report
-
- report
-
-
-
-
-
- **/*_*
- **/Dagger*
-
-
-
-
-
-
-
- maven-compiler-plugin
-
- ${project.jdk.version}
-
-
- com.google.dagger
- dagger-compiler
- ${dagger.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
-
-
-
diff --git a/main/ui/pom.xml b/main/ui/pom.xml
deleted file mode 100644
index 868eb2cdd..000000000
--- a/main/ui/pom.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
- 4.0.0
-
- org.cryptomator
- main
- 1.5.19
-
- ui
- Cryptomator GUI
-
-
-
- org.cryptomator
- commons
-
-
-
-
- org.openjfx
- javafx-controls
-
-
- org.openjfx
- javafx-fxml
-
-
-
-
- com.tobiasdiez
- easybind
-
-
-
-
- com.google.guava
- guava
-
-
- com.google.code.gson
- gson
-
-
-
-
- org.apache.commons
- commons-lang3
-
-
-
-
- com.google.dagger
- dagger
-
-
-
-
- com.nulab-inc
- zxcvbn
- 1.3.0
-
-
-
-
- org.slf4j
- slf4j-simple
- test
-
-
-
-
- com.google.jimfs
- jimfs
- 1.1
- test
-
-
- org.openjfx
- javafx-swing
- test
-
-
-
-
-
-
- org.codehaus.mojo
- license-maven-plugin
-
-
- add-third-party
-
- add-third-party
-
- generate-resources
-
- ${project.basedir}/src/main/resources/license
- THIRD-PARTY.txt
- compile
- org\.cryptomator
-
- Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0
- MIT License|The MIT License (MIT)|The MIT License|MIT license
- LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
- GPLv2|GNU General Public License Version 2
- GPLv2+CE|CDDL + GPLv2 with classpath exception
-
- ${project.basedir}/src/license/template.ftl
-
-
-
-
-
-
-
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorController.java b/main/ui/src/main/java/org/cryptomator/ui/common/ErrorController.java
deleted file mode 100644
index c7f19a9e6..000000000
--- a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorController.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.cryptomator.ui.common;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javafx.fxml.FXML;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-
-public class ErrorController implements FxController {
-
- private final String stackTrace;
- private final Scene previousScene;
- private final Stage window;
-
- @Inject
- ErrorController(@Named("stackTrace") String stackTrace, @Nullable Scene previousScene, Stage window) {
- this.stackTrace = stackTrace;
- this.previousScene = previousScene;
- this.window = window;
- }
-
- @FXML
- public void back() {
- if (previousScene != null) {
- window.setScene(previousScene);
- }
- }
-
- @FXML
- public void close() {
- window.close();
- }
-
- /* Getter/Setter */
-
- public boolean isPreviousScenePresent() {
- return previousScene != null;
- }
-
- public String getStackTrace() {
- return stackTrace;
- }
-}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java
deleted file mode 100644
index 908944d68..000000000
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package org.cryptomator.ui.mainwindow;
-
-import org.cryptomator.ui.common.FxController;
-
-import javax.inject.Inject;
-import javafx.fxml.FXML;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.layout.Region;
-import javafx.stage.Stage;
-
-@MainWindow
-public class ResizeController implements FxController {
-
- private final Stage window;
-
- public Region tlResizer;
- public Region trResizer;
- public Region blResizer;
- public Region brResizer;
-
- private double origX, origY, origW, origH;
-
- @Inject
- ResizeController(@MainWindow Stage window) {
- this.window = window;
- // TODO inject settings and save current position and size
- }
-
- @FXML
- public void initialize() {
- tlResizer.setOnMousePressed(this::startResize);
- trResizer.setOnMousePressed(this::startResize);
- blResizer.setOnMousePressed(this::startResize);
- brResizer.setOnMousePressed(this::startResize);
- tlResizer.setOnMouseDragged(this::resizeTopLeft);
- trResizer.setOnMouseDragged(this::resizeTopRight);
- blResizer.setOnMouseDragged(this::resizeBottomLeft);
- brResizer.setOnMouseDragged(this::resizeBottomRight);
- }
-
- private void startResize(MouseEvent evt) {
- origX = window.getX();
- origY = window.getY();
- origW = window.getWidth();
- origH = window.getHeight();
- }
-
- private void resizeTopLeft(MouseEvent evt) {
- resizeTop(evt);
- resizeLeft(evt);
- }
-
- private void resizeTopRight(MouseEvent evt) {
- resizeTop(evt);
- resizeRight(evt);
- }
-
- private void resizeBottomLeft(MouseEvent evt) {
- resizeBottom(evt);
- resizeLeft(evt);
- }
-
- private void resizeBottomRight(MouseEvent evt) {
- resizeBottom(evt);
- resizeRight(evt);
- }
-
- private void resizeTop(MouseEvent evt) {
- double newY = evt.getScreenY();
- double dy = newY - origY;
- double newH = origH - dy;
- if (newH < window.getMaxHeight() && newH > window.getMinHeight()) {
- window.setY(newY);
- window.setHeight(newH);
- }
- }
-
- private void resizeLeft(MouseEvent evt) {
- double newX = evt.getScreenX();
- double dx = newX - origX;
- double newW = origW - dx;
- if (newW < window.getMaxWidth() && newW > window.getMinWidth()) {
- window.setX(newX);
- window.setWidth(newW);
- }
- }
-
- private void resizeBottom(MouseEvent evt) {
- double newH = evt.getSceneY();
- if (newH < window.getMaxHeight() && newH > window.getMinHeight()) {
- window.setHeight(newH);
- }
- }
-
- private void resizeRight(MouseEvent evt) {
- double newW = evt.getSceneX();
- if (newW < window.getMaxWidth() && newW > window.getMinWidth()) {
- window.setWidth(newW);
- }
- }
-
-}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java
deleted file mode 100644
index 13d1bf5c9..000000000
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnknownErrorController.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.cryptomator.ui.mainwindow;
-
-import com.tobiasdiez.easybind.EasyBind;
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.ui.common.FxController;
-
-import javax.inject.Inject;
-import javafx.beans.binding.Binding;
-import javafx.beans.property.ObjectProperty;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
-
-@MainWindowScoped
-public class VaultDetailUnknownErrorController implements FxController {
-
- private final Binding stackTrace;
-
- @Inject
- public VaultDetailUnknownErrorController(ObjectProperty vault) {
- this.stackTrace = EasyBind.select(vault) //
- .selectObject(Vault::lastKnownExceptionProperty) //
- .map(this::provideStackTrace);
- }
-
- private String provideStackTrace(Throwable cause) {
- // TODO deduplicate ErrorModule.java
- if (cause != null) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- cause.printStackTrace(new PrintStream(baos));
- return baos.toString(StandardCharsets.UTF_8);
- } else {
- return "";
- }
- }
-
- /* Getter/Setter */
-
- public Binding stackTraceProperty() {
- return stackTrace;
- }
-
- public String getStackTrace() {
- return stackTrace.getValue();
- }
-}
diff --git a/main/ui/src/main/resources/fxml/error.fxml b/main/ui/src/main/resources/fxml/error.fxml
deleted file mode 100644
index adcebdb67..000000000
--- a/main/ui/src/main/resources/fxml/error.fxml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/main/ui/src/main/resources/fxml/main_window_resize.fxml b/main/ui/src/main/resources/fxml/main_window_resize.fxml
deleted file mode 100644
index b4eb46916..000000000
--- a/main/ui/src/main/resources/fxml/main_window_resize.fxml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml b/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml
deleted file mode 100644
index 5fc60f27b..000000000
--- a/main/ui/src/main/resources/fxml/vault_detail_unknownerror.fxml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/main/ui/src/main/resources/i18n/strings_id.properties b/main/ui/src/main/resources/i18n/strings_id.properties
deleted file mode 100644
index 80a018fa8..000000000
--- a/main/ui/src/main/resources/i18n/strings_id.properties
+++ /dev/null
@@ -1,142 +0,0 @@
-# Locale Specific CSS files such as CJK, RTL,...
-
-# Generics
-## Button
-generic.button.apply=Terapkan
-generic.button.back=Kembali
-generic.button.cancel=Batalkan
-generic.button.change=Ganti
-generic.button.close=Tutup
-generic.button.copy=Salin
-generic.button.copied=Tersalin!
-generic.button.done=Selesai
-generic.button.next=Lanjut
-generic.button.print=Cetak
-## Error
-generic.error.title=Telah terjadi kesalahan tak terduga
-generic.error.instruction=Ini harusnya tidak terjadi. Harap laporkan pesan kesalahan dibawah dan tulis deskripsi kenapa ini bisa terjadi.
-
-# Defaults
-defaults.vault.vaultName=Brankas
-
-# Tray Menu
-traymenu.showMainWindow=Tampilkan
-traymenu.showPreferencesWindow=Preferensi
-traymenu.lockAllVaults=Gembok Semua
-traymenu.quitApplication=Keluar
-traymenu.vault.unlock=Buka Gembok
-traymenu.vault.lock=Gembok
-traymenu.vault.reveal=Perlihatkan
-
-# Add Vault Wizard
-addvaultwizard.title=Tambah Brankas
-## Welcome
-addvaultwizard.welcome.newButton=Buat Brankas Baru
-addvaultwizard.welcome.existingButton=Buka Brankas yg Sudah Ada
-## New
-### Name
-addvaultwizard.new.nameInstruction=Buat sebuah nama untuk brankas
-addvaultwizard.new.namePrompt=Nama Brankas
-### Location
-addvaultwizard.new.locationInstruction=Dimana Cryptomator seharusnya menyimpan brankas berkas terenkripsi kamu?
-addvaultwizard.new.locationLabel=Lokasi penyimpanan
-addvaultwizard.new.locationPrompt=…
-addvaultwizard.new.directoryPickerLabel=Sesuaikan Lokasi
-addvaultwizard.new.directoryPickerButton=Pilih…
-addvaultwizard.new.directoryPickerTitle=Pilih Folder
-addvaultwizard.new.invalidName=Nama brankas salah. Harap pilih nama folder yang umum.
-### Password
-addvaultwizard.new.createVaultBtn=Buat Brankas
-addvaultwizard.new.generateRecoveryKeyChoice=Kamu tidak dapat mengakses data tanpa kata sandi kamu. Apa kamu ingin sebuah kunci pemulihan buat jaga-jaga?
-addvaultwizard.new.generateRecoveryKeyChoice.yes=Ya, sedia payung sebelum hujan
-addvaultwizard.new.generateRecoveryKeyChoice.no=Tidak, terima kasih, Saya tidak akan kehilangan kata sandi saya
-### Information
-addvault.new.readme.storageLocation.fileName=PENTING.rtf
-addvault.new.readme.storageLocation.1=⚠️ BERKAS BRANKAS ⚠️
-addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan brankas kamu.
-addvault.new.readme.storageLocation.3=JANGAN
-addvault.new.readme.storageLocation.6=Jika kamu ingin mengenkripsi berkas dan melihat isi brankas, lakukan hal berikut:
-addvault.new.readme.storageLocation.7=1. Tambahkan brankas ini ke Cryptomator.
-addvault.new.readme.storageLocation.8=2. Buka gembok brankas di Cryptomator.
-addvault.new.readme.storageLocation.9=3. Buka akses lokasi dengan mengklik tombol "Perlihatkan".
-addvault.new.readme.storageLocation.10=Jika kamu butuh bantuan, kunjungi dokumentasi: %s
-addvault.new.readme.accessLocation.fileName=SELAMAT_DATANG.rtf
-addvault.new.readme.accessLocation.1=🔐️ ISI TERENKRIPSI 🔐️
-addvault.new.readme.accessLocation.2=Ini adalah lokasi akses brankas kamu.
-## Existing
-addvaultwizard.existing.chooseBtn=Pilih…
-## Success
-
-# Remove Vault
-
-# Change Password
-
-# Forget Password
-
-# Unlock
-unlock.unlockBtn=Buka Gembok
-## Success
-## Failure
-### Invalid Mount Point
-
-# Lock
-## Force
-## Failure
-
-# Migration
-## Start
-## Run
-## Sucess
-## Missing file system capabilities
-## Impossible
-
-# Preferences
-preferences.title=Preferensi
-## General
-## Volume
-## Updates
-## Contribution
-#<-- Add entries for donations and code/translation/documentation contribution -->
-
-## About
-
-# Vault Statistics
-## Read
-## Write
-
-# Main Window
-main.closeBtn.tooltip=Tutup
-main.preferencesBtn.tooltip=Preferensi
-## Drag 'n' Drop
-## Vault List
-main.vaultlist.contextMenu.lock=Gembok
-main.vaultlist.addVaultBtn=Tambah Brankas
-## Vault Detail
-### Welcome
-### Locked
-### Unlocked
-main.vaultDetail.lockBtn=Gembok
-### Missing
-### Needs Migration
-
-# Wrong File Alert
-
-# Vault Options
-## General
-vaultOptions.general.vaultName=Nama Brankas
-## Mount
-vaultOptions.mount.mountPoint.directoryPickerButton=Pilih…
-## Master Key
-
-# Recovery Key
-
-# New Password
-passwordStrength.messageLabel.0=Sangat lemah
-passwordStrength.messageLabel.1=Lemah
-passwordStrength.messageLabel.2=Cukup
-passwordStrength.messageLabel.3=Kuat
-passwordStrength.messageLabel.4=Sangat kuat
-
-# Quit
-quit.prompt=Keluar aplikasi? Terdapat brankas yg belum digembok.
-quit.lockAndQuit=Gembok dan Keluar
diff --git a/main/ui/src/main/resources/i18n/strings_it.properties b/main/ui/src/main/resources/i18n/strings_it.properties
deleted file mode 100644
index 1593cee8c..000000000
--- a/main/ui/src/main/resources/i18n/strings_it.properties
+++ /dev/null
@@ -1,317 +0,0 @@
-# Locale Specific CSS files such as CJK, RTL,...
-
-# Generics
-## Button
-generic.button.apply=Applica
-generic.button.back=Indietro
-generic.button.cancel=Annulla
-generic.button.change=Modifica
-generic.button.close=Chiudi
-generic.button.copy=Copia
-generic.button.copied=Copiato!
-generic.button.done=Fatto
-generic.button.next=Avanti
-generic.button.print=Stampa
-## Error
-generic.error.title=Si è verificato un errore inatteso
-generic.error.instruction=Questo non dovrebbe essere accaduto. Si prega di segnalare il testo dell'errore qui sotto e includere una descrizione delle fasi che hanno portato a questo errore.
-
-# Defaults
-defaults.vault.vaultName=Cassaforte
-
-# Tray Menu
-traymenu.showMainWindow=Visualizza
-traymenu.showPreferencesWindow=Impostazioni
-traymenu.lockAllVaults=Blocca Tutto
-traymenu.quitApplication=Esci
-traymenu.vault.unlock=Sblocca
-traymenu.vault.lock=Blocca
-traymenu.vault.reveal=Mostra
-
-# Add Vault Wizard
-addvaultwizard.title=Aggiungi Cassaforte
-## Welcome
-addvaultwizard.welcome.newButton=Crea Nuova Cassaforte
-addvaultwizard.welcome.existingButton=Apri Cassaforte Esistente
-## New
-### Name
-addvaultwizard.new.nameInstruction=Scegli un nome per la tua cassaforte
-addvaultwizard.new.namePrompt=Nome Cassaforte
-### Location
-addvaultwizard.new.locationInstruction=Dove dovrebbe memorizzare Cryptomator i file crittografati della tua cassaforte?
-addvaultwizard.new.locationLabel=Posizione archivio
-addvaultwizard.new.locationPrompt=…
-addvaultwizard.new.directoryPickerLabel=Posizione personalizzata
-addvaultwizard.new.directoryPickerButton=Scegli…
-addvaultwizard.new.directoryPickerTitle=Seleziona cartella
-addvaultwizard.new.invalidName=Nome della cassaforte non valido. Si prega di considerare un nome di directory regolare.
-### Password
-addvaultwizard.new.createVaultBtn=Crea Cassaforte
-addvaultwizard.new.generateRecoveryKeyChoice=Non sarai in grado di accedere ai tuoi dati senza password. Vuoi una chiave di recupero nel caso in cui perdi la password?
-addvaultwizard.new.generateRecoveryKeyChoice.yes=Si, per favore, è meglio essere al sicuro
-addvaultwizard.new.generateRecoveryKeyChoice.no=No grazie, non perderò la mia password
-### Information
-addvault.new.readme.storageLocation.fileName=IMPORTANTE.rtf
-addvault.new.readme.storageLocation.1=⚠ FILE CASSAFORTE ⚠
-addvault.new.readme.storageLocation.2=Questa è la posizione di archiviazione della tua cassaforte.
-addvault.new.readme.storageLocation.3=NON
-addvault.new.readme.storageLocation.4=• modificare qualsiasi file in questa cartella o
-addvault.new.readme.storageLocation.5=• Incolla tutti i file da criptare in questa directory.
-addvault.new.readme.storageLocation.6=Se si desidera crittografare i file e visualizzare il contenuto della cassaforte, effettuare le seguenti operazioni:
-addvault.new.readme.storageLocation.7=1. Aggiungi questa cassaforte a Cryptomator.
-addvault.new.readme.storageLocation.8=2. Sblocca la cassaforte in Criptomator.
-addvault.new.readme.storageLocation.9=3. Apri la posizione di accesso cliccando sul pulsante "Rivela".
-addvault.new.readme.storageLocation.10=Se hai bisogno di aiuto, leggi la documentazione: %s
-addvault.new.readme.accessLocation.fileName=BENVENUTO.rtf
-addvault.new.readme.accessLocation.1=🔐 VOLUME CRIPTATO 🔐
-addvault.new.readme.accessLocation.2=Questa è la posizione di accesso della tua cassaforte.
-addvault.new.readme.accessLocation.3=Tutti i file aggiunti a questo volume verranno crittografati da Cryptomator. Puoi lavorare su di esso come su qualsiasi altra unità/cartella. Questa è solo una vista decriptata del suo contenuto, i file rimangono continuamente criptati sul disco rigido.
-addvault.new.readme.accessLocation.4=Sei libero di rimuovere questo file.
-## Existing
-addvaultwizard.existing.instruction=Scegliere il file "masterkey.cryptomator" del deposito esistente.
-addvaultwizard.existing.chooseBtn=Scegli…
-addvaultwizard.existing.filePickerTitle=Seleziona file Masterkey
-## Success
-addvaultwizard.success.nextStepsInstructions=Aggiunta cassaforte"%s".\nÈ necessario sbloccare questa cassaforte per accedere o aggiungere contenuti. In alternativa puoi sbloccarla in qualsiasi momento successivo.
-addvaultwizard.success.unlockNow=Sblocca adesso
-
-# Remove Vault
-removeVault.title=Rimuovi Cassaforte
-removeVault.information=Questo farà dimenticare Cryptomator a questo vault. Puoi aggiungerlo di nuovo in seguito. Nessun file crittografato verrà eliminato dal tuo disco rigido.
-removeVault.confirmBtn=Rimuovi Cassaforte
-
-# Change Password
-changepassword.title=Modifica password
-changepassword.enterOldPassword=Inserisci la password attuale per "%s"
-changepassword.finalConfirmation=Ho capito che non sarò in grado di accedere ai miei dati se dimentico la mia password
-
-# Forget Password
-forgetPassword.title=Password dimenticata
-forgetPassword.information=Questo cancellerà la password salvata di questa cassaforte dal tuo portachiavi di sistema.
-forgetPassword.confirmBtn=Password dimenticata
-
-# Unlock
-unlock.title=Sblocca Cassaforte
-unlock.passwordPrompt=Inserisci la password per "%s":
-unlock.savePassword=Ricorda la Password
-unlock.unlockBtn=Sblocca
-## Success
-unlock.success.message="%s" sbloccato con successo! La tua cassaforte è ora accessibile tramite il suo drive virtuale.
-unlock.success.rememberChoice=Ricorda la scelta, non mostrare ancora
-unlock.success.revealBtn=Rivela Drive
-## Failure
-unlock.error.heading=Impossibile sbloccare la cassaforte
-### Invalid Mount Point
-unlock.error.invalidMountPoint.notExisting=Il punto di montaggio non è una directory vuota o non esiste: %s
-unlock.error.invalidMountPoint.existing=Il punto di Mount/cartella esiste già o la cartella superiore è mancante: %s
-
-# Lock
-## Force
-lock.forced.heading=Blocco normale fallito
-lock.forced.message=Il bloccaggio di "%s" è stato impedito da operazioni in sospeso o da file aperti. È possibile forzare il blocco di questa cassaforte, tuttavia interrompere I/O potrebbe causare la perdita di dati non salvati.
-lock.forced.confirmBtn=Forza Blocco
-## Failure
-lock.fail.heading=Blocco cassaforte fallito.
-lock.fail.message=Impossibile bloccare la cassaforte "%s". Assicurati che il lavoro non salvato sia salvato ovunque e che le operazioni di Lettura/Scrittura importanti siano concluse. Per chiudere la cassaforte, termina il processo di Cryptomator.
-
-# Migration
-migration.title=Aggiorna Cassaforte
-## Start
-migration.start.prompt=La tua cassaforte "%s" deve essere aggiornata a un formato più recente. Prima di procedere, assicurati che non ci sia nessuna sincronizzazione in sospeso che influisca su questa cassaforte.
-migration.start.confirm=Sì, la mia cassaforte è completamente sincronizzata
-## Run
-migration.run.enterPassword=Immettere la password per "%s"
-migration.run.startMigrationBtn=Migra Cassaforte
-migration.run.progressHint=Potrebbe richiedere un po' di tempo…
-## Sucess
-migration.success.nextStepsInstructions=Migrato "%s" con successo.\nOra puoi sbloccare la tua cassaforte.
-migration.success.unlockNow=Sblocca adesso
-## Missing file system capabilities
-migration.error.missingFileSystemCapabilities.title=File System non supportato
-migration.error.missingFileSystemCapabilities.description=La migrazione non è stata avviata perché la cassaforte è situata su un file system inadeguato.
-migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Il file system non supporta nomi di file lunghi.
-migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Il file system non supporta percorsi lunghi.
-migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Il file system non consente di essere letto.
-migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Il file system non consente di essere scritto.
-## Impossible
-migration.impossible.heading=Impossibile migrare la cassaforte
-migration.impossible.reason=La cassaforte non può essere migrata automaticamente perché la sua posizione di archiviazione di accesso non è compatibile.
-migration.impossible.moreInfo=La cassaforte può ancora essere aperta con una versione precedente. Per istruzioni su come migrare manualmente una cassaforte, visita
-
-# Preferences
-preferences.title=Impostazioni
-## General
-preferences.general=Generali
-preferences.general.theme=Aspetto
-preferences.general.theme.automatic=Automatico
-preferences.general.theme.light=Chiaro
-preferences.general.theme.dark=Scuro
-preferences.general.unlockThemes=Sblocca modalità scura
-preferences.general.showMinimizeButton=Mostra pulsante riduci a icona
-preferences.general.startHidden=Nascondi la finestra all'avvio di Cryptomator
-preferences.general.debugLogging=Abilita i registri di debug
-preferences.general.debugDirectory=Mostra file log
-preferences.general.autoStart=Avvia Cryptomator all'avvio del sistema
-preferences.general.keychainBackend=Memorizza password con
-preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Portachiavi Gnome
-preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=Portafoglio KDE
-preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=accesso portachiavi macOS
-preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Protezione Dati Di Windows
-preferences.general.interfaceOrientation=Orientamento Interfaccia
-preferences.general.interfaceOrientation.ltr=Da Sinistra a Destra
-preferences.general.interfaceOrientation.rtl=Da Destra a Sinistra
-## Volume
-preferences.volume=Drive virtuale
-preferences.volume.type=Tipo volume
-preferences.volume.webdav.port=Porta WebDAV
-preferences.volume.webdav.scheme=Schema WebDAV
-## Updates
-preferences.updates=Aggiornamenti
-preferences.updates.currentVersion=Versione attuale %s
-preferences.updates.autoUpdateCheck=Controlla automaticamente la presenza di aggiornamenti
-preferences.updates.checkNowBtn=Controlla adesso
-preferences.updates.updateAvailable=Disponibile aggiornamento alla versione %s.
-## Contribution
-#<-- Add entries for donations and code/translation/documentation contribution -->
-
-## About
-preferences.about=Informazioni
-
-# Vault Statistics
-stats.title=Statistiche per %s
-## Read
-stats.read.throughput.idle=Lettura: inattivo
-stats.read.throughput.kibs=Lettura: %.2f kiB/s
-stats.read.throughput.mibs=Lettura: %.2f MiB/s
-stats.read.total.data.none=Dati letti: -
-stats.read.total.data.kib=Dati letti: %.1f kiB
-stats.read.total.data.mib=Dati letti: %.1f MiB
-stats.read.total.data.gib=Dati letti: %.1f GiB
-stats.decr.total.data.none=Dati decriptati: -
-stats.decr.total.data.kib=Dati decrittografati: %.1f kiB
-stats.decr.total.data.mib=Dati decriptati: %.1f MiB
-stats.decr.total.data.gib=Dati decrittografati: %.1f GiB
-stats.read.accessCount=Totale lettura: %d
-## Write
-stats.write.throughput.idle=Scrivi: inattivo
-stats.write.throughput.kibs=Scrittura: %.2f kiB/s
-stats.write.throughput.mibs=Scrittura: %.2f MiB/s
-stats.write.total.data.none=Dati scritti: -
-stats.write.total.data.kib=Dati scritti: %.1f kiB
-stats.write.total.data.mib=Dati scritti: %.1f MiB
-stats.write.total.data.gib=Dati scritti: %.1f GiB
-stats.encr.total.data.none=Dati crittografati: -
-stats.encr.total.data.kib=Dati crittografati: %.1f kiB
-stats.encr.total.data.mib=Dati crittografati: %.1f MiB
-stats.encr.total.data.gib=Dati crittografati: %.1f GiB
-stats.write.accessCount=Totale scritture: %d
-
-# Main Window
-main.closeBtn.tooltip=Chiudi
-main.minimizeBtn.tooltip=Minimizza
-main.preferencesBtn.tooltip=Impostazioni
-main.debugModeEnabled.tooltip=La modalità di debug è abilitata
-main.donationKeyMissing.tooltip=Per favore considera una donazione
-## Drag 'n' Drop
-main.dropZone.dropVault=Aggiungi questa cassaforte
-main.dropZone.unknownDragboardContent=Se vuoi aggiungere una cassaforte, trascinala in questa finestra
-## Vault List
-main.vaultlist.emptyList.onboardingInstruction=Clicca qui per aggiungere una cassaforte
-main.vaultlist.contextMenu.remove=Rimuovi…
-main.vaultlist.contextMenu.lock=Blocca
-main.vaultlist.contextMenu.unlock=Sblocca…
-main.vaultlist.contextMenu.unlockNow=Sblocca Ora
-main.vaultlist.contextMenu.vaultoptions=Mostra Opzioni Cassaforte
-main.vaultlist.contextMenu.reveal=Rivela Drive
-main.vaultlist.addVaultBtn=Aggiungi Cassaforte
-## Vault Detail
-### Welcome
-main.vaultDetail.welcomeOnboarding=Grazie per aver scelto Cryptomator per proteggere i tuoi file. Se hai bisogno di assistenza, dai un'occhiata alle guide per iniziare:
-### Locked
-main.vaultDetail.lockedStatus=BLOCCATO
-main.vaultDetail.unlockBtn=Sblocca…
-main.vaultDetail.unlockNowBtn=Sblocca adesso
-main.vaultDetail.optionsBtn=Opzioni Cassaforte
-main.vaultDetail.passwordSavedInKeychain=Password salvata
-### Unlocked
-main.vaultDetail.unlockedStatus=SBLOCCATO
-main.vaultDetail.accessLocation=I contenuti della tua cassaforte sono accessibili qui:
-main.vaultDetail.revealBtn=Visualizza disco
-main.vaultDetail.lockBtn=Blocca
-main.vaultDetail.bytesPerSecondRead=Lettura:
-main.vaultDetail.bytesPerSecondWritten=Scrittura:
-main.vaultDetail.throughput.idle=inattivo
-main.vaultDetail.throughput.kbps=%.1f kiB/s
-main.vaultDetail.throughput.mbps=%.1f MiB/s
-main.vaultDetail.stats=Statistiche Cassaforte
-### Missing
-main.vaultDetail.missing.info=Cryptomator non ha potuto trovare una cassaforte in questo percorso.
-main.vaultDetail.missing.recheck=Ricontrollare
-main.vaultDetail.missing.remove=Rimuovi dalla Lista Cassaforte…
-main.vaultDetail.missing.changeLocation=Cambia posizione della Cassaforte…
-### Needs Migration
-main.vaultDetail.migrateButton=Aggiorna la cassaforte
-main.vaultDetail.migratePrompt=Prima di potervi accedere la tua cassaforte deve essere aggiornata al nuovo formato
-
-# Wrong File Alert
-wrongFileAlert.title=Come Criptare i File
-wrongFileAlert.header.title=Hai provato a cifrare questi file?
-wrongFileAlert.header.lead=A questo scopo, Cryptomator fornisce un volume nel file manager di sistema.
-wrongFileAlert.instruction.0=Per cifrare file, segui questi passi:
-wrongFileAlert.instruction.1=1. Sblocca la tua cassaforte.
-wrongFileAlert.instruction.2=2. Clicca su "Rivela" per aprire il volume nel tuo file manager.
-wrongFileAlert.instruction.3=3. Aggiungi i tuoi file a questo volume.
-wrongFileAlert.link=Per ulteriore assistenza, visita
-
-# Vault Options
-## General
-vaultOptions.general=Generali
-vaultOptions.general.vaultName=Nome Cassaforte
-vaultOptions.general.unlockAfterStartup=Sblocca vault all'avvio di Cryptomator
-vaultOptions.general.actionAfterUnlock=Dopo aver sbloccato con successo
-vaultOptions.general.actionAfterUnlock.ignore=Non fare nulla
-vaultOptions.general.actionAfterUnlock.reveal=Visualizza disco
-vaultOptions.general.actionAfterUnlock.ask=Chiedi
-## Mount
-vaultOptions.mount=Montaggio
-vaultOptions.mount.readonly=Sola lettura
-vaultOptions.mount.customMountFlags=Opzioni personalizzate
-vaultOptions.mount.winDriveLetterOccupied=occupato
-vaultOptions.mount.mountPoint=Punto di mount
-vaultOptions.mount.mountPoint.auto=Scegli automaticamente una posizione adatta
-vaultOptions.mount.mountPoint.driveLetter=Usa la lettera del drive assegnata
-vaultOptions.mount.mountPoint.custom=Percorso personalizzato
-vaultOptions.mount.mountPoint.directoryPickerButton=Scegli…
-vaultOptions.mount.mountPoint.directoryPickerTitle=Scegli una directory vuota
-## Master Key
-vaultOptions.masterkey=Password
-vaultOptions.masterkey.changePasswordBtn=Modifica password
-vaultOptions.masterkey.forgetSavedPasswordBtn=Dimentica Password Salvata
-vaultOptions.masterkey.recoveryKeyExpanation=Una chiave di recupero è il tuo unico mezzo per ripristinare l'accesso a una cassaforte se perdi la tua password.
-vaultOptions.masterkey.showRecoveryKeyBtn=Visualizza la chiave di recupero
-vaultOptions.masterkey.recoverPasswordBtn=Recupera password
-
-# Recovery Key
-recoveryKey.title=Chiave di recupero
-recoveryKey.enterPassword.prompt=Inserisci la password per visualizzare la chiave di recupero per "%s":
-recoveryKey.display.message=La seguente chiave di recupero può essere utilizzata per ripristinare l'accesso a %s":
-recoveryKey.display.StorageHints=Mantienilo da qualche parte molto sicuro, ad es.\n • Archivialo usando un gestore di password\n • Salvarlo su un'unità flash USB\n • Stamparlo su carta
-recoveryKey.recover.prompt=Inserisci la tua chiave di recupero per "%s":
-recoveryKey.recover.validKey=Questa è una chiave di recupero valida
-recoveryKey.printout.heading=Chiave di recupero Cryptomator\n"%s"\n
-
-# New Password
-newPassword.promptText=Inserisci una nuova password
-newPassword.reenterPassword=Conferma la nuova password
-newPassword.passwordsMatch=Le password corrispondono!
-newPassword.passwordsDoNotMatch=Le password non corrispondono
-passwordStrength.messageLabel.tooShort=Usa almeno %d caratteri
-passwordStrength.messageLabel.0=Molto debole
-passwordStrength.messageLabel.1=Debole
-passwordStrength.messageLabel.2=Sufficiente
-passwordStrength.messageLabel.3=Forte
-passwordStrength.messageLabel.4=Molto forte
-
-# Quit
-quit.prompt=Chiudere l'applicazione? Ci sono depositi sbloccati.
-quit.lockAndQuit=Blocca ed Esci
diff --git a/main/ui/src/main/resources/i18n/strings_ro.properties b/main/ui/src/main/resources/i18n/strings_ro.properties
deleted file mode 100644
index 8dce8a60c..000000000
--- a/main/ui/src/main/resources/i18n/strings_ro.properties
+++ /dev/null
@@ -1,77 +0,0 @@
-# Locale Specific CSS files such as CJK, RTL,...
-
-# Generics
-## Button
-## Error
-
-# Defaults
-
-# Tray Menu
-
-# Add Vault Wizard
-## Welcome
-## New
-### Name
-### Location
-### Password
-### Information
-## Existing
-## Success
-
-# Remove Vault
-
-# Change Password
-
-# Forget Password
-
-# Unlock
-## Success
-## Failure
-### Invalid Mount Point
-
-# Lock
-## Force
-## Failure
-
-# Migration
-## Start
-## Run
-## Sucess
-## Missing file system capabilities
-## Impossible
-
-# Preferences
-## General
-## Volume
-## Updates
-## Contribution
-#<-- Add entries for donations and code/translation/documentation contribution -->
-
-## About
-
-# Vault Statistics
-## Read
-## Write
-
-# Main Window
-## Drag 'n' Drop
-## Vault List
-## Vault Detail
-### Welcome
-### Locked
-### Unlocked
-### Missing
-### Needs Migration
-
-# Wrong File Alert
-
-# Vault Options
-## General
-## Mount
-## Master Key
-
-# Recovery Key
-
-# New Password
-
-# Quit
diff --git a/main/ui/src/main/resources/i18n/strings_sk.properties b/main/ui/src/main/resources/i18n/strings_sk.properties
deleted file mode 100644
index 7578acdf2..000000000
--- a/main/ui/src/main/resources/i18n/strings_sk.properties
+++ /dev/null
@@ -1,220 +0,0 @@
-# Locale Specific CSS files such as CJK, RTL,...
-
-# Generics
-## Button
-generic.button.apply=Použiť
-generic.button.back=Späť
-generic.button.cancel=Zrušiť
-generic.button.change=Zmeniť
-generic.button.close=Zavrieť
-generic.button.copy=Kopírovať
-generic.button.copied=Skopírované!
-generic.button.done=Hotovo
-generic.button.next=Ďalej
-generic.button.print=Tlač
-## Error
-generic.error.title=Vyskytla sa neočakávaná chyba
-generic.error.instruction=Toto sa nemalo stať. Nahláste text chyby uvedený nižšie a uveďte popis krokov, ktoré viedli k tejto chybe.
-
-# Defaults
-defaults.vault.vaultName=Trezor
-
-# Tray Menu
-traymenu.showMainWindow=Zobraziť
-traymenu.showPreferencesWindow=Predvoľby
-traymenu.lockAllVaults=Zamknúť všetky
-traymenu.quitApplication=Ukončiť
-traymenu.vault.unlock=Odomknúť
-traymenu.vault.lock=Uzamknúť
-traymenu.vault.reveal=Odkryť
-
-# Add Vault Wizard
-addvaultwizard.title=Pridať trezor
-## Welcome
-addvaultwizard.welcome.newButton=Vytvoriť nový trezor
-addvaultwizard.welcome.existingButton=Otvoriť existujúci trezor
-## New
-### Name
-addvaultwizard.new.nameInstruction=Zvoľte názov pre trezor
-addvaultwizard.new.namePrompt=Názov trezoru
-### Location
-addvaultwizard.new.locationInstruction=Kde by mal Cryptomator uchovávať šifrované súbory vášho trezoru?
-addvaultwizard.new.locationLabel=Umiestnenie úložiska
-addvaultwizard.new.directoryPickerLabel=Vlastné umiestnenie
-addvaultwizard.new.directoryPickerButton=Vybrať…
-addvaultwizard.new.directoryPickerTitle=Vybrať adresár
-addvaultwizard.new.invalidName=Neplatný názov trezoru. Zvážte bežný názov adresára.
-### Password
-addvaultwizard.new.createVaultBtn=Vytvoriť trezor
-addvaultwizard.new.generateRecoveryKeyChoice=Bez hesla nebudete mať prístup k svojim údajom. Chcete obnovovací kľúč pre prípad straty hesla?
-addvaultwizard.new.generateRecoveryKeyChoice.yes=Áno, prosím, istota je istota
-addvaultwizard.new.generateRecoveryKeyChoice.no=Nie, ďakujem, nestratím svoje heslo
-### Information
-addvault.new.readme.storageLocation.fileName=Dôležité.rtf
-addvault.new.readme.storageLocation.1=⚠️ SÚBORY TREZORU ⚠️
-addvault.new.readme.storageLocation.2=Toto je umiestnenie úložiska vášho trezoru.
-addvault.new.readme.storageLocation.3=NEVYKONÁVAJTE
-addvault.new.readme.storageLocation.4=zmeniť akékoľvek súbory v tomto adresári alebo
-addvault.new.readme.storageLocation.5=vložiť do tohto adresára akékoľvek súbory na šifrovanie.
-addvault.new.readme.storageLocation.6=Ak chcete šifrovať súbory a zobraziť obsah trezoru, postupujte takto:
-addvault.new.readme.storageLocation.7=1. Pridajte tento trezor do Cryptomatoru.
-addvault.new.readme.storageLocation.8=2. Odomknite trezor v Cryptomatore.
-addvault.new.readme.storageLocation.9=3. Otvorte prístupové miesto kliknutím na tlačidlo „Odkryť“.
-addvault.new.readme.storageLocation.10=Ak potrebujete pomoc, navštívte dokumentáciu: %s
-addvault.new.readme.accessLocation.fileName=VITAJTE.rtf
-addvault.new.readme.accessLocation.1=🔐️ ZAŠIFROVANÝ ZVÄZOK 🔐️
-addvault.new.readme.accessLocation.2=Toto je prístupové miesto vášho trezoru.
-addvault.new.readme.accessLocation.3=Všetky súbory pridané do tohto zväzku budú šifrované programom Cryptomator. Môžete na tom pracovať ako na akomkoľvek inom disku / priečinku. Toto je iba dešifrované zobrazenie jeho obsahu, vaše súbory zostávajú stále šifrované na pevnom disku.
-addvault.new.readme.accessLocation.4=Tento súbor môžete kedykoľvek odstrániť.
-## Existing
-addvaultwizard.existing.instruction=Vyberte súbor „masterkey.cryptomator“ existujúceho trezoru.
-addvaultwizard.existing.chooseBtn=Vybrať…
-## Success
-addvaultwizard.success.nextStepsInstructions=Pridaný trezor "%s".\nAk chcete získať prístup alebo pridať obsah, musíte tento trezor odomknúť. Prípadne ju môžete odomknúť kedykoľvek neskôr.
-addvaultwizard.success.unlockNow=Odomknúť teraz
-
-# Remove Vault
-removeVault.title=Odstrániť trezor
-removeVault.information=To spôsobí, že Cryptomator iba zabudne na tento trezor. Môžete ho pridať znova neskôr. Z pevného disku sa neodstránia žiadne šifrované súbory.
-removeVault.confirmBtn=Odstrániť trezor
-
-# Change Password
-changepassword.title=Zmeniť heslo
-changepassword.enterOldPassword=Zadaj aktuálne heslo pre "%s"
-changepassword.finalConfirmation=Chápem, že ak zabudnem svoje heslo, nebudem mať prístup k svojim údajom
-
-# Forget Password
-forgetPassword.title=Zabudnuté heslo
-forgetPassword.information=Týmto vymažete uložené heslo tohto trezoru z vášho systému kľúčov.
-forgetPassword.confirmBtn=Zabudnuté heslo
-
-# Unlock
-unlock.title=Odomknúť trezor
-unlock.passwordPrompt=Zadajte heslo pre "%s":
-unlock.savePassword=Odomknúť.uložiťHeslo
-unlock.unlockBtn=Odomknúť
-## Success
-unlock.success.message=Odomknutie "%s" úspešné! Vaša peňaženka je už prístupná cez jej virtuálny disk.
-unlock.success.revealBtn=Odkry disk
-## Failure
-unlock.error.heading=Nie je možné odomknúť účet
-### Invalid Mount Point
-unlock.error.invalidMountPoint.notExisting=Bod pripojenia "%s" nie je adresár, nie je prázdny alebo neexistuje.
-unlock.error.invalidMountPoint.existing=Bod pripojenia "%s" už existuje alebo chýba nadradený adresár.
-
-# Lock
-## Force
-lock.forced.heading=Bežné uzamknutie zlyhalo
-lock.forced.message=Zamknutie "%s" bolo zablokované prebiehajúcimi operáciami alebo otvorenými súbormi. Smiete vynútiť uzamknutie tejto peňaženky, ale prerušením I/O môže viesť k strate alebo neuloženiu dát.
-lock.forced.confirmBtn=Vynútené uzamknutie
-## Failure
-lock.fail.heading=Uzatváranie peňaženky zlyhalo.
-lock.fail.message=Peňaženku "%s" nie je možné uzamknúť. Uistite sa že neuložená páca je uložená inde a dôležité Read/Write operácie sú ukončené. Ináč uzavretím peňaženky, ukončíte proces Cryptomator-a.
-
-# Migration
-## Start
-## Run
-## Sucess
-migration.success.unlockNow=Odomknúť teraz
-## Missing file system capabilities
-## Impossible
-
-# Preferences
-preferences.title=Predvoľby
-## General
-preferences.general.theme.automatic=Automaticky
-preferences.general.showMinimizeButton=Predvoľba.hlavná.ukážMinimalizujTlačítko
-preferences.general.showTrayIcon=Predvoľba.hlavná.ukážIkonaSytémovejlišty
-preferences.general.keychainBackend.org.cryptomator.linux.keychain.SecretServiceKeychainAccess=Gnome klúčenka
-preferences.general.keychainBackend.org.cryptomator.linux.keychain.KDEWalletKeychainAccess=KDE účet
-preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeychainAccess=maccOS reťazový prístup
-preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows ochrana dát
-## Volume
-## Updates
-## Contribution
-preferences.contribute=Podporte nás
-preferences.contribute.registeredFor=Certifikát podporovateľa registrovaný na %s
-preferences.contribute.noCertificate=Podpor Cryptomator a získaj certifikát podporovateľa. Je to ako licenčný klúč ale dobrí ľudia používajú voľný software. ;-)
-preferences.contribute.getCertificate=Ešte ho nemáš? Sleduj ako ho vieš získať.
-preferences.contribute.promptText=Vlož sem kód certifikátu podporovateľa
-#<-- Add entries for donations and code/translation/documentation contribution -->
-
-## About
-
-# Vault Statistics
-stats.title=Štatistiky pre %s
-stats.cacheHitRate=Pomer nakešovaných záznamov
-## Read
-stats.read.throughput.idle=Čítanie: nečinnosť
-stats.read.throughput.kibs=Čítanie: %.2f kiB/s
-stats.read.throughput.mibs=Čítanie: %.2f MiB/s
-stats.read.total.data.none=Čítanie dát: -
-stats.read.total.data.kib=Čítanie dát: %.1f kiB
-stats.read.total.data.mib=Čítanie dát: %.1f MiB
-stats.read.total.data.gib=Čítanie dát: %.1f GiB
-stats.decr.total.data.none=Odkódované dáta: -
-stats.decr.total.data.kib=Odkódované dáta: %.1f kiB
-stats.decr.total.data.mib=Odkódované dáta: %.1f MiB
-stats.decr.total.data.gib=Odkódované dáta: %.1f GiB
-stats.read.accessCount=Spolu načítané: %d
-## Write
-stats.write.throughput.idle=Zápis: nečinnosť
-stats.write.throughput.kibs=Zápis: %.2f KiB/s
-stats.write.throughput.mibs=Zápis: %.2f MiB/s
-stats.write.total.data.none=štatistika.zápis.celkové.dáta.nič
-stats.write.total.data.kib=Zapísaných dát: %.1f kiB
-stats.write.total.data.mib=Zapísaných dát: %.1f MiB
-stats.write.total.data.gib=Zapísaných dát: %.1f GiB
-stats.encr.total.data.none=Zapísaných dát: -
-stats.encr.total.data.kib=Odkódovaných dát: %.1f kiB
-stats.encr.total.data.mib=Odkódovaných dát: %.1f MiB
-stats.encr.total.data.gib=Odkódovaných dát: %.1f GiB
-stats.write.accessCount=Suma zápisov: %d
-
-# Main Window
-main.closeBtn.tooltip=Zavrieť
-main.preferencesBtn.tooltip=Predvoľby
-main.debugModeEnabled.tooltip=Debagovací mód je aktivovaný
-## Drag 'n' Drop
-## Vault List
-main.vaultlist.contextMenu.remove=Odstráň…
-main.vaultlist.contextMenu.lock=Uzamknúť
-main.vaultlist.contextMenu.unlock=Odomknúť…
-main.vaultlist.contextMenu.unlockNow=Odomknúť teraz
-main.vaultlist.contextMenu.vaultoptions=Ukáž možnosti peňaženky
-main.vaultlist.contextMenu.reveal=Odkry disk
-main.vaultlist.addVaultBtn=Pridať trezor
-## Vault Detail
-### Welcome
-### Locked
-main.vaultDetail.unlockBtn=Odomknúť…
-main.vaultDetail.unlockNowBtn=Odomknúť teraz
-main.vaultDetail.passwordSavedInKeychain=Heslo uložené
-### Unlocked
-main.vaultDetail.revealBtn=Odkry disk
-main.vaultDetail.lockBtn=Uzamknúť
-main.vaultDetail.bytesPerSecondRead=Čítanie:
-main.vaultDetail.bytesPerSecondWritten=Zápis:
-main.vaultDetail.stats=Štatistiky účtu
-### Missing
-main.vaultDetail.missing.recheck=Prekontrolovať
-main.vaultDetail.missing.remove=Odstrániť zo zoznamu peňaženky…
-### Needs Migration
-
-# Wrong File Alert
-
-# Vault Options
-## General
-vaultOptions.general.vaultName=Názov trezoru
-vaultOptions.general.actionAfterUnlock.reveal=Odkry disk
-## Mount
-vaultOptions.mount.mountPoint.directoryPickerButton=Vybrať…
-## Master Key
-vaultOptions.masterkey.changePasswordBtn=Zmeniť heslo
-vaultOptions.masterkey.forgetSavedPasswordBtn=Zabudnúť uložené heslo
-
-# Recovery Key
-
-# New Password
-
-# Quit
diff --git a/main/ui/src/main/resources/i18n/strings_sr_Latn.properties b/main/ui/src/main/resources/i18n/strings_sr_Latn.properties
deleted file mode 100644
index 8dce8a60c..000000000
--- a/main/ui/src/main/resources/i18n/strings_sr_Latn.properties
+++ /dev/null
@@ -1,77 +0,0 @@
-# Locale Specific CSS files such as CJK, RTL,...
-
-# Generics
-## Button
-## Error
-
-# Defaults
-
-# Tray Menu
-
-# Add Vault Wizard
-## Welcome
-## New
-### Name
-### Location
-### Password
-### Information
-## Existing
-## Success
-
-# Remove Vault
-
-# Change Password
-
-# Forget Password
-
-# Unlock
-## Success
-## Failure
-### Invalid Mount Point
-
-# Lock
-## Force
-## Failure
-
-# Migration
-## Start
-## Run
-## Sucess
-## Missing file system capabilities
-## Impossible
-
-# Preferences
-## General
-## Volume
-## Updates
-## Contribution
-#<-- Add entries for donations and code/translation/documentation contribution -->
-
-## About
-
-# Vault Statistics
-## Read
-## Write
-
-# Main Window
-## Drag 'n' Drop
-## Vault List
-## Vault Detail
-### Welcome
-### Locked
-### Unlocked
-### Missing
-### Needs Migration
-
-# Wrong File Alert
-
-# Vault Options
-## General
-## Mount
-## Master Key
-
-# Recovery Key
-
-# New Password
-
-# Quit
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..8f5080ab4
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,483 @@
+
+
+ 4.0.0
+ org.cryptomator
+ cryptomator
+ 1.6.0-SNAPSHOT
+ Cryptomator Desktop App
+
+
+ cryptomator.org
+ https://cryptomator.org
+
+
+
+
+ Sebastian Stenzel
+ sebastian.stenzel@gmail.com
+ +1
+
+
+
+
+ UTF-8
+ 17
+
+
+ com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh
+
+
+ 2.1.0
+ 1.0.0
+ 1.0.0
+ 1.0.0
+ 1.0.0
+ 1.3.3
+ 1.3.3
+ 1.2.6
+
+
+ 17.0.0.1
+ 3.12.0
+ 3.18.2
+ 2.2
+ 31.0-jre
+ 2.39
+ 2.8.8
+ 1.5.2
+ 1.7.32
+ 1.2.6
+
+
+ 5.8.1
+ 3.12.4
+ 2.2
+
+
+
+
+
+ org.cryptomator
+ cryptofs
+ ${cryptomator.cryptofs.version}
+
+
+ org.cryptomator
+ fuse-nio-adapter
+ ${cryptomator.fuse.version}
+
+
+ org.cryptomator
+ dokany-nio-adapter
+ ${cryptomator.dokany.version}
+
+
+ org.cryptomator
+ webdav-nio-adapter
+ ${cryptomator.webdav.version}
+
+
+ org.cryptomator
+ integrations-api
+ ${cryptomator.integrations.version}
+
+
+
+
+ org.openjfx
+ javafx-base
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-graphics
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ ch.qos.logback
+ logback-core
+ ${logback.version}
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+
+ com.auth0
+ java-jwt
+ ${jwt.version}
+
+
+
+
+ com.tobiasdiez
+ easybind
+ ${easybind.version}
+
+
+
+
+ com.nulab-inc
+ zxcvbn
+ ${zxcvbn.version}
+
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+ com.google.guava
+ listenablefuture
+
+
+ com.google.code.findbugs
+ jsr305
+
+
+ org.checkerframework
+ checker-qual
+
+
+ com.google.errorprone
+ error_prone_annotations
+
+
+ com.google.j2objc
+ j2objc-annotations
+
+
+
+
+ com.google.dagger
+ dagger
+ ${dagger.version}
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.hamcrest
+ hamcrest
+ ${hamcrest.version}
+ test
+
+
+ org.openjfx
+ javafx-swing
+ ${javafx.version}
+ test
+
+
+ com.google.jimfs
+ jimfs
+ 1.2
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.2.0
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.2.0
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+ 2.0.0
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.7
+
+
+ org.owasp
+ dependency-check-maven
+ 6.3.1
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${project.jdk.version}
+
+
+ com.google.dagger
+ dagger-compiler
+ ${dagger.version}
+
+
+
+ -Adagger.fastInit=enabled
+ -Adagger.formatGeneratedSource=enabled
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-mods
+ prepare-package
+
+ copy-dependencies
+
+
+ runtime
+ ${project.build.directory}/mods
+ ${nonModularGroupIds}
+
+
+
+ copy-libs
+ prepare-package
+
+ copy-dependencies
+
+
+ runtime
+ ${project.build.directory}/libs
+ ${nonModularGroupIds}
+
+
+
+
+
+ org.codehaus.mojo
+ license-maven-plugin
+
+
+ add-third-party
+
+ add-third-party
+
+ generate-resources
+
+ ${project.basedir}/src/main/resources/license
+ THIRD-PARTY.txt
+ compile
+ org\.cryptomator
+
+ Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0
+ MIT License|The MIT License (MIT)|The MIT License|MIT license
+ LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
+ GPLv2|GNU General Public License Version 2
+ GPLv2+CE|CDDL + GPLv2 with classpath exception
+
+ ${project.basedir}/src/license/template.ftl
+
+
+
+
+
+
+
+
+
+ coverage
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ report
+
+ report
+
+
+
+
+
+ **/*_*
+ **/Dagger*
+
+
+
+
+
+
+
+
+ dependency-check
+
+
+
+ org.owasp
+ dependency-check-maven
+
+ 24
+ 0
+ true
+ true
+ suppression.xml
+
+
+
+
+ check
+
+
+
+
+
+
+
+
+
+ mac
+
+
+ mac
+
+
+ idea.version
+
+
+
+
+ org.cryptomator
+ integrations-mac
+ ${cryptomator.integrations.mac.version}
+
+
+
+
+
+ linux
+
+
+ unix
+ Linux
+
+
+ idea.version
+
+
+
+
+ org.cryptomator
+ integrations-linux
+ ${cryptomator.integrations.linux.version}
+
+
+
+
+
+ win
+
+
+ windows
+
+
+ idea.version
+
+
+
+
+ org.cryptomator
+ integrations-win
+ ${cryptomator.integrations.win.version}
+
+
+
+
+
+
diff --git a/main/ui/src/license/THIRD-PARTY.properties b/src/license/THIRD-PARTY.properties
similarity index 100%
rename from main/ui/src/license/THIRD-PARTY.properties
rename to src/license/THIRD-PARTY.properties
diff --git a/main/ui/src/license/template.ftl b/src/license/template.ftl
similarity index 100%
rename from main/ui/src/license/template.ftl
rename to src/license/template.ftl
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 000000000..f13308be5
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,60 @@
+import org.cryptomator.integrations.autostart.AutoStartProvider;
+import org.cryptomator.integrations.keychain.KeychainAccessProvider;
+import org.cryptomator.integrations.tray.TrayIntegrationProvider;
+import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
+
+module org.cryptomator.desktop {
+ requires org.cryptomator.cryptofs;
+ requires org.cryptomator.frontend.dokany;
+ requires org.cryptomator.frontend.fuse;
+ requires org.cryptomator.frontend.webdav;
+ requires org.cryptomator.integrations.api;
+ requires java.desktop;
+ requires java.net.http;
+ requires javafx.base;
+ requires javafx.graphics;
+ requires javafx.controls;
+ requires javafx.fxml;
+ requires com.tobiasdiez.easybind;
+ requires com.google.common;
+ requires com.google.gson;
+ requires com.nulabinc.zxcvbn;
+ requires org.slf4j;
+ requires org.apache.commons.lang3;
+ requires dagger;
+ requires com.auth0.jwt;
+
+ /* TODO: filename-based modules: */
+ requires static javax.inject; /* ugly dagger/guava crap */
+ requires logback.classic;
+ requires logback.core;
+
+ uses AutoStartProvider;
+ uses KeychainAccessProvider;
+ uses TrayIntegrationProvider;
+ uses UiAppearanceProvider;
+
+ opens org.cryptomator.common.settings to com.google.gson;
+
+ opens org.cryptomator.common to javafx.fxml;
+ opens org.cryptomator.common.vaults to javafx.fxml;
+ opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
+ opens org.cryptomator.ui.changepassword to javafx.fxml;
+ opens org.cryptomator.ui.common to javafx.fxml;
+ opens org.cryptomator.ui.controls to javafx.fxml;
+ opens org.cryptomator.ui.forgetPassword to javafx.fxml;
+ opens org.cryptomator.ui.fxapp to javafx.fxml;
+ opens org.cryptomator.ui.health to javafx.fxml;
+ opens org.cryptomator.ui.keyloading.masterkeyfile to javafx.fxml;
+ opens org.cryptomator.ui.lock to javafx.fxml;
+ opens org.cryptomator.ui.mainwindow to javafx.fxml;
+ opens org.cryptomator.ui.migration to javafx.fxml;
+ opens org.cryptomator.ui.preferences to javafx.fxml;
+ opens org.cryptomator.ui.quit to javafx.fxml;
+ opens org.cryptomator.ui.recoverykey to javafx.fxml;
+ opens org.cryptomator.ui.removevault to javafx.fxml;
+ opens org.cryptomator.ui.stats to javafx.fxml;
+ opens org.cryptomator.ui.unlock to javafx.fxml;
+ opens org.cryptomator.ui.vaultoptions to javafx.fxml;
+ opens org.cryptomator.ui.wrongfilealert to javafx.fxml;
+}
\ No newline at end of file
diff --git a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java b/src/main/java/org/cryptomator/common/CommonsModule.java
similarity index 86%
rename from main/commons/src/main/java/org/cryptomator/common/CommonsModule.java
rename to src/main/java/org/cryptomator/common/CommonsModule.java
index 2a0a42ecb..eb7e6cd13 100644
--- a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java
+++ b/src/main/java/org/cryptomator/common/CommonsModule.java
@@ -15,6 +15,8 @@ import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
+import org.cryptomator.common.vaults.VaultListModule;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,6 +27,8 @@ import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import java.net.InetSocketAddress;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -34,7 +38,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-@Module(subcomponents = {VaultComponent.class}, includes = {KeychainModule.class})
+@Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class})
public abstract class CommonsModule {
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
@@ -42,6 +46,7 @@ public abstract class CommonsModule {
private static final int NUM_CORE_BG_THREADS = 6;
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
+ @SuppressWarnings("SpellCheckingInspection")
@Provides
@Singleton
@Named("licensePublicKey")
@@ -55,6 +60,22 @@ public abstract class CommonsModule {
""";
}
+ @Provides
+ @Singleton
+ static SecureRandom provideCSPRNG() {
+ try {
+ return SecureRandom.getInstanceStrong();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e);
+ }
+ }
+
+ @Provides
+ @Singleton
+ static MasterkeyFileAccess provideMasterkeyFileAccess(SecureRandom csprng) {
+ return new MasterkeyFileAccess(Constants.PEPPER, csprng);
+ }
+
@Provides
@Singleton
@Named("SemVer")
@@ -68,12 +89,6 @@ public abstract class CommonsModule {
return settingsProvider.get();
}
- @Provides
- @Singleton
- static ObservableList provideVaultList(VaultListManager vaultListManager) {
- return vaultListManager.getVaultList();
- }
-
@Provides
@Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
diff --git a/src/main/java/org/cryptomator/common/Constants.java b/src/main/java/org/cryptomator/common/Constants.java
new file mode 100644
index 000000000..90bd3c8ec
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/Constants.java
@@ -0,0 +1,11 @@
+package org.cryptomator.common;
+
+public interface Constants {
+
+ String MASTERKEY_FILENAME = "masterkey.cryptomator";
+ String MASTERKEY_BACKUP_SUFFIX = ".bkup";
+ String VAULTCONFIG_FILENAME = "vault.cryptomator";
+ String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
+ byte[] PEPPER = new byte[0];
+
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/ConsumerThrowingException.java b/src/main/java/org/cryptomator/common/ConsumerThrowingException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/ConsumerThrowingException.java
rename to src/main/java/org/cryptomator/common/ConsumerThrowingException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/Environment.java b/src/main/java/org/cryptomator/common/Environment.java
similarity index 82%
rename from main/commons/src/main/java/org/cryptomator/common/Environment.java
rename to src/main/java/org/cryptomator/common/Environment.java
index 0fa12ee20..b42b1a356 100644
--- a/main/commons/src/main/java/org/cryptomator/common/Environment.java
+++ b/src/main/java/org/cryptomator/common/Environment.java
@@ -33,11 +33,13 @@ public class Environment {
LOG.debug("user.region: {}", System.getProperty("user.region"));
LOG.debug("logback.configurationFile: {}", System.getProperty("logback.configurationFile"));
LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath"));
- LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath"));
+ LOG.debug("cryptomator.ipcSocketPath: {}", System.getProperty("cryptomator.ipcSocketPath"));
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
+ LOG.debug("cryptomator.pluginDir: {}", System.getProperty("cryptomator.pluginDir"));
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
+ LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
@@ -51,8 +53,8 @@ public class Environment {
return getPaths("cryptomator.settingsPath");
}
- public Stream getIpcPortPath() {
- return getPaths("cryptomator.ipcPortPath");
+ public Stream ipcSocketPath() {
+ return getPaths("cryptomator.ipcSocketPath");
}
public Stream getKeychainPath() {
@@ -63,10 +65,18 @@ public class Environment {
return getPath("cryptomator.logDir").map(this::replaceHomeDir);
}
+ public Optional getPluginDir() {
+ return getPath("cryptomator.pluginDir").map(this::replaceHomeDir);
+ }
+
public Optional getMountPointsDir() {
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
}
+ public Optional getAppVersion() {
+ return Optional.ofNullable(System.getProperty("cryptomator.appVersion"));
+ }
+
public Optional getBuildNumber() {
return Optional.ofNullable(System.getProperty("cryptomator.buildNumber"));
}
@@ -99,12 +109,12 @@ public class Environment {
}
// visible for testing
- Path getHomeDir() {
+ public Path getHomeDir() {
return getPath("user.home").orElseThrow();
}
// visible for testing
- Stream getPaths(String propertyName) {
+ public Stream getPaths(String propertyName) {
Stream rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
}
@@ -123,8 +133,8 @@ public class Environment {
return Stream.empty();
} else {
Iterable iter = Splitter.on(separator).split(value);
- Spliterator spliter = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
- return StreamSupport.stream(spliter, false);
+ Spliterator spliterator = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
+ return StreamSupport.stream(spliterator, false);
}
}
}
diff --git a/src/main/java/org/cryptomator/common/ErrorCode.java b/src/main/java/org/cryptomator/common/ErrorCode.java
new file mode 100644
index 000000000..51fb355b6
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/ErrorCode.java
@@ -0,0 +1,121 @@
+package org.cryptomator.common;
+
+import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
+
+import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Holds a throwable and provides a human-readable {@link #toString() three-component string representation}
+ * aiming to allow documentation and lookup of same or similar errors.
+ */
+public class ErrorCode {
+
+ private final static int A_PRIME = 31;
+ private final static int SEED = 0xdeadbeef;
+ public final static String DELIM = ":";
+
+ private final static int LATEST_FRAME = 1;
+ private final static int ALL_FRAMES = Integer.MAX_VALUE;
+
+ private final Throwable throwable;
+ private final Throwable rootCause;
+ private final int rootCauseSpecificFrames;
+
+ private ErrorCode(Throwable throwable, Throwable rootCause, int rootCauseSpecificFrames) {
+ this.throwable = Objects.requireNonNull(throwable);
+ this.rootCause = Objects.requireNonNull(rootCause);
+ this.rootCauseSpecificFrames = rootCauseSpecificFrames;
+ }
+
+ // visible for testing
+ String methodCode() {
+ return format(traceCode(rootCause, LATEST_FRAME));
+ }
+
+ // visible for testing
+ String rootCauseCode() {
+ return format(traceCode(rootCause, rootCauseSpecificFrames));
+ }
+
+ // visible for testing
+ String throwableCode() {
+ return format(traceCode(throwable, ALL_FRAMES));
+ }
+
+ /**
+ * Produces an error code consisting of three {@value DELIM}-separated components.
+ *
+ * A full match of the error code indicates the exact same throwable (to the extent possible
+ * without hash collisions). A partial match of the first or second component indicates related problems
+ * with the same root cause.
+ *
+ * @return A three-part error code
+ */
+ @Override
+ public String toString() {
+ return methodCode() + DELIM + rootCauseCode() + DELIM + throwableCode();
+ }
+
+ /**
+ * Deterministically creates an error code from the stack trace of the given cause.
+ *
+ * The code consists of three parts separated by {@value DELIM}:
+ *
+ *
The first part depends on the root cause and the method that threw it
+ *
The second part depends on the root cause and its stack trace
+ *
The third part depends on all the cause hierarchy
+ *
+ *
+ * Parts may be identical if the cause is the root cause or the root cause has just one single item in its stack trace.
+ *
+ * @param throwable The exception
+ * @return A three-part error code
+ */
+ public static ErrorCode of(Throwable throwable) {
+ var causalChain = Throwables.getCausalChain(throwable);
+ if (causalChain.size() > 1) {
+ var rootCause = causalChain.get(causalChain.size() - 1);
+ var parentOfRootCause = causalChain.get(causalChain.size() - 2);
+ var rootSpecificFrames = nonOverlappingFrames(parentOfRootCause.getStackTrace(), rootCause.getStackTrace());
+ return new ErrorCode(throwable, rootCause, rootSpecificFrames);
+ } else {
+ return new ErrorCode(throwable, throwable, ALL_FRAMES);
+ }
+ }
+
+ private String format(int value) {
+ // Cut off highest 12 bits (only leave 20 least significant bits) and XOR rest with cutoff
+ value = (value & 0xfffff) ^ (value >>> 20);
+ return Strings.padStart(Integer.toString(value, 32).toUpperCase(Locale.ROOT), 4, '0');
+ }
+
+ private int traceCode(Throwable e, int frameCount) {
+ int result = SEED;
+ if (e.getCause() != null) {
+ result = traceCode(e.getCause(), frameCount);
+ }
+ result = result * A_PRIME + e.getClass().getName().hashCode();
+ var stack = e.getStackTrace();
+ for (int i = 0; i < Math.min(stack.length, frameCount); i++) {
+ result = result * A_PRIME + stack[i].getClassName().hashCode();
+ result = result * A_PRIME + stack[i].getMethodName().hashCode();
+ }
+ return result;
+ }
+
+ private static int nonOverlappingFrames(StackTraceElement[] frames, StackTraceElement[] enclosingFrames) {
+ // Compute the number of elements in `frames` not contained in `enclosingFrames` by iterating backwards
+ // Result should usually be equal to the difference in size of both traces
+ var i = reverseStream(enclosingFrames).iterator();
+ return (int) reverseStream(frames).dropWhile(f -> i.hasNext() && i.next().equals(f)).count();
+ }
+
+ private static Stream reverseStream(T[] array) {
+ return IntStream.rangeClosed(1, array.length).mapToObj(i -> array[array.length - i]);
+ }
+
+}
\ No newline at end of file
diff --git a/main/commons/src/main/java/org/cryptomator/common/LicenseChecker.java b/src/main/java/org/cryptomator/common/LicenseChecker.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/LicenseChecker.java
rename to src/main/java/org/cryptomator/common/LicenseChecker.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/LicenseHolder.java b/src/main/java/org/cryptomator/common/LicenseHolder.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/LicenseHolder.java
rename to src/main/java/org/cryptomator/common/LicenseHolder.java
diff --git a/src/main/java/org/cryptomator/common/Nullable.java b/src/main/java/org/cryptomator/common/Nullable.java
new file mode 100644
index 000000000..cb0d9e887
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/Nullable.java
@@ -0,0 +1,8 @@
+package org.cryptomator.common;
+
+/**
+ * Replacement for JSR-305 to avoid runtime dependencies. Used in Dagger components.
+ */
+public @interface Nullable {
+
+}
diff --git a/src/main/java/org/cryptomator/common/PluginClassLoader.java b/src/main/java/org/cryptomator/common/PluginClassLoader.java
new file mode 100644
index 000000000..16932923b
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/PluginClassLoader.java
@@ -0,0 +1,66 @@
+package org.cryptomator.common;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+@Singleton
+public class PluginClassLoader extends URLClassLoader {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
+ private static final String NAME = "PluginClassLoader";
+ private static final String JAR_SUFFIX = ".jar";
+
+ @Inject
+ public PluginClassLoader(Environment env) {
+ super(NAME, env.getPluginDir().map(PluginClassLoader::findJars).orElse(new URL[0]), PluginClassLoader.class.getClassLoader());
+ }
+
+ private static URL[] findJars(Path path) {
+ if (!Files.isDirectory(path)) {
+ return new URL[0];
+ } else {
+ try {
+ var visitor = new JarVisitor();
+ Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
+ return visitor.urls.toArray(URL[]::new);
+ } catch (IOException e) {
+ LOG.warn("Failed to scan plugin dir " + path, e);
+ return new URL[0];
+ }
+ }
+ }
+
+ private static final class JarVisitor extends SimpleFileVisitor {
+
+ private final List urls = new ArrayList<>();
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ if (attrs.isRegularFile() && file.getFileName().toString().toLowerCase().endsWith(JAR_SUFFIX)) {
+ try {
+ urls.add(file.toUri().toURL());
+ } catch (MalformedURLException e) {
+ LOG.warn("Failed to create URL for jar file {}", file);
+ }
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/RunnableThrowingException.java b/src/main/java/org/cryptomator/common/RunnableThrowingException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/RunnableThrowingException.java
rename to src/main/java/org/cryptomator/common/RunnableThrowingException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/SemVerComparator.java b/src/main/java/org/cryptomator/common/SemVerComparator.java
similarity index 93%
rename from main/commons/src/main/java/org/cryptomator/common/SemVerComparator.java
rename to src/main/java/org/cryptomator/common/SemVerComparator.java
index e0ff9ae25..0f9148bd5 100644
--- a/main/commons/src/main/java/org/cryptomator/common/SemVerComparator.java
+++ b/src/main/java/org/cryptomator/common/SemVerComparator.java
@@ -60,17 +60,17 @@ public class SemVerComparator implements Comparator {
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
- int subversionComparisionResult = 0;
+ int subversionComparisonResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
- subversionComparisionResult = v1 - v2;
+ subversionComparisonResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
- subversionComparisionResult = vComps1[i].compareTo(vComps2[i]);
+ subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
}
- if (subversionComparisionResult != 0) {
- return subversionComparisionResult;
+ if (subversionComparisonResult != 0) {
+ return subversionComparisonResult;
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/ShutdownHook.java b/src/main/java/org/cryptomator/common/ShutdownHook.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/ShutdownHook.java
rename to src/main/java/org/cryptomator/common/ShutdownHook.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/SupplierThrowingException.java b/src/main/java/org/cryptomator/common/SupplierThrowingException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/SupplierThrowingException.java
rename to src/main/java/org/cryptomator/common/SupplierThrowingException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
similarity index 95%
rename from main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
rename to src/main/java/org/cryptomator/common/keychain/KeychainManager.java
index 64db08018..c97d0e9c9 100644
--- a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
+++ b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java
@@ -38,6 +38,11 @@ public class KeychainManager implements KeychainAccessProvider {
return result;
}
+ @Override
+ public String displayName() {
+ return getClass().getName();
+ }
+
@Override
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, passphrase);
@@ -59,8 +64,10 @@ public class KeychainManager implements KeychainAccessProvider {
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
- getKeychainOrFail().changePassphrase(key, passphrase);
- setPassphraseStored(key, true);
+ if (isPassphraseStored(key)) {
+ getKeychainOrFail().changePassphrase(key, passphrase);
+ setPassphraseStored(key, true);
+ }
}
@Override
diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java b/src/main/java/org/cryptomator/common/keychain/KeychainModule.java
similarity index 79%
rename from main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java
rename to src/main/java/org/cryptomator/common/keychain/KeychainModule.java
index 9ac343d36..6356c4966 100644
--- a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java
+++ b/src/main/java/org/cryptomator/common/keychain/KeychainModule.java
@@ -2,6 +2,7 @@ package org.cryptomator.common.keychain;
import dagger.Module;
import dagger.Provides;
+import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
@@ -17,8 +18,8 @@ public class KeychainModule {
@Provides
@Singleton
- static Set> provideAvailableKeychainAccessProviderFactories() {
- return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet());
+ static Set> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
+ return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
}
@Provides
@@ -34,11 +35,11 @@ public class KeychainModule {
@Singleton
static ObjectExpression provideKeychainAccessProvider(Settings settings, Set providers) {
return Bindings.createObjectBinding(() -> {
- var selectedProviderClass = settings.keychainBackend().get().getProviderClass();
+ var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findAny().orElse(null);
return selectedProvider.orElse(fallbackProvider);
- }, settings.keychainBackend());
+ }, settings.keychainProvider());
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java b/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java
rename to src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java b/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java
similarity index 90%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java
index d0fc04f3c..23ce8466a 100644
--- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java
+++ b/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java
@@ -24,6 +24,6 @@ class AvailableDriveLetterChooser implements MountPointChooser {
@Override
public Optional chooseMountPoint(Volume caller) {
- return this.windowsDriveLetters.getAvailableDriveLetterPath();
+ return this.windowsDriveLetters.getDesiredAvailableDriveLetterPath();
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java b/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java b/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/InvalidMountPointException.java b/src/main/java/org/cryptomator/common/mountpoint/InvalidMountPointException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/InvalidMountPointException.java
rename to src/main/java/org/cryptomator/common/mountpoint/InvalidMountPointException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java b/src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java b/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java
similarity index 96%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java
index 3a4d82cf3..1e36ba5f6 100644
--- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java
+++ b/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java
@@ -1,6 +1,6 @@
package org.cryptomator.common.mountpoint;
-import com.google.common.base.Preconditions;
+import dagger.multibindings.IntKey;
import org.cryptomator.common.vaults.Volume;
import java.nio.file.Path;
@@ -12,13 +12,13 @@ import java.util.SortedSet;
* preparation of a mountpoint or an exception otherwise.
*
All MountPointChoosers (MPCs) need to implement this class and must be added to
* the pool of possible MPCs by the {@link MountPointChooserModule MountPointChooserModule.}
- * The MountPointChooserModule will sort them according to their {@link #getPriority() priority.}
+ * The MountPointChooserModule will sort them according to their {@link IntKey IntKey priority.}
* The priority must be defined by the developer to reflect a useful execution order.
* A specific priority must not be assigned to more than one MPC at a time;
* the result of having two MPCs with equal priority is undefined.
*
- *
MPCs are executed by a {@link Volume} in ascending order of their priority
- * (smaller priorities are tried first) to find and prepare a suitable mountpoint for the volume.
+ *
MPCs are executed by a {@link Volume} in descending order of their priority
+ * (higher priorities are tried first) to find and prepare a suitable mountpoint for the volume.
* The volume has access to a {@link SortedSet} of MPCs in this specific order,
* that is provided by the Module. The Set contains all available Choosers, even if they
* are not {@link #isApplicable(Volume) applicable} for the Vault/Volume. The Volume must
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java b/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java
similarity index 91%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java
rename to src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java
index cfb7b68ec..9c7893e42 100644
--- a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java
+++ b/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java
@@ -9,6 +9,7 @@ import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.PerVault;
import javax.inject.Named;
+import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -24,16 +25,22 @@ public abstract class MountPointChooserModule {
@Binds
@IntoMap
- @IntKey(0)
+ @IntKey(1000)
@PerVault
public abstract MountPointChooser bindCustomMountPointChooser(CustomMountPointChooser chooser);
@Binds
@IntoMap
- @IntKey(100)
+ @IntKey(900)
@PerVault
public abstract MountPointChooser bindCustomDriveLetterChooser(CustomDriveLetterChooser chooser);
+ @Binds
+ @IntoMap
+ @IntKey(800)
+ @PerVault
+ public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
+
@Binds
@IntoMap
@IntKey(101)
@@ -42,13 +49,7 @@ public abstract class MountPointChooserModule {
@Binds
@IntoMap
- @IntKey(200)
- @PerVault
- public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
-
- @Binds
- @IntoMap
- @IntKey(999)
+ @IntKey(100)
@PerVault
public abstract MountPointChooser bindTemporaryMountPointChooser(TemporaryMountPointChooser chooser);
@@ -56,7 +57,8 @@ public abstract class MountPointChooserModule {
@PerVault
@Named("orderedMountPointChoosers")
public static Iterable provideOrderedMountPointChoosers(Map choosers) {
- SortedMap sortedChoosers = new TreeMap<>(choosers);
+ SortedMap sortedChoosers = new TreeMap<>(Comparator.reverseOrder());
+ sortedChoosers.putAll(choosers);
return Iterables.unmodifiableIterable(sortedChoosers.values());
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointHelper.java b/src/main/java/org/cryptomator/common/mountpoint/MountPointHelper.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointHelper.java
rename to src/main/java/org/cryptomator/common/mountpoint/MountPointHelper.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java b/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java
rename to src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java
similarity index 75%
rename from main/commons/src/main/java/org/cryptomator/common/settings/Settings.java
rename to src/main/java/org/cryptomator/common/settings/Settings.java
index a419d206a..82f2fb794 100644
--- a/main/commons/src/main/java/org/cryptomator/common/settings/Settings.java
+++ b/src/main/java/org/cryptomator/common/settings/Settings.java
@@ -30,7 +30,7 @@ public class Settings {
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
- public static final boolean DEFAULT_CHECK_FOR_UDPATES = false;
+ public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
public static final boolean DEFAULT_START_HIDDEN = false;
public static final int DEFAULT_PORT = 42427;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
@@ -38,14 +38,17 @@ public class Settings {
public static final boolean DEFAULT_DEBUG_MODE = false;
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
- public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = SystemUtils.IS_OS_WINDOWS ? KeychainBackend.WIN_SYSTEM_KEYCHAIN : SystemUtils.IS_OS_MAC ? KeychainBackend.MAC_SYSTEM_KEYCHAIN : KeychainBackend.GNOME;
+ @Deprecated // to be changed to "whatever is available" eventually
+ public static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
+ public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
+
private final ObservableList directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
- private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
+ private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
@@ -53,11 +56,17 @@ public class Settings {
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
private final ObjectProperty preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
private final ObjectProperty theme = new SimpleObjectProperty<>(DEFAULT_THEME);
- private final ObjectProperty keychainBackend = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_BACKEND);
+ private final ObjectProperty keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
private final ObjectProperty userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
private final BooleanProperty showTrayIcon;
+ private final IntegerProperty windowXPosition = new SimpleIntegerProperty();
+ private final IntegerProperty windowYPosition = new SimpleIntegerProperty();
+ private final IntegerProperty windowWidth = new SimpleIntegerProperty();
+ private final IntegerProperty windowHeight = new SimpleIntegerProperty();
+ private final ObjectProperty displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
+
private Consumer saveCmd;
@@ -77,11 +86,16 @@ public class Settings {
debugMode.addListener(this::somethingChanged);
preferredVolumeImpl.addListener(this::somethingChanged);
theme.addListener(this::somethingChanged);
- keychainBackend.addListener(this::somethingChanged);
+ keychainProvider.addListener(this::somethingChanged);
userInterfaceOrientation.addListener(this::somethingChanged);
licenseKey.addListener(this::somethingChanged);
showMinimizeButton.addListener(this::somethingChanged);
showTrayIcon.addListener(this::somethingChanged);
+ windowXPosition.addListener(this::somethingChanged);
+ windowYPosition.addListener(this::somethingChanged);
+ windowWidth.addListener(this::somethingChanged);
+ windowHeight.addListener(this::somethingChanged);
+ displayConfiguration.addListener(this::somethingChanged);
}
void setSaveCmd(Consumer saveCmd) {
@@ -140,7 +154,7 @@ public class Settings {
return theme;
}
- public ObjectProperty keychainBackend() { return keychainBackend; }
+ public ObjectProperty keychainProvider() {return keychainProvider;}
public ObjectProperty userInterfaceOrientation() {
return userInterfaceOrientation;
@@ -157,4 +171,24 @@ public class Settings {
public BooleanProperty showTrayIcon() {
return showTrayIcon;
}
+
+ public IntegerProperty windowXPositionProperty() {
+ return windowXPosition;
+ }
+
+ public IntegerProperty windowYPositionProperty() {
+ return windowYPosition;
+ }
+
+ public IntegerProperty windowWidthProperty() {
+ return windowWidth;
+ }
+
+ public IntegerProperty windowHeightProperty() {
+ return windowHeight;
+ }
+
+ public ObjectProperty displayConfigurationProperty() {
+ return displayConfiguration;
+ }
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
similarity index 85%
rename from main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
rename to src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
index d22e0867f..6d8d880e6 100644
--- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsJsonAdapter.java
@@ -48,10 +48,16 @@ public class SettingsJsonAdapter extends TypeAdapter {
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
out.name("theme").value(value.theme().get().name());
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
- out.name("keychainBackend").value(value.keychainBackend().get().name());
+ out.name("keychainProvider").value(value.keychainProvider().get());
out.name("licenseKey").value(value.licenseKey().get());
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
out.name("showTrayIcon").value(value.showTrayIcon().get());
+ out.name("windowXPosition").value((value.windowXPositionProperty().get()));
+ out.name("windowYPosition").value((value.windowYPositionProperty().get()));
+ out.name("windowWidth").value((value.windowWidthProperty().get()));
+ out.name("windowHeight").value((value.windowHeightProperty().get()));
+ out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
+
out.endObject();
}
@@ -82,10 +88,16 @@ public class SettingsJsonAdapter extends TypeAdapter {
case "preferredVolumeImpl" -> settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
- case "keychainBackend" -> settings.keychainBackend().set(parseKeychainBackend(in.nextString()));
+ case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
case "licenseKey" -> settings.licenseKey().set(in.nextString());
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
+ case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
+ case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
+ case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
+ case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
+ case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
+
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
@@ -124,15 +136,6 @@ public class SettingsJsonAdapter extends TypeAdapter {
}
}
- private KeychainBackend parseKeychainBackend(String backendName) {
- try {
- return KeychainBackend.valueOf(backendName.toUpperCase());
- } catch (IllegalArgumentException e) {
- LOG.warn("Invalid keychain backend {}. Defaulting to {}.", backendName, Settings.DEFAULT_KEYCHAIN_BACKEND);
- return Settings.DEFAULT_KEYCHAIN_BACKEND;
- }
- }
-
private NodeOrientation parseUiOrientation(String uiOrientationName) {
try {
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
similarity index 98%
rename from main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
rename to src/main/java/org/cryptomator/common/settings/SettingsProvider.java
index 8310d0fa9..3be42f7ac 100644
--- a/main/commons/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
+++ b/src/main/java/org/cryptomator/common/settings/SettingsProvider.java
@@ -101,7 +101,7 @@ public class SettingsProvider implements Supplier {
if (settings == null) {
return;
}
- final Optional settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
+ final Optional settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
settingsPath.ifPresent(path -> {
Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/UiTheme.java b/src/main/java/org/cryptomator/common/settings/UiTheme.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/settings/UiTheme.java
rename to src/main/java/org/cryptomator/common/settings/UiTheme.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
similarity index 84%
rename from main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java
rename to src/main/java/org/cryptomator/common/settings/VaultSettings.java
index b273bb642..8ae20406c 100644
--- a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettings.java
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettings.java
@@ -24,8 +24,6 @@ import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
-import java.util.Set;
-import java.util.stream.Collectors;
/**
* The settings specific to a single vault.
@@ -33,28 +31,31 @@ import java.util.stream.Collectors;
public class VaultSettings {
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
- public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
+ public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
public static final boolean DEFAULT_USES_READONLY_MODE = false;
public static final String DEFAULT_MOUNT_FLAGS = "";
- public static final int DEFAULT_FILENAME_LENGTH_LIMIT = -1;
+ public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
+ public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
+ public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
private static final Random RNG = new Random();
private final String id;
- private final ObjectProperty path = new SimpleObjectProperty();
+ private final ObjectProperty path = new SimpleObjectProperty<>();
private final StringProperty displayName = new SimpleStringProperty();
private final StringProperty winDriveLetter = new SimpleStringProperty();
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
- private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
+ private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
private final StringProperty customMountPath = new SimpleStringProperty();
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
- private final IntegerProperty filenameLengthLimit = new SimpleIntegerProperty(DEFAULT_FILENAME_LENGTH_LIMIT);
+ private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
private final ObjectProperty actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
-
+ private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
+ private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
private final StringBinding mountName;
public VaultSettings(String id) {
@@ -63,7 +64,7 @@ public class VaultSettings {
}
Observable[] observables() {
- return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, filenameLengthLimit, actionAfterUnlock};
+ return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, maxCleartextFilenameLength, actionAfterUnlock, autoLockWhenIdle, autoLockIdleSeconds};
}
public static VaultSettings withRandomId() {
@@ -152,8 +153,8 @@ public class VaultSettings {
return mountFlags;
}
- public IntegerProperty filenameLengthLimit() {
- return filenameLengthLimit;
+ public IntegerProperty maxCleartextFilenameLength() {
+ return maxCleartextFilenameLength;
}
public ObjectProperty actionAfterUnlock() {
@@ -164,6 +165,14 @@ public class VaultSettings {
return actionAfterUnlock.get();
}
+ public BooleanProperty autoLockWhenIdle() {
+ return autoLockWhenIdle;
+ }
+
+ public IntegerProperty autoLockIdleSeconds() {
+ return autoLockIdleSeconds;
+ }
+
/* Hashcode/Equals */
@Override
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java b/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
similarity index 82%
rename from main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
rename to src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
index 04a352a49..15a081f0d 100644
--- a/main/commons/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
+++ b/src/main/java/org/cryptomator/common/settings/VaultSettingsJsonAdapter.java
@@ -29,8 +29,10 @@ class VaultSettingsJsonAdapter {
out.name("customMountPath").value(value.customMountPath().get());
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
out.name("mountFlags").value(value.mountFlags().get());
- out.name("filenameLengthLimit").value(value.filenameLengthLimit().get());
+ out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
+ out.name("autoLockWhenIdle").value(value.autoLockWhenIdle().get());
+ out.name("autoLockIdleSeconds").value(value.autoLockIdleSeconds().get());
out.endObject();
}
@@ -42,12 +44,14 @@ class VaultSettingsJsonAdapter {
String customMountPath = null;
String winDriveLetter = null;
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
- boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
+ boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
- int filenameLengthLimit = VaultSettings.DEFAULT_FILENAME_LENGTH_LIMIT;
+ int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
+ boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
+ int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
in.beginObject();
while (in.hasNext()) {
@@ -64,8 +68,10 @@ class VaultSettingsJsonAdapter {
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
case "mountFlags" -> mountFlags = in.nextString();
- case "filenameLengthLimit" -> filenameLengthLimit = in.nextInt();
+ case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
+ case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
+ case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
@@ -88,8 +94,10 @@ class VaultSettingsJsonAdapter {
vaultSettings.customMountPath().set(customMountPath);
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
vaultSettings.mountFlags().set(mountFlags);
- vaultSettings.filenameLengthLimit().set(filenameLengthLimit);
+ vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
+ vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
+ vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
return vaultSettings;
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java b/src/main/java/org/cryptomator/common/settings/VolumeImpl.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/settings/VolumeImpl.java
rename to src/main/java/org/cryptomator/common/settings/VolumeImpl.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/WebDavUrlScheme.java b/src/main/java/org/cryptomator/common/settings/WebDavUrlScheme.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/settings/WebDavUrlScheme.java
rename to src/main/java/org/cryptomator/common/settings/WebDavUrlScheme.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java b/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
rename to src/main/java/org/cryptomator/common/settings/WhenUnlocked.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/AbstractVolume.java b/src/main/java/org/cryptomator/common/vaults/AbstractVolume.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/AbstractVolume.java
rename to src/main/java/org/cryptomator/common/vaults/AbstractVolume.java
diff --git a/src/main/java/org/cryptomator/common/vaults/AutoLocker.java b/src/main/java/org/cryptomator/common/vaults/AutoLocker.java
new file mode 100644
index 000000000..da584086d
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/vaults/AutoLocker.java
@@ -0,0 +1,60 @@
+package org.cryptomator.common.vaults;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javafx.collections.ObservableList;
+import java.time.Instant;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+public class AutoLocker {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AutoLocker.class);
+
+ private final ScheduledExecutorService scheduler;
+ private final ObservableList vaultList;
+
+ @Inject
+ public AutoLocker(ScheduledExecutorService scheduler, ObservableList vaultList) {
+ this.scheduler = scheduler;
+ this.vaultList = vaultList;
+ }
+
+ public void init() {
+ scheduler.scheduleAtFixedRate(this::tick, 0, 1, TimeUnit.MINUTES);
+ }
+
+ private void tick() {
+ vaultList.stream() // all vaults
+ .filter(Vault::isUnlocked) // unlocked vaults
+ .filter(this::exceedsIdleTime) // idle vaults
+ .forEach(this::autolock);
+ }
+
+ private void autolock(Vault vault) {
+ try {
+ vault.lock(false);
+ LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
+ } catch (Volume.VolumeException | LockNotCompletedException e) {
+ LOG.error("Autolocking failed.", e);
+ }
+ }
+
+ private boolean exceedsIdleTime(Vault vault) {
+ assert vault.isUnlocked();
+ // TODO: shouldn't we read these properties from within FX Application Thread?
+ if (vault.getVaultSettings().autoLockWhenIdle().get()) {
+ int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
+ var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
+ return deadline.isBefore(Instant.now());
+ } else {
+ return false;
+ }
+ }
+
+
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java b/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
rename to src/main/java/org/cryptomator/common/vaults/DefaultMountFlags.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java b/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
rename to src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java b/src/main/java/org/cryptomator/common/vaults/FuseVolume.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java
rename to src/main/java/org/cryptomator/common/vaults/FuseVolume.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java b/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java
rename to src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java b/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java
rename to src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/PerVault.java b/src/main/java/org/cryptomator/common/vaults/PerVault.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/PerVault.java
rename to src/main/java/org/cryptomator/common/vaults/PerVault.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java
similarity index 82%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
rename to src/main/java/org/cryptomator/common/vaults/Vault.java
index 319272687..96bf3b252 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
+++ b/src/main/java/org/cryptomator/common/vaults/Vault.java
@@ -17,10 +17,10 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
-import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.CryptoException;
-import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.cryptolib.api.MasterkeyLoader;
+import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +35,6 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.io.IOException;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
@@ -45,13 +44,12 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
-
@PerVault
public class Vault {
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
+ private static final int UNLIMITED_FILENAME_LENGTH = Integer.MAX_VALUE;
private final VaultSettings vaultSettings;
private final Provider volumeProvider;
@@ -59,6 +57,7 @@ public class Vault {
private final AtomicReference cryptoFileSystem;
private final VaultState state;
private final ObjectProperty lastKnownException;
+ private final VaultConfigCache configCache;
private final VaultStats stats;
private final StringBinding displayName;
private final StringBinding displayablePath;
@@ -75,8 +74,9 @@ public class Vault {
private volatile Volume volume;
@Inject
- Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) {
+ Vault(VaultSettings vaultSettings, VaultConfigCache configCache, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) {
this.vaultSettings = vaultSettings;
+ this.configCache = configCache;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
this.cryptoFileSystem = cryptoFileSystem;
@@ -100,32 +100,31 @@ public class Vault {
// Commands
// ********************************************************************************/
- private CryptoFileSystem createCryptoFileSystem(CharSequence passphrase) throws NoSuchFileException, IOException, InvalidPassphraseException, CryptoException {
+ private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
Set flags = EnumSet.noneOf(FileSystemFlags.class);
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
+ } else if (vaultSettings.maxCleartextFilenameLength().get() == -1) {
+ LOG.debug("Determining cleartext filename length limitations...");
+ var checker = new FileSystemCapabilityChecker();
+ int shorteningThreshold = configCache.get().allegedShorteningThreshold();
+ int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
+ if (ciphertextLimit < shorteningThreshold) {
+ int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
+ vaultSettings.maxCleartextFilenameLength().set(cleartextLimit);
+ } else {
+ vaultSettings.maxCleartextFilenameLength().setValue(UNLIMITED_FILENAME_LENGTH);
+ }
}
- int usedFilenameLengthLimit;
- var fileSystemCapabilityChecker = new FileSystemCapabilityChecker();
- if (flags.contains(FileSystemFlags.READONLY)) {
- usedFilenameLengthLimit = Constants.MAX_CIPHERTEXT_NAME_LENGTH;
- } else if (vaultSettings.filenameLengthLimit().get() == -1) {
- LOG.debug("Determining file name length limitations...");
- usedFilenameLengthLimit = fileSystemCapabilityChecker.determineSupportedFileNameLength(getPath());
- vaultSettings.filenameLengthLimit().set(usedFilenameLengthLimit);
- LOG.info("Storing file name length limit of {}", usedFilenameLengthLimit);
- } else {
- usedFilenameLengthLimit = vaultSettings.filenameLengthLimit().get();
+ if (vaultSettings.maxCleartextFilenameLength().get() < UNLIMITED_FILENAME_LENGTH) {
+ LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength().get());
}
- assert usedFilenameLengthLimit > 0;
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
- .withPassphrase(passphrase) //
+ .withKeyLoader(keyLoader) //
.withFlags(flags) //
- .withMasterkeyFilename(MASTERKEY_FILENAME) //
- .withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) //
- .withMaxNameLength(usedFilenameLengthLimit) //
+ .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
@@ -142,20 +141,22 @@ public class Vault {
}
}
- public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
- if (cryptoFileSystem.get() == null) {
- CryptoFileSystem fs = createCryptoFileSystem(passphrase);
- cryptoFileSystem.set(fs);
- try {
- volume = volumeProvider.get();
- volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
- } catch (Exception e) {
- destroyCryptoFileSystem();
- throw e;
- }
- } else {
+ public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
+ if (cryptoFileSystem.get() != null) {
throw new IllegalStateException("Already unlocked.");
}
+ CryptoFileSystem fs = createCryptoFileSystem(keyLoader);
+ boolean success = false;
+ try {
+ cryptoFileSystem.set(fs);
+ volume = volumeProvider.get();
+ volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
+ success = true;
+ } finally {
+ if (!success) {
+ destroyCryptoFileSystem();
+ }
+ }
}
private void lockOnVolumeExit(Throwable t) {
@@ -324,6 +325,7 @@ public class Vault {
return stats;
}
+
public Observable[] observables() {
return new Observable[]{state};
}
@@ -357,6 +359,10 @@ public class Vault {
}
}
+ public VaultConfigCache getVaultConfigCache() {
+ return configCache;
+ }
+
public void setCustomMountFlags(String mountFlags) {
vaultSettings.mountFlags().set(mountFlags);
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java b/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
similarity index 90%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
rename to src/main/java/org/cryptomator/common/vaults/VaultComponent.java
index 47be62520..be844f510 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
@@ -7,10 +7,10 @@ package org.cryptomator.common.vaults;
import dagger.BindsInstance;
import dagger.Subcomponent;
+import org.cryptomator.common.Nullable;
import org.cryptomator.common.mountpoint.MountPointChooserModule;
import org.cryptomator.common.settings.VaultSettings;
-import javax.annotation.Nullable;
import javax.inject.Named;
@PerVault
@@ -25,6 +25,9 @@ public interface VaultComponent {
@BindsInstance
Builder vaultSettings(VaultSettings vaultSettings);
+ @BindsInstance
+ Builder vaultConfigCache(VaultConfigCache configCache);
+
@BindsInstance
Builder initialVaultState(VaultState.Value vaultState);
diff --git a/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java b/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java
new file mode 100644
index 000000000..80d70ffbf
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java
@@ -0,0 +1,65 @@
+package org.cryptomator.common.vaults;
+
+import org.cryptomator.common.Constants;
+import org.cryptomator.common.settings.VaultSettings;
+import org.cryptomator.cryptofs.VaultConfig;
+import org.cryptomator.cryptofs.VaultConfigLoadException;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Wrapper for lazy loading and on-demand reloading of the vault configuration.
+ */
+public class VaultConfigCache {
+
+ private final VaultSettings settings;
+ private final AtomicReference config;
+
+ VaultConfigCache(VaultSettings settings) {
+ this.settings = settings;
+ this.config = new AtomicReference<>(null);
+ }
+
+ void reloadConfig() throws IOException {
+ try {
+ config.set(readConfigFromStorage(this.settings.path().get()));
+ } catch (IOException e) {
+ config.set(null);
+ throw e;
+ }
+ }
+
+ public VaultConfig.UnverifiedVaultConfig get() throws IOException {
+ if (config.get() == null) {
+ reloadConfig();
+ }
+ return config.get();
+ }
+
+ public VaultConfig.UnverifiedVaultConfig getUnchecked() {
+ try {
+ return get();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+
+ /**
+ * Attempts to read the vault config file and parse it without verifying its integrity.
+ *
+ * @throws VaultConfigLoadException if the read file cannot be properly parsed
+ * @throws IOException if reading the file fails
+ */
+ static VaultConfig.UnverifiedVaultConfig readConfigFromStorage(Path vaultPath) throws IOException {
+ Path configPath = vaultPath.resolve(Constants.VAULTCONFIG_FILENAME);
+ String token = Files.readString(configPath, StandardCharsets.US_ASCII);
+ return VaultConfig.decode(token);
+ }
+
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListChangeListener.java b/src/main/java/org/cryptomator/common/vaults/VaultListChangeListener.java
similarity index 100%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultListChangeListener.java
rename to src/main/java/org/cryptomator/common/vaults/VaultListChangeListener.java
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
similarity index 65%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
rename to src/main/java/org/cryptomator/common/vaults/VaultListManager.java
index 4b8ca71e8..6a8c31d4f 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
@@ -11,15 +11,16 @@ package org.cryptomator.common.vaults;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.migration.Migrators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
-import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Collection;
@@ -27,43 +28,44 @@ import java.util.Optional;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
+import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
+import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
+import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
@Singleton
public class VaultListManager {
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
+ private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder;
private final ObservableList vaultList;
private final String defaultVaultName;
@Inject
- public VaultListManager(VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
+ public VaultListManager(ObservableList vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
+ this.vaultList = vaultList;
+ this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
- this.vaultList = FXCollections.observableArrayList(Vault::observables);
addAll(settings.getDirectories());
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
+ autoLocker.init();
}
- public ObservableList getVaultList() {
- return vaultList;
- }
-
- public Vault add(Path pathToVault) throws NoSuchFileException {
+ public Vault add(Path pathToVault) throws IOException {
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
- if (!CryptoFileSystemProvider.containsVault(normalizedPathToVault, MASTERKEY_FILENAME)) {
+ if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory");
}
- Optional alreadyExistingVault = get(normalizedPathToVault);
- if (alreadyExistingVault.isPresent()) {
- return alreadyExistingVault.get();
- } else {
- Vault newVault = create(newVaultSettings(normalizedPathToVault));
- vaultList.add(newVault);
- return newVault;
- }
+
+ return get(normalizedPathToVault) //
+ .orElseGet(() -> {
+ Vault newVault = create(newVaultSettings(normalizedPathToVault));
+ vaultList.add(newVault);
+ return newVault;
+ });
}
private VaultSettings newVaultSettings(Path path) {
@@ -94,10 +96,15 @@ public class VaultListManager {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
try {
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
+ VaultConfigCache wrapper = new VaultConfigCache(vaultSettings);
+ compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config
+ if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
+ wrapper.reloadConfig();
+ }
compBuilder.initialVaultState(vaultState);
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
- compBuilder.initialVaultState(VaultState.Value.ERROR);
+ compBuilder.initialVaultState(ERROR);
compBuilder.initialErrorCause(e);
}
return compBuilder.build().vault();
@@ -109,14 +116,17 @@ public class VaultListManager {
return switch (previousState) {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
try {
- VaultState.Value determinedState = determineVaultState(vault.getPath());
+ var determinedState = determineVaultState(vault.getPath());
+ if (determinedState == LOCKED) {
+ vault.getVaultConfigCache().reloadConfig();
+ }
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
- state.set(VaultState.Value.ERROR);
+ state.set(ERROR);
vault.setLastKnownException(e);
- yield VaultState.Value.ERROR;
+ yield ERROR;
}
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
@@ -124,13 +134,16 @@ public class VaultListManager {
}
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
- if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
+ if (!Files.exists(pathToVault)) {
return VaultState.Value.MISSING;
- } else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
- return VaultState.Value.NEEDS_MIGRATION;
- } else {
- return VaultState.Value.LOCKED;
}
+ return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
+ case VAULT -> VaultState.Value.LOCKED;
+ case UNRELATED -> VaultState.Value.MISSING;
+ case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? //
+ VaultState.Value.NEEDS_MIGRATION //
+ : VaultState.Value.MISSING;
+ };
}
}
diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListModule.java b/src/main/java/org/cryptomator/common/vaults/VaultListModule.java
new file mode 100644
index 000000000..644d05335
--- /dev/null
+++ b/src/main/java/org/cryptomator/common/vaults/VaultListModule.java
@@ -0,0 +1,19 @@
+package org.cryptomator.common.vaults;
+
+import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Singleton;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+@Module
+public class VaultListModule {
+
+ @Provides
+ @Singleton
+ public ObservableList provideVaultList() {
+ return FXCollections.observableArrayList(Vault::observables);
+ }
+
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java b/src/main/java/org/cryptomator/common/vaults/VaultModule.java
similarity index 97%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java
rename to src/main/java/org/cryptomator/common/vaults/VaultModule.java
index b6e070310..cc38e6933 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultModule.java
@@ -8,6 +8,7 @@ package org.cryptomator.common.vaults;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.Nullable;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
@@ -15,7 +16,6 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
@@ -47,7 +47,6 @@ public class VaultModule {
return new SimpleObjectProperty<>(initialErrorCause);
}
-
@Provides
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
@@ -99,7 +98,6 @@ public class VaultModule {
flags.append(" -oatomic_o_trunc");
flags.append(" -oauto_xattr");
flags.append(" -oauto_cache");
- flags.append(" -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
@@ -140,7 +138,7 @@ public class VaultModule {
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse_main.c#L53-L62 for syntax guide
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse.c#L295-L319 for options (-o <...>)
- // see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were choosen
+ // see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were chosen
private String getWindowsFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_WINDOWS;
StringBuilder flags = new StringBuilder();
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java b/src/main/java/org/cryptomator/common/vaults/VaultState.java
similarity index 98%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java
rename to src/main/java/org/cryptomator/common/vaults/VaultState.java
index 801ea7653..51365fbd2 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultState.java
@@ -46,7 +46,7 @@ public class VaultState extends ObservableValueBase implements
UNLOCKED,
/**
- * Unknown state due to preceeding unrecoverable exceptions.
+ * Unknown state due to preceding unrecoverable exceptions.
*/
ERROR;
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java b/src/main/java/org/cryptomator/common/vaults/VaultStats.java
similarity index 80%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java
rename to src/main/java/org/cryptomator/common/vaults/VaultStats.java
index 6dc86e8be..ac0b8df38 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java
+++ b/src/main/java/org/cryptomator/common/vaults/VaultStats.java
@@ -13,9 +13,11 @@ import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
+import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
+import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,12 +35,13 @@ public class VaultStats {
private final LongProperty bytesPerSecondEncrypted = new SimpleLongProperty();
private final LongProperty bytesPerSecondDecrypted = new SimpleLongProperty();
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
- private final LongProperty toalBytesRead = new SimpleLongProperty();
- private final LongProperty toalBytesWritten = new SimpleLongProperty();
+ private final LongProperty totalBytesRead = new SimpleLongProperty();
+ private final LongProperty totalBytesWritten = new SimpleLongProperty();
private final LongProperty totalBytesEncrypted = new SimpleLongProperty();
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
private final LongProperty filesWritten = new SimpleLongProperty();
+ private final ObjectProperty lastActivity = new SimpleObjectProperty<>();
@Inject
VaultStats(AtomicReference fs, VaultState state, ExecutorService executor) {
@@ -55,7 +58,10 @@ public class VaultStats {
if (VaultState.Value.UNLOCKED == state.get()) {
assert fs.get() != null;
LOG.debug("start recording stats");
- Platform.runLater(() -> updateService.restart());
+ Platform.runLater(() -> {
+ lastActivity.set(Instant.now());
+ updateService.restart();
+ });
} else {
LOG.debug("stop recording stats");
Platform.runLater(() -> updateService.cancel());
@@ -69,13 +75,19 @@ public class VaultStats {
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
bytesPerSecondDecrypted.set(stats.map(CryptoFileSystemStats::pollBytesDecrypted).orElse(0L));
bytesPerSecondEncrypted.set(stats.map(CryptoFileSystemStats::pollBytesEncrypted).orElse(0L));
- toalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
- toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
+ totalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
+ totalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
+ var oldAccessCount = filesRead.get() + filesWritten.get();
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
+ var newAccessCount = filesRead.get() + filesWritten.get();
+ // check for any I/O activity
+ if (newAccessCount > oldAccessCount) {
+ lastActivity.set(Instant.now());
+ }
}
private double getCacheHitRate(CryptoFileSystemStats stats) {
@@ -134,7 +146,7 @@ public class VaultStats {
return bytesPerSecondEncrypted;
}
- public long getBytesPerSecondEnrypted() {
+ public long getBytesPerSecondEncrypted() {
return bytesPerSecondEncrypted.get();
}
@@ -152,13 +164,13 @@ public class VaultStats {
return cacheHitRate.get();
}
- public LongProperty toalBytesReadProperty() {return toalBytesRead;}
+ public LongProperty totalBytesReadProperty() {return totalBytesRead;}
- public long getTotalBytesRead() { return toalBytesRead.get();}
+ public long getTotalBytesRead() { return totalBytesRead.get();}
- public LongProperty toalBytesWrittenProperty() {return toalBytesWritten;}
+ public LongProperty totalBytesWrittenProperty() {return totalBytesWritten;}
- public long getTotalBytesWritten() { return toalBytesWritten.get();}
+ public long getTotalBytesWritten() { return totalBytesWritten.get();}
public LongProperty totalBytesEncryptedProperty() {return totalBytesEncrypted;}
@@ -175,4 +187,12 @@ public class VaultStats {
public LongProperty filesWritten() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
+
+ public ObjectProperty lastActivityProperty() {
+ return lastActivity;
+ }
+
+ public Instant getLastActivity() {
+ return lastActivity.get();
+ }
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java b/src/main/java/org/cryptomator/common/vaults/Volume.java
similarity index 92%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java
rename to src/main/java/org/cryptomator/common/vaults/Volume.java
index f608122bf..5f434fa43 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java
+++ b/src/main/java/org/cryptomator/common/vaults/Volume.java
@@ -12,7 +12,7 @@ import java.util.function.Consumer;
import java.util.stream.Stream;
/**
- * Takes a Volume and usess it to mount an unlocked vault
+ * Takes a Volume and uses it to mount an unlocked vault
*/
public interface Volume {
@@ -24,7 +24,7 @@ public interface Volume {
boolean isSupported();
/**
- * Gets the coresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
+ * Gets the corresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
*
* @return the type of implementation as defined by the {@link VolumeImpl VolumeImpl enum}
*/
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java b/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
similarity index 96%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
rename to src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
index 03c83377c..3ac1820d4 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
+++ b/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
@@ -67,10 +67,10 @@ public class WebDavVolume implements Volume {
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
}
- //on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specifc one or there is no free.
+ //on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specific one or there is no free.
Supplier driveLetterSupplier;
if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
- driveLetterSupplier = () -> windowsDriveLetters.getAvailableDriveLetter().orElse(null);
+ driveLetterSupplier = () -> windowsDriveLetters.getDesiredAvailableDriveLetter().orElse(null);
} else {
driveLetterSupplier = () -> vaultSettings.winDriveLetter().get();
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java b/src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java
similarity index 64%
rename from main/commons/src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java
rename to src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java
index f3ef2c7dc..9ca56b462 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java
+++ b/src/main/java/org/cryptomator/common/vaults/WindowsDriveLetters.java
@@ -22,11 +22,11 @@ import java.util.stream.StreamSupport;
@Singleton
public final class WindowsDriveLetters {
- private static final Set C_TO_Z;
+ private static final Set A_TO_Z;
static {
- try (IntStream stream = IntStream.rangeClosed('C', 'Z')) {
- C_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(ImmutableSet.toImmutableSet());
+ try (IntStream stream = IntStream.rangeClosed('A', 'Z')) {
+ A_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(ImmutableSet.toImmutableSet());
}
}
@@ -35,7 +35,7 @@ public final class WindowsDriveLetters {
}
public Set getAllDriveLetters() {
- return C_TO_Z;
+ return A_TO_Z;
}
public Set getOccupiedDriveLetters() {
@@ -59,6 +59,26 @@ public final class WindowsDriveLetters {
return getAvailableDriveLetter().map(this::toPath);
}
+ /**
+ * Skips A and B and only returns them if all other are occupied.
+ *
+ * @return an Optional containing either the letter of a free drive letter or empty, if none is available
+ */
+ public Optional getDesiredAvailableDriveLetter() {
+ var availableDriveLetters = getAvailableDriveLetters();
+ var optString = availableDriveLetters.stream().filter(s -> !(s.equals("A") || s.equals("B"))).findFirst();
+ return optString.or(() -> availableDriveLetters.stream().findFirst());
+ }
+
+ /**
+ * Skips A and B and only returns them if all other are occupied.
+ *
+ * @return an Optional containing either the path to a free drive letter or empty, if none is available
+ */
+ public Optional getDesiredAvailableDriveLetterPath() {
+ return getDesiredAvailableDriveLetter().map(this::toPath);
+ }
+
public Path toPath(String driveLetter) {
return Path.of(driveLetter + ":\\");
}
diff --git a/src/main/java/org/cryptomator/ipc/Client.java b/src/main/java/org/cryptomator/ipc/Client.java
new file mode 100644
index 000000000..fcd084032
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/Client.java
@@ -0,0 +1,65 @@
+package org.cryptomator.ipc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.UnixDomainSocketAddress;
+import java.nio.channels.SocketChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.concurrent.Executor;
+
+class Client implements IpcCommunicator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Client.class);
+
+ private final SocketChannel socketChannel;
+
+ private Client(SocketChannel socketChannel) {
+ this.socketChannel = socketChannel;
+ }
+
+ public static Client create(Path socketPath) throws IOException {
+ var address = UnixDomainSocketAddress.of(socketPath);
+ var socketChannel = SocketChannel.open(address);
+ LOG.info("Connected to IPC server on socket {}", socketPath);
+ return new Client(socketChannel);
+ }
+
+ @Override
+ public boolean isClient() {
+ return true;
+ }
+
+ @Override
+ public void listen(IpcMessageListener listener, Executor executor) {
+ executor.execute(() -> {
+ try {
+ while (socketChannel.isConnected()) {
+ var msg = IpcMessage.receive(socketChannel);
+ listener.handleMessage(msg);
+ }
+ } catch (IOException e) {
+ LOG.error("Failed to read IPC message", e);
+ }
+ });
+ }
+
+ @Override
+ public void send(IpcMessage message, Executor executor) {
+ executor.execute(() -> {
+ try {
+ message.send(socketChannel);
+ } catch (IOException e) {
+ LOG.error("Failed to send IPC message", e);
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ socketChannel.close();
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/HandleLaunchArgsMessage.java b/src/main/java/org/cryptomator/ipc/HandleLaunchArgsMessage.java
new file mode 100644
index 000000000..0a7916c16
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/HandleLaunchArgsMessage.java
@@ -0,0 +1,30 @@
+package org.cryptomator.ipc;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+record HandleLaunchArgsMessage(List args) implements IpcMessage {
+
+ private static final char DELIMITER = '\n';
+
+ public static HandleLaunchArgsMessage decode(ByteBuffer encoded) {
+ var str = StandardCharsets.UTF_8.decode(encoded).toString();
+ var args = Splitter.on(DELIMITER).omitEmptyStrings().splitToList(str);
+ return new HandleLaunchArgsMessage(args);
+ }
+
+ @Override
+ public MessageType getMessageType() {
+ return MessageType.HANDLE_LAUNCH_ARGS;
+ }
+
+ @Override
+ public ByteBuffer encodePayload() {
+ var str = Joiner.on(DELIMITER).join(args);
+ return StandardCharsets.UTF_8.encode(str);
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/IpcCommunicator.java b/src/main/java/org/cryptomator/ipc/IpcCommunicator.java
new file mode 100644
index 000000000..0120389c9
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/IpcCommunicator.java
@@ -0,0 +1,96 @@
+package org.cryptomator.ipc;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
+public interface IpcCommunicator extends Closeable {
+
+ Logger LOG = LoggerFactory.getLogger(IpcCommunicator.class);
+
+ /**
+ * Attempts to establish a socket connection via one of the given paths.
+ *
+ * If no connection to an existing sockets can be established, a new socket is created for the first given path.
+ *
+ * If this fails as well, a fallback communicator is returned that allows process-internal communication mocking the API
+ * that would have been used for IPC.
+ *
+ * @param socketPaths The socket path(s)
+ * @return A communicator object that allows sending and receiving messages
+ */
+ static IpcCommunicator create(Iterable socketPaths) {
+ Preconditions.checkArgument(socketPaths.iterator().hasNext(), "socketPaths must contain at least one element");
+ for (var p : socketPaths) {
+ try {
+ var attr = Files.readAttributes(p, BasicFileAttributes.class);
+ if (attr.isOther()) {
+ return Client.create(p);
+ }
+ } catch (IOException e) {
+ // attempt next socket path
+ }
+ }
+ // Didn't get any connection yet? I.e. we're the first app instance, so let's launch a server:
+ try {
+ return Server.create(socketPaths.iterator().next());
+ } catch (IOException e) {
+ LOG.warn("Failed to create IPC server", e);
+ return new LoopbackCommunicator();
+ }
+ }
+
+ boolean isClient();
+
+ /**
+ * Listens to incoming messages until the connection gets closed.
+ * @param listener The listener that should be notified of incoming messages
+ * @param executor An executor on which to listen. Listening will block, so you might want to use a background thread.
+ * @return
+ */
+ void listen(IpcMessageListener listener, Executor executor);
+
+ /**
+ * Sends the given message.
+ *
+ * @param message The message to send
+ * @param executor An executor used to send the message. Sending will block, so you might want to use a background thread.
+ */
+ void send(IpcMessage message, Executor executor);
+
+ default void sendRevealRunningApp() {
+ send(new RevealRunningAppMessage(), MoreExecutors.directExecutor());
+ }
+
+ default void sendHandleLaunchargs(List args) {
+ send(new HandleLaunchArgsMessage(args), MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Clean up resources.
+ *
+ * @implSpec Must be idempotent
+ * @throws IOException In case of I/O errors.
+ */
+ @Override
+ void close() throws IOException;
+
+ default void closeUnchecked() throws UncheckedIOException {
+ try {
+ close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/IpcMessage.java b/src/main/java/org/cryptomator/ipc/IpcMessage.java
new file mode 100644
index 000000000..9d1c8d3de
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/IpcMessage.java
@@ -0,0 +1,68 @@
+package org.cryptomator.ipc;
+
+import org.cryptomator.cryptolib.common.ByteBuffers;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.function.Function;
+
+// TODO make sealed, remove enum
+interface IpcMessage {
+
+ enum MessageType {
+ REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
+ HANDLE_LAUNCH_ARGS(HandleLaunchArgsMessage::decode);
+
+ private final Function decoder;
+
+ MessageType(Function decoder) {
+ this.decoder = decoder;
+ }
+
+ static MessageType forOrdinal(int ordinal) {
+ try {
+ return values()[ordinal];
+ } catch (IndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("No such message type: " + ordinal, e);
+ }
+ }
+
+ IpcMessage decodePayload(ByteBuffer payload) {
+ return decoder.apply(payload);
+ }
+ }
+
+ MessageType getMessageType();
+
+ ByteBuffer encodePayload();
+
+ static IpcMessage receive(ReadableByteChannel channel) throws IOException {
+ var header = ByteBuffer.allocate(2 * Integer.BYTES);
+ if (ByteBuffers.fill(channel, header) < header.capacity()) {
+ throw new EOFException();
+ }
+ header.flip();
+ int typeNo = header.getInt();
+ int length = header.getInt();
+ MessageType type = MessageType.forOrdinal(typeNo);
+ var payload = ByteBuffer.allocate(length);
+ ByteBuffers.fill(channel, payload);
+ payload.flip();
+ return type.decodePayload(payload);
+ }
+
+ default void send(WritableByteChannel channel) throws IOException {
+ var payload = encodePayload();
+ var buf = ByteBuffer.allocate(2 * Integer.BYTES + payload.remaining());
+ buf.putInt(getMessageType().ordinal()); // message type
+ buf.putInt(payload.remaining()); // message length
+ buf.put(payload); // message
+ buf.flip();
+ while (buf.hasRemaining()) {
+ channel.write(buf);
+ }
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/IpcMessageListener.java b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java
new file mode 100644
index 000000000..f49275824
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/IpcMessageListener.java
@@ -0,0 +1,19 @@
+package org.cryptomator.ipc;
+
+import java.util.List;
+
+public interface IpcMessageListener {
+
+ default void handleMessage(IpcMessage message) {
+ if (message instanceof RevealRunningAppMessage) {
+ revealRunningApp();
+ } else if (message instanceof HandleLaunchArgsMessage m) {
+ handleLaunchArgs(m.args());
+ }
+ }
+
+ void revealRunningApp();
+
+ void handleLaunchArgs(List args);
+
+}
diff --git a/src/main/java/org/cryptomator/ipc/LoopbackCommunicator.java b/src/main/java/org/cryptomator/ipc/LoopbackCommunicator.java
new file mode 100644
index 000000000..ba5152c93
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/LoopbackCommunicator.java
@@ -0,0 +1,50 @@
+package org.cryptomator.ipc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.TransferQueue;
+
+class LoopbackCommunicator implements IpcCommunicator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LoopbackCommunicator.class);
+
+ private final TransferQueue transferQueue = new LinkedTransferQueue<>();
+
+ @Override
+ public boolean isClient() {
+ return false;
+ }
+
+ @Override
+ public void listen(IpcMessageListener listener, Executor executor) {
+ executor.execute(() -> {
+ try {
+ var msg = transferQueue.take();
+ listener.handleMessage(msg);
+ } catch (InterruptedException e) {
+ LOG.error("Failed to read IPC message", e);
+ Thread.currentThread().interrupt();
+ }
+ });
+ }
+
+ @Override
+ public void send(IpcMessage message, Executor executor) {
+ executor.execute(() -> {
+ try {
+ transferQueue.put(message);
+ } catch (InterruptedException e) {
+ LOG.error("Failed to send IPC message", e);
+ Thread.currentThread().interrupt();
+ }
+ });
+ }
+
+ @Override
+ public void close() {
+ // no-op
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/RevealRunningAppMessage.java b/src/main/java/org/cryptomator/ipc/RevealRunningAppMessage.java
new file mode 100644
index 000000000..fa4d1375b
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/RevealRunningAppMessage.java
@@ -0,0 +1,20 @@
+package org.cryptomator.ipc;
+
+import java.nio.ByteBuffer;
+
+public record RevealRunningAppMessage() implements IpcMessage {
+
+ static RevealRunningAppMessage decode(ByteBuffer ignored) {
+ return new RevealRunningAppMessage();
+ }
+
+ @Override
+ public MessageType getMessageType() {
+ return MessageType.REVEAL_RUNNING_APP;
+ }
+
+ @Override
+ public ByteBuffer encodePayload() {
+ return ByteBuffer.allocate(0);
+ }
+}
diff --git a/src/main/java/org/cryptomator/ipc/Server.java b/src/main/java/org/cryptomator/ipc/Server.java
new file mode 100644
index 000000000..6058a608f
--- /dev/null
+++ b/src/main/java/org/cryptomator/ipc/Server.java
@@ -0,0 +1,83 @@
+package org.cryptomator.ipc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.StandardProtocolFamily;
+import java.net.UnixDomainSocketAddress;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.Executor;
+
+class Server implements IpcCommunicator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Server.class);
+
+ private final ServerSocketChannel serverSocketChannel;
+ private final Path socketPath;
+
+ private Server(ServerSocketChannel serverSocketChannel, Path socketPath) {
+ this.serverSocketChannel = serverSocketChannel;
+ this.socketPath = socketPath;
+ }
+
+ public static Server create(Path socketPath) throws IOException {
+ Files.createDirectories(socketPath.getParent());
+ var address = UnixDomainSocketAddress.of(socketPath);
+ var serverSocketChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
+ serverSocketChannel.bind(address);
+ LOG.info("Spawning IPC server listening on socket {}", socketPath);
+ return new Server(serverSocketChannel, socketPath);
+ }
+
+ @Override
+ public boolean isClient() {
+ return false;
+ }
+
+ @Override
+ public void listen(IpcMessageListener listener, Executor executor) {
+ executor.execute(() -> {
+ while (serverSocketChannel.isOpen()) {
+ try (var ch = serverSocketChannel.accept()) {
+ while (ch.isConnected()) {
+ var msg = IpcMessage.receive(ch);
+ listener.handleMessage(msg);
+ }
+ } catch (AsynchronousCloseException e) {
+ return; // serverSocketChannel closed or listener interrupted
+ } catch (EOFException | ClosedChannelException e) {
+ // continue with next connected client
+ } catch (IOException e) {
+ LOG.error("Failed to read IPC message", e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void send(IpcMessage message, Executor executor) {
+ executor.execute(() -> {
+ try (var ch = serverSocketChannel.accept()) {
+ message.send(ch);
+ } catch (IOException e) {
+ LOG.error("Failed to send IPC message", e);
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ serverSocketChannel.close();
+ } finally {
+ Files.deleteIfExists(socketPath);
+ LOG.debug("IPC server closed");
+ }
+ }
+}
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java b/src/main/java/org/cryptomator/launcher/Cryptomator.java
similarity index 64%
rename from main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java
rename to src/main/java/org/cryptomator/launcher/Cryptomator.java
index 04eb9448d..5502d804a 100644
--- a/main/launcher/src/main/java/org/cryptomator/launcher/Cryptomator.java
+++ b/src/main/java/org/cryptomator/launcher/Cryptomator.java
@@ -5,7 +5,12 @@
*******************************************************************************/
package org.cryptomator.launcher;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.Environment;
+import org.cryptomator.common.ShutdownHook;
+import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration;
import org.cryptomator.ui.launcher.UiLauncher;
@@ -16,8 +21,10 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
@Singleton
public class Cryptomator {
@@ -29,23 +36,26 @@ public class Cryptomator {
private final LoggerConfiguration logConfig;
private final DebugMode debugMode;
- private final IpcFactory ipcFactory;
- private final Optional applicationVersion;
+ private final Environment env;
+ private final Lazy ipcMessageHandler;
private final CountDownLatch shutdownLatch;
- private final UiLauncher uiLauncher;
+ private final ShutdownHook shutdownHook;
+ private final Lazy uiLauncher;
@Inject
- Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, UiLauncher uiLauncher) {
+ Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy uiLauncher) {
this.logConfig = logConfig;
this.debugMode = debugMode;
- this.ipcFactory = ipcFactory;
- this.applicationVersion = applicationVersion;
+ this.env = env;
+ this.ipcMessageHandler = ipcMessageHandler;
this.shutdownLatch = shutdownLatch;
+ this.shutdownHook = shutdownHook;
this.uiLauncher = uiLauncher;
}
public static void main(String[] args) {
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
+ LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads.
}
@@ -57,26 +67,31 @@ public class Cryptomator {
*/
private int run(String[] args) {
logConfig.init();
- LOG.info("Starting Cryptomator {} on {} {} ({})", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
+ LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
debugMode.initialize();
/*
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
*/
- try (IpcFactory.IpcEndpoint endpoint = ipcFactory.create()) {
- endpoint.getRemote().handleLaunchArgs(args); // if we are the server, getRemote() returns self.
- if (endpoint.isConnectedToRemote()) {
- endpoint.getRemote().revealRunningApp();
+ try (var communicator = IpcCommunicator.create(env.ipcSocketPath().toList())) {
+ if (communicator.isClient()) {
+ communicator.sendHandleLaunchargs(List.of(args));
+ communicator.sendRevealRunningApp();
LOG.info("Found running application instance. Shutting down...");
return 2;
} else {
+ shutdownHook.runOnShutdown(communicator::closeUnchecked);
+ var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build());
+ var msgHandler = ipcMessageHandler.get();
+ msgHandler.handleLaunchArgs(List.of(args));
+ communicator.listen(msgHandler, executor);
LOG.debug("Did not find running application instance. Launching GUI...");
return runGuiApplication();
}
- } catch (IOException e) {
- LOG.error("Failed to initiate inter-process communication.", e);
- return runGuiApplication();
+ } catch (Throwable e) {
+ LOG.error("Running application failed", e);
+ return 1;
}
}
@@ -88,7 +103,7 @@ public class Cryptomator {
*/
private int runGuiApplication() {
try {
- uiLauncher.launch();
+ uiLauncher.get().launch();
shutdownLatch.await();
LOG.info("UI shut down");
return 0;
@@ -98,5 +113,4 @@ public class Cryptomator {
}
}
-
}
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java b/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
similarity index 100%
rename from main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
rename to src/main/java/org/cryptomator/launcher/CryptomatorComponent.java
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java b/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
similarity index 65%
rename from main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
rename to src/main/java/org/cryptomator/launcher/CryptomatorModule.java
index 4afe332b6..906971492 100644
--- a/main/launcher/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
+++ b/src/main/java/org/cryptomator/launcher/CryptomatorModule.java
@@ -18,11 +18,4 @@ class CryptomatorModule {
return new CountDownLatch(1);
}
- @Provides
- @Singleton
- @Named("applicationVersion")
- static Optional provideApplicationVersion() {
- return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
- }
-
}
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
similarity index 92%
rename from main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
rename to src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
index 9d1e3144d..b4e37e1f9 100644
--- a/main/launcher/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
+++ b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java
@@ -22,6 +22,7 @@ import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
@@ -46,13 +47,13 @@ class FileOpenRequestHandler {
tryToEnqueueFileOpenRequest(launchEvent);
}
- public void handleLaunchArgs(String[] args) {
+ public void handleLaunchArgs(List args) {
handleLaunchArgs(FileSystems.getDefault(), args);
}
// visible for testing
- void handleLaunchArgs(FileSystem fs, String[] args) {
- Collection pathsToOpen = Arrays.stream(args).map(str -> {
+ void handleLaunchArgs(FileSystem fs, List args) {
+ Collection pathsToOpen = args.stream().map(str -> {
try {
return fs.getPath(str);
} catch (InvalidPathException e) {
diff --git a/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java b/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
similarity index 60%
rename from main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java
rename to src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
index 44f67e0cd..5c28d05a4 100644
--- a/main/launcher/src/main/java/org/cryptomator/launcher/IpcProtocolImpl.java
+++ b/src/main/java/org/cryptomator/launcher/IpcMessageHandler.java
@@ -1,5 +1,6 @@
package org.cryptomator.launcher;
+import org.cryptomator.ipc.IpcMessageListener;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -7,20 +8,20 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.BlockingQueue;
@Singleton
-class IpcProtocolImpl implements IpcProtocol {
+class IpcMessageHandler implements IpcMessageListener {
- private static final Logger LOG = LoggerFactory.getLogger(IpcProtocolImpl.class);
+ private static final Logger LOG = LoggerFactory.getLogger(IpcMessageHandler.class);
private final FileOpenRequestHandler fileOpenRequestHandler;
private final BlockingQueue launchEventQueue;
@Inject
- public IpcProtocolImpl(FileOpenRequestHandler fileOpenRequestHandler, @Named("launchEventQueue") BlockingQueue launchEventQueue) {
+ public IpcMessageHandler(FileOpenRequestHandler fileOpenRequestHandler, @Named("launchEventQueue") BlockingQueue launchEventQueue) {
this.fileOpenRequestHandler = fileOpenRequestHandler;
this.launchEventQueue = launchEventQueue;
}
@@ -31,8 +32,8 @@ class IpcProtocolImpl implements IpcProtocol {
}
@Override
- public void handleLaunchArgs(String... args) {
- LOG.debug("Received launch args: {}", Arrays.stream(args).reduce((a, b) -> a + ", " + b).orElse(""));
+ public void handleLaunchArgs(List args) {
+ LOG.debug("Received launch args: {}", args.stream().reduce((a, b) -> a + ", " + b).orElse(""));
fileOpenRequestHandler.handleLaunchArgs(args);
}
diff --git a/main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java b/src/main/java/org/cryptomator/logging/DebugMode.java
similarity index 100%
rename from main/launcher/src/main/java/org/cryptomator/logging/DebugMode.java
rename to src/main/java/org/cryptomator/logging/DebugMode.java
diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java b/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
similarity index 86%
rename from main/launcher/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java
rename to src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
index 3879fbdc6..25f5239ab 100644
--- a/main/launcher/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggerinPolicy.java
+++ b/src/main/java/org/cryptomator/logging/LaunchAndSizeBasedTriggeringPolicy.java
@@ -11,12 +11,12 @@ import java.io.File;
*
* @param Event type the policy possibly reacts to
*/
-public class LaunchAndSizeBasedTriggerinPolicy extends TriggeringPolicyBase {
+public class LaunchAndSizeBasedTriggeringPolicy extends TriggeringPolicyBase {
LaunchBasedTriggeringPolicy launchBasedTriggeringPolicy;
SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy;
- public LaunchAndSizeBasedTriggerinPolicy(FileSize threshold) {
+ public LaunchAndSizeBasedTriggeringPolicy(FileSize threshold) {
this.launchBasedTriggeringPolicy = new LaunchBasedTriggeringPolicy<>();
this.sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy<>();
sizeBasedTriggeringPolicy.setMaxFileSize(threshold);
diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LaunchBasedTriggeringPolicy.java b/src/main/java/org/cryptomator/logging/LaunchBasedTriggeringPolicy.java
similarity index 100%
rename from main/launcher/src/main/java/org/cryptomator/logging/LaunchBasedTriggeringPolicy.java
rename to src/main/java/org/cryptomator/logging/LaunchBasedTriggeringPolicy.java
diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java b/src/main/java/org/cryptomator/logging/LoggerConfiguration.java
similarity index 100%
rename from main/launcher/src/main/java/org/cryptomator/logging/LoggerConfiguration.java
rename to src/main/java/org/cryptomator/logging/LoggerConfiguration.java
diff --git a/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java b/src/main/java/org/cryptomator/logging/LoggerModule.java
similarity index 97%
rename from main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java
rename to src/main/java/org/cryptomator/logging/LoggerModule.java
index 0b24e0e24..4866655e3 100644
--- a/main/launcher/src/main/java/org/cryptomator/logging/LoggerModule.java
+++ b/src/main/java/org/cryptomator/logging/LoggerModule.java
@@ -85,7 +85,7 @@ public class LoggerModule {
appender.setContext(context);
appender.setFile(logDir.resolve(LOGFILE_NAME).toString());
appender.setEncoder(encoder);
- LaunchAndSizeBasedTriggerinPolicy triggeringPolicy = new LaunchAndSizeBasedTriggerinPolicy(FileSize.valueOf(LOG_MAX_SIZE));
+ LaunchAndSizeBasedTriggeringPolicy triggeringPolicy = new LaunchAndSizeBasedTriggeringPolicy(FileSize.valueOf(LOG_MAX_SIZE));
triggeringPolicy.setContext(context);
triggeringPolicy.start();
appender.setTriggeringPolicy(triggeringPolicy);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
similarity index 95%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
index 42d243e80..8a5a776ea 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultModule.java
@@ -6,10 +6,10 @@ import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
@@ -33,13 +33,6 @@ import java.util.ResourceBundle;
@Module
public abstract class AddVaultModule {
- @Provides
- @AddVaultWizardScoped
- @Named("newPassword")
- static ObjectProperty provideNewPasswordProperty() {
- return new SimpleObjectProperty<>("");
- }
-
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
@@ -167,8 +160,8 @@ public abstract class AddVaultModule {
@Provides
@IntoMap
@FxControllerKey(NewPasswordController.class)
- static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty password) {
- return new NewPasswordController(resourceBundle, strengthRater, password);
+ static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
+ return new NewPasswordController(resourceBundle, strengthRater);
}
@Binds
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultSuccessController.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWelcomeController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWelcomeController.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWelcomeController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWelcomeController.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardComponent.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardComponent.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardComponent.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardComponent.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardScoped.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardScoped.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardScoped.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardScoped.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardWindow.java b/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardWindow.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardWindow.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/AddVaultWizardWindow.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
similarity index 98%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
index a5398d51b..214ed19b0 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java
@@ -81,7 +81,7 @@ public class ChooseExistingVaultController implements FxController {
Vault newVault = vaultListManager.add(vaultPath.get());
vault.set(newVault);
window.setScene(successScene.get());
- } catch (NoSuchFileException e) {
+ } catch (IOException e) {
LOG.error("Failed to open existing vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
similarity index 91%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
index 39e25d503..35f2be069 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
@@ -29,16 +29,20 @@ import javafx.scene.control.ToggleGroup;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.ResourceBundle;
+import java.util.UUID;
@AddVaultWizardScoped
public class CreateNewVaultLocationController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultLocationController.class);
private static final Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home"));
+ private static final String TEMP_FILE_FORMAT = "cryptomator-%s.tmp";
private final Stage window;
private final Lazy chooseNameScene;
@@ -92,7 +96,7 @@ public class CreateNewVaultLocationController implements FxController {
statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
statusGraphic.set(badLocation);
return false;
- } else if (!Files.isWritable(p.getParent())) {
+ } else if (!isActuallyWritable(p.getParent())) {
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
statusGraphic.set(badLocation);
return false;
@@ -107,6 +111,21 @@ public class CreateNewVaultLocationController implements FxController {
}
}
+ private boolean isActuallyWritable(Path p) {
+ Path tmpFile = p.resolve(String.format(TEMP_FILE_FORMAT, UUID.randomUUID()));
+ try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
+ return true;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ try {
+ Files.deleteIfExists(tmpFile);
+ } catch (IOException e) {
+ LOG.warn("Unable to delete temporary file {}. Needs to be deleted manually.", tmpFile);
+ }
+ }
+ }
+
@FXML
public void initialize() {
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultNameController.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
similarity index 71%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
index c4e226359..578b90969 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java
@@ -5,11 +5,18 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.cryptolib.api.CryptoException;
+import org.cryptomator.cryptolib.api.CryptorProvider;
+import org.cryptomator.cryptolib.api.Masterkey;
+import org.cryptomator.cryptolib.api.MasterkeyLoader;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.Tasks;
+import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,7 +24,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
-import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
@@ -31,13 +37,13 @@ import javafx.scene.control.ToggleGroup;
import javafx.stage.Stage;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.net.URI;
import java.nio.channels.WritableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
-import java.util.Collections;
+import java.security.SecureRandom;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
@@ -48,6 +54,7 @@ import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
public class CreateNewVaultPasswordController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class);
+ private static final URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); // TODO better place?
private final Stage window;
private final Lazy chooseLocationScene;
@@ -62,18 +69,21 @@ public class CreateNewVaultPasswordController implements FxController {
private final StringProperty recoveryKeyProperty;
private final VaultListManager vaultListManager;
private final ResourceBundle resourceBundle;
- private final ObjectProperty password;
private final ReadmeGenerator readmeGenerator;
+ private final SecureRandom csprng;
+ private final MasterkeyFileAccess masterkeyFileAccess;
private final BooleanProperty processing;
private final BooleanProperty readyToCreateVault;
private final ObjectBinding createVaultButtonState;
+ /* FXML */
public ToggleGroup recoveryKeyChoice;
public Toggle showRecoveryKey;
public Toggle skipRecoveryKey;
+ public NewPasswordController newPasswordSceneController;
@Inject
- CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty password, ReadmeGenerator readmeGenerator) {
+ CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.chooseLocationScene = chooseLocationScene;
this.recoveryKeyScene = recoveryKeyScene;
@@ -87,8 +97,9 @@ public class CreateNewVaultPasswordController implements FxController {
this.recoveryKeyProperty = recoveryKey;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
- this.password = password;
this.readmeGenerator = readmeGenerator;
+ this.csprng = csprng;
+ this.masterkeyFileAccess = masterkeyFileAccess;
this.processing = new SimpleBooleanProperty();
this.readyToCreateVault = new SimpleBooleanProperty();
this.createVaultButtonState = Bindings.createObjectBinding(this::getCreateVaultButtonState, processing);
@@ -96,8 +107,11 @@ public class CreateNewVaultPasswordController implements FxController {
@FXML
public void initialize() {
- BooleanBinding isValidNewPassword = Bindings.createBooleanBinding(() -> password.get() != null && password.get().length() > 0, password);
- readyToCreateVault.bind(isValidNewPassword.and(recoveryKeyChoice.selectedToggleProperty().isNotNull()).and(processing.not()));
+ readyToCreateVault.bind(newPasswordSceneController.goodPasswordProperty().and(recoveryKeyChoice.selectedToggleProperty().isNotNull()).and(processing.not()));
+ window.setOnHiding(event -> {
+ newPasswordSceneController.passwordField.wipe();
+ newPasswordSceneController.reenterField.wipe();
+ });
}
@FXML
@@ -130,8 +144,8 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get();
processing.set(true);
Tasks.create(() -> {
- initializeVault(pathToVault, password.get());
- return recoveryKeyFactory.createRecoveryKey(pathToVault, password.get());
+ initializeVault(pathToVault);
+ return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters());
}).onSuccess(recoveryKey -> {
initializationSucceeded(pathToVault);
recoveryKeyProperty.set(recoveryKey);
@@ -148,7 +162,7 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get();
processing.set(true);
Tasks.create(() -> {
- initializeVault(pathToVault, password.get());
+ initializeVault(pathToVault);
}).onSuccess(() -> {
initializationSucceeded(pathToVault);
window.setScene(successScene.get());
@@ -160,24 +174,35 @@ public class CreateNewVaultPasswordController implements FxController {
}).runOnce(executor);
}
- private void initializeVault(Path path, CharSequence passphrase) throws IOException {
- CryptoFileSystemProvider.initialize(path, MASTERKEY_FILENAME, passphrase);
- CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
- .withPassphrase(passphrase) //
- .withFlags(Collections.emptySet()) //
- .withMasterkeyFilename(MASTERKEY_FILENAME) //
- .build();
+ private void initializeVault(Path path) throws IOException {
+ // 1. write masterkey:
+ Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
+ try (Masterkey masterkey = Masterkey.generate(csprng)) {
+ masterkeyFileAccess.persist(masterkey, masterkeyFilePath, newPasswordSceneController.passwordField.getCharacters());
- String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName");
- try (FileSystem fs = CryptoFileSystemProvider.newFileSystem(path, fsProps); //
- WritableByteChannel ch = Files.newByteChannel(fs.getPath("/", vaultReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
- ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
+ // 2. initialize vault:
+ try {
+ MasterkeyLoader loader = ignored -> masterkey.copy();
+ CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC).withKeyLoader(loader).build();
+ CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID);
+
+ // 3. write vault-internal readme file:
+ String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName");
+ try (FileSystem fs = CryptoFileSystemProvider.newFileSystem(path, fsProps); //
+ WritableByteChannel ch = Files.newByteChannel(fs.getPath("/", vaultReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
+ ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
+ }
+ } catch (CryptoException e) {
+ throw new IOException("Failed initialize vault.", e);
+ }
}
+ // 4. write vault-external readme file:
String storagePathReadmeFileName = resourceBundle.getString("addvault.new.readme.storageLocation.fileName");
try (WritableByteChannel ch = Files.newByteChannel(path.resolve(storagePathReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
ch.write(US_ASCII.encode(readmeGenerator.createVaultStorageLocationReadmeRtf()));
}
+
LOG.info("Created vault at {}", path);
}
@@ -185,7 +210,7 @@ public class CreateNewVaultPasswordController implements FxController {
try {
Vault newVault = vaultListManager.add(pathToVault);
vaultProperty.set(newVault);
- } catch (NoSuchFileException e) {
+ } catch (IOException e) {
throw new UncheckedIOException(e);
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultRecoveryKeyController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultRecoveryKeyController.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultRecoveryKeyController.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultRecoveryKeyController.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java b/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
similarity index 99%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
index 6cec655cb..313a31dc9 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
+++ b/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
@@ -14,7 +14,7 @@ public class LocationPresets {
private static final String USER_HOME = System.getProperty("user.home");
private static final String[] ICLOUDDRIVE_LOCATIONS = {"~/Library/Mobile Documents/iCloud~com~setolabs~Cryptomator/Documents", "~/iCloudDrive/iCloud~com~setolabs~Cryptomator"};
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
- private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive"};
+ private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive/My Drive", "~/Google Drive"};
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java
rename to src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordComponent.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordComponent.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordComponent.java
rename to src/main/java/org/cryptomator/ui/changepassword/ChangePasswordComponent.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
similarity index 54%
rename from main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
rename to src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
index 52cfe2b81..54519f21f 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
+++ b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java
@@ -2,29 +2,33 @@ package org.cryptomator.ui.changepassword;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
+import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
-import javax.inject.Named;
-import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
-import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
import java.io.IOException;
-import java.nio.CharBuffer;
-import java.util.Optional;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.security.SecureRandom;
+import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@ChangePasswordScoped
@@ -34,29 +38,36 @@ public class ChangePasswordController implements FxController {
private final Stage window;
private final Vault vault;
- private final ObjectProperty newPassword;
private final ErrorComponent.Builder errorComponent;
private final KeychainManager keychain;
+ private final SecureRandom csprng;
+ private final MasterkeyFileAccess masterkeyFileAccess;
public NiceSecurePasswordField oldPasswordField;
public CheckBox finalConfirmationCheckbox;
public Button finishButton;
+ public NewPasswordController newPasswordController;
@Inject
- public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain) {
+ public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.vault = vault;
- this.newPassword = newPassword;
this.errorComponent = errorComponent;
this.keychain = keychain;
+ this.csprng = csprng;
+ this.masterkeyFileAccess = masterkeyFileAccess;
}
@FXML
public void initialize() {
BooleanBinding checkboxNotConfirmed = finalConfirmationCheckbox.selectedProperty().not();
BooleanBinding oldPasswordFieldEmpty = oldPasswordField.textProperty().isEmpty();
- BooleanBinding newPasswordInvalid = Bindings.createBooleanBinding(() -> newPassword.get() == null || newPassword.get().length() == 0, newPassword);
- finishButton.disableProperty().bind(checkboxNotConfirmed.or(oldPasswordFieldEmpty).or(newPasswordInvalid));
+ finishButton.disableProperty().bind(checkboxNotConfirmed.or(oldPasswordFieldEmpty).or(newPasswordController.goodPasswordProperty().not()));
+ window.setOnHiding(event -> {
+ oldPasswordField.wipe();
+ newPasswordController.passwordField.wipe();
+ newPasswordController.reenterField.wipe();
+ });
}
@FXML
@@ -67,24 +78,31 @@ public class ChangePasswordController implements FxController {
@FXML
public void finish() {
try {
- CryptoFileSystemProvider.changePassphrase(vault.getPath(), MASTERKEY_FILENAME, oldPasswordField.getCharacters(), newPassword.get());
+ CharSequence oldPassphrase = oldPasswordField.getCharacters();
+ CharSequence newPassphrase = newPasswordController.passwordField.getCharacters();
+ Path masterkeyPath = vault.getPath().resolve(MASTERKEY_FILENAME);
+ byte[] oldMasterkeyBytes = Files.readAllBytes(masterkeyPath);
+ byte[] newMasterkeyBytes = masterkeyFileAccess.changePassphrase(oldMasterkeyBytes, oldPassphrase, newPassphrase);
+ Path backupKeyPath = vault.getPath().resolve(MASTERKEY_FILENAME + MasterkeyBackupHelper.generateFileIdSuffix(oldMasterkeyBytes) + MASTERKEY_BACKUP_SUFFIX);
+ Files.move(masterkeyPath, backupKeyPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ Files.write(masterkeyPath, newMasterkeyBytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
LOG.info("Successfully changed password for {}", vault.getDisplayName());
- window.close();
updatePasswordInSystemkeychain();
- } catch (IOException e) {
- LOG.error("IO error occured during password change. Unable to perform operation.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ window.close();
} catch (InvalidPassphraseException e) {
Animations.createShakeWindowAnimation(window).play();
oldPasswordField.selectAll();
oldPasswordField.requestFocus();
+ } catch (IOException | CryptoException e) {
+ LOG.error("Password change failed. Unable to perform operation.", e);
+ errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
}
private void updatePasswordInSystemkeychain() {
- if (keychain.isSupported()) {
+ if (keychain.isSupported() && !keychain.isLocked()) {
try {
- keychain.changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get()));
+ keychain.changePassphrase(vault.getId(), newPasswordController.passwordField.getCharacters());
LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName());
} catch (KeychainAccessException e) {
LOG.error("Failed to update password in system keychain.", e);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java
similarity index 84%
rename from main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java
rename to src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java
index e80871208..d95b19410 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java
+++ b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordModule.java
@@ -5,10 +5,10 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
-import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
@@ -16,8 +16,6 @@ import org.cryptomator.ui.common.StageFactory;
import javax.inject.Named;
import javax.inject.Provider;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
@@ -27,13 +25,6 @@ import java.util.ResourceBundle;
@Module
abstract class ChangePasswordModule {
- @Provides
- @ChangePasswordScoped
- @Named("newPassword")
- static ObjectProperty provideNewPasswordProperty() {
- return new SimpleObjectProperty<>("");
- }
-
@Provides
@ChangePasswordWindow
@ChangePasswordScoped
@@ -71,8 +62,8 @@ abstract class ChangePasswordModule {
@Provides
@IntoMap
@FxControllerKey(NewPasswordController.class)
- static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty password) {
- return new NewPasswordController(resourceBundle, strengthRater, password);
+ static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
+ return new NewPasswordController(resourceBundle, strengthRater);
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordScoped.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordScoped.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordScoped.java
rename to src/main/java/org/cryptomator/ui/changepassword/ChangePasswordScoped.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordWindow.java b/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordWindow.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordWindow.java
rename to src/main/java/org/cryptomator/ui/changepassword/ChangePasswordWindow.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/Animations.java b/src/main/java/org/cryptomator/ui/common/Animations.java
similarity index 61%
rename from main/ui/src/main/java/org/cryptomator/ui/common/Animations.java
rename to src/main/java/org/cryptomator/ui/common/Animations.java
index d71309933..3c703a50d 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/Animations.java
+++ b/src/main/java/org/cryptomator/ui/common/Animations.java
@@ -1,11 +1,17 @@
package org.cryptomator.ui.common;
+import javafx.animation.Animation;
+import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
+import javafx.animation.RotateTransition;
+import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.beans.value.WritableValue;
+import javafx.scene.Node;
import javafx.stage.Window;
import javafx.util.Duration;
+import java.util.stream.IntStream;
public class Animations {
@@ -33,4 +39,19 @@ public class Animations {
);
}
+ public static SequentialTransition createDiscrete360Rotation(Node toAnimate) {
+ var animation = new SequentialTransition(IntStream.range(0, 8).mapToObj(i -> Animations.createDiscrete45Rotation()).toArray(Animation[]::new));
+ animation.setCycleCount(Animation.INDEFINITE);
+ animation.setNode(toAnimate);
+ return animation;
+ }
+
+ private static RotateTransition createDiscrete45Rotation() {
+ var animation = new RotateTransition(Duration.millis(100));
+ animation.setInterpolator(Interpolator.DISCRETE);
+ animation.setByAngle(45);
+ animation.setCycleCount(1);
+ return animation;
+ }
+
}
diff --git a/src/main/java/org/cryptomator/ui/common/AutoAnimator.java b/src/main/java/org/cryptomator/ui/common/AutoAnimator.java
new file mode 100644
index 000000000..b5398339c
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/common/AutoAnimator.java
@@ -0,0 +1,84 @@
+package org.cryptomator.ui.common;
+
+import com.tobiasdiez.easybind.EasyBind;
+import com.tobiasdiez.easybind.Subscription;
+
+import javafx.animation.Animation;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ObservableValue;
+
+/**
+ * Animation which starts and stops automatically based on an observable condition.
+ *
+ * During creation the consumer can optionally define actions to be executed everytime before the animation starts and after it stops.
+ */
+public class AutoAnimator {
+
+ private final T animation;
+ private final ObservableValue condition;
+ private final Runnable beforeStart;
+ private final Runnable afterStop;
+ private final Subscription sub;
+
+ AutoAnimator(T animation, ObservableValue condition, Runnable beforeStart, Runnable afterStop) {
+ this.animation = animation;
+ this.condition = condition;
+ this.beforeStart = beforeStart;
+ this.afterStop = afterStop;
+ this.sub = EasyBind.subscribe(condition, this::togglePlay);
+ }
+
+ public void playFromStart() {
+ beforeStart.run();
+ animation.playFromStart();
+ }
+
+ public void stop() {
+ animation.stop();
+ afterStop.run();
+ }
+
+ private void togglePlay(boolean play) {
+ if (play) {
+ this.playFromStart();
+ } else {
+ this.stop();
+ }
+ }
+
+ public static Builder animate(Animation animation) {
+ return new Builder(animation);
+ }
+
+ public static class Builder {
+
+ private Animation animation;
+ private ObservableValue condition = new SimpleBooleanProperty(true);
+ private Runnable beforeStart = () -> {};
+ private Runnable afterStop = () -> {};
+
+ private Builder(Animation animation) {
+ this.animation = animation;
+ }
+
+ public Builder onCondition(ObservableValue condition) {
+ this.condition = condition;
+ return this;
+ }
+
+ public Builder beforeStart(Runnable beforeStart) {
+ this.beforeStart = beforeStart;
+ return this;
+ }
+
+ public Builder afterStop(Runnable afterStop) {
+ this.afterStop = afterStop;
+ return this;
+ }
+
+ public AutoAnimator build() {
+ return new AutoAnimator(animation, condition, beforeStart, afterStop);
+ }
+
+ }
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java b/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java
rename to src/main/java/org/cryptomator/ui/common/DefaultSceneFactory.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorComponent.java b/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
similarity index 95%
rename from main/ui/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
rename to src/main/java/org/cryptomator/ui/common/ErrorComponent.java
index 285270b4c..92276f5bd 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
+++ b/src/main/java/org/cryptomator/ui/common/ErrorComponent.java
@@ -2,8 +2,8 @@ package org.cryptomator.ui.common;
import dagger.BindsInstance;
import dagger.Subcomponent;
+import org.cryptomator.common.Nullable;
-import javax.annotation.Nullable;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
diff --git a/src/main/java/org/cryptomator/ui/common/ErrorController.java b/src/main/java/org/cryptomator/ui/common/ErrorController.java
new file mode 100644
index 000000000..c75df26ce
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/common/ErrorController.java
@@ -0,0 +1,111 @@
+package org.cryptomator.ui.common;
+
+import org.cryptomator.common.ErrorCode;
+import org.cryptomator.common.Nullable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.fxml.FXML;
+import javafx.scene.Scene;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
+import javafx.stage.Stage;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+public class ErrorController implements FxController {
+
+ private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
+ private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
+ private static final String SEARCH_ERRORCODE_DELIM = " OR ";
+ private static final String REPORT_BODY_TEMPLATE = """
+
+
+ """;
+
+ private final Application application;
+ private final String stackTrace;
+ private final ErrorCode errorCode;
+ private final Scene previousScene;
+ private final Stage window;
+
+ private BooleanProperty copiedDetails = new SimpleBooleanProperty();
+
+ @Inject
+ ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window) {
+ this.application = application;
+ this.stackTrace = stackTrace;
+ this.errorCode = errorCode;
+ this.previousScene = previousScene;
+ this.window = window;
+ }
+
+ @FXML
+ public void back() {
+ if (previousScene != null) {
+ window.setScene(previousScene);
+ }
+ }
+
+ @FXML
+ public void close() {
+ window.close();
+ }
+
+ @FXML
+ public void searchError() {
+ var searchTerm = URLEncoder.encode(getErrorCode().replace(ErrorCode.DELIM, SEARCH_ERRORCODE_DELIM), StandardCharsets.UTF_8);
+ application.getHostServices().showDocument(SEARCH_URL_FORMAT.formatted(searchTerm));
+ }
+
+ @FXML
+ public void reportError() {
+ var title = URLEncoder.encode(getErrorCode(), StandardCharsets.UTF_8);
+ var body = URLEncoder.encode(REPORT_BODY_TEMPLATE, StandardCharsets.UTF_8);
+ application.getHostServices().showDocument(REPORT_URL_FORMAT.formatted(title, body));
+ }
+
+ @FXML
+ public void copyDetails() {
+ ClipboardContent clipboardContent = new ClipboardContent();
+ clipboardContent.putString(getDetailText());
+ Clipboard.getSystemClipboard().setContent(clipboardContent);
+
+ copiedDetails.set(true);
+ CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
+ copiedDetails.set(false);
+ });
+ }
+
+ /* Getter/Setter */
+
+ public boolean isPreviousScenePresent() {
+ return previousScene != null;
+ }
+
+ public String getStackTrace() {
+ return stackTrace;
+ }
+
+ public String getErrorCode() {
+ return errorCode.toString();
+ }
+
+ public String getDetailText() {
+ return "```\nError Code " + getErrorCode() + "\n" + getStackTrace() + "\n```";
+ }
+
+ public BooleanProperty copiedDetailsProperty() {
+ return copiedDetails;
+ }
+
+ public boolean getCopiedDetails() {
+ return copiedDetails.get();
+ }
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java b/src/main/java/org/cryptomator/ui/common/ErrorModule.java
similarity index 90%
rename from main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java
rename to src/main/java/org/cryptomator/ui/common/ErrorModule.java
index d2515e661..01b8790c1 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/ErrorModule.java
+++ b/src/main/java/org/cryptomator/ui/common/ErrorModule.java
@@ -4,6 +4,7 @@ import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
+import org.cryptomator.common.ErrorCode;
import javax.inject.Named;
import javax.inject.Provider;
@@ -31,6 +32,11 @@ abstract class ErrorModule {
return baos.toString(StandardCharsets.UTF_8);
}
+ @Provides
+ static ErrorCode provideErrorCode(Throwable cause) {
+ return ErrorCode.of(cause);
+ }
+
@Binds
@IntoMap
@FxControllerKey(ErrorController.class)
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FontLoader.java b/src/main/java/org/cryptomator/ui/common/FontLoader.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FontLoader.java
rename to src/main/java/org/cryptomator/ui/common/FontLoader.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxController.java b/src/main/java/org/cryptomator/ui/common/FxController.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FxController.java
rename to src/main/java/org/cryptomator/ui/common/FxController.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxControllerKey.java b/src/main/java/org/cryptomator/ui/common/FxControllerKey.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FxControllerKey.java
rename to src/main/java/org/cryptomator/ui/common/FxControllerKey.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java
similarity index 88%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java
rename to src/main/java/org/cryptomator/ui/common/FxmlFile.java
index 4d429ab7d..b8d5bbff0 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java
+++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java
@@ -11,6 +11,8 @@ public enum FxmlFile {
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
ERROR("/fxml/error.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
+ HEALTH_START("/fxml/health_start.fxml"), //
+ HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //
MAIN_WINDOW("/fxml/main_window.fxml"), //
@@ -26,8 +28,9 @@ public enum FxmlFile {
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
- UNLOCK("/fxml/unlock.fxml"),
+ UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
+ UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
VAULT_OPTIONS("/fxml/vault_options.fxml"), //
VAULT_STATISTICS("/fxml/stats.fxml"), //
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java b/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
similarity index 79%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
rename to src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
index cf8940cc2..c10054ef4 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
+++ b/src/main/java/org/cryptomator/ui/common/FxmlLoaderFactory.java
@@ -5,7 +5,6 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import java.io.IOException;
-import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Map;
import java.util.ResourceBundle;
@@ -26,11 +25,9 @@ public class FxmlLoaderFactory {
/**
* @return A new FXMLLoader instance
*/
- public FXMLLoader construct() {
- FXMLLoader loader = new FXMLLoader();
- loader.setControllerFactory(this::constructController);
- loader.setResources(resourceBundle);
- return loader;
+ private FXMLLoader construct(String fxmlResourceName) {
+ var url = getClass().getResource(fxmlResourceName);
+ return new FXMLLoader(url, resourceBundle, null, this::constructController);
}
/**
@@ -41,10 +38,8 @@ public class FxmlLoaderFactory {
* @throws IOException if an error occurs while loading the FXML file
*/
public FXMLLoader load(String fxmlResourceName) throws IOException {
- FXMLLoader loader = construct();
- try (InputStream in = getClass().getResourceAsStream(fxmlResourceName)) {
- loader.load(in);
- }
+ FXMLLoader loader = construct(fxmlResourceName);
+ loader.load();
return loader;
}
@@ -67,8 +62,8 @@ public class FxmlLoaderFactory {
}
Parent root = loader.getRoot();
// TODO: discuss if we can remove language-specific stylesheets
- // List addtionalStyleSheets = Splitter.on(',').omitEmptyStrings().splitToList(resourceBundle.getString("additionalStyleSheets"));
- // addtionalStyleSheets.forEach(styleSheet -> root.getStylesheets().add("/css/" + styleSheet));
+ // List additionalStyleSheets = Splitter.on(',').omitEmptyStrings().splitToList(resourceBundle.getString("additionalStyleSheets"));
+ // additionalStyleSheets.forEach(styleSheet -> root.getStylesheets().add("/css/" + styleSheet));
return sceneFactory.apply(root);
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlScene.java b/src/main/java/org/cryptomator/ui/common/FxmlScene.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/FxmlScene.java
rename to src/main/java/org/cryptomator/ui/common/FxmlScene.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/HostServiceRevealer.java b/src/main/java/org/cryptomator/ui/common/HostServiceRevealer.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/HostServiceRevealer.java
rename to src/main/java/org/cryptomator/ui/common/HostServiceRevealer.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java b/src/main/java/org/cryptomator/ui/common/NewPasswordController.java
similarity index 77%
rename from main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java
rename to src/main/java/org/cryptomator/ui/common/NewPasswordController.java
index b7bf48870..caa0962f8 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/NewPasswordController.java
+++ b/src/main/java/org/cryptomator/ui/common/NewPasswordController.java
@@ -4,11 +4,12 @@ import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
-import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
+import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
@@ -18,8 +19,8 @@ public class NewPasswordController implements FxController {
private final ResourceBundle resourceBundle;
private final PasswordStrengthUtil strengthRater;
- private final ObjectProperty password;
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(-1);
+ private final BooleanProperty goodPassword = new SimpleBooleanProperty();
public NiceSecurePasswordField passwordField;
public NiceSecurePasswordField reenterField;
@@ -31,10 +32,9 @@ public class NewPasswordController implements FxController {
public FontAwesome5IconView passwordMatchCheckmark;
public FontAwesome5IconView passwordMatchCross;
- public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ObjectProperty password) {
+ public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
this.resourceBundle = resourceBundle;
this.strengthRater = strengthRater;
- this.password = password;
}
@FXML
@@ -44,14 +44,14 @@ public class NewPasswordController implements FxController {
passwordStrengthLabel.graphicProperty().bind(Bindings.createObjectBinding(this::getIconViewForPasswordStrengthLabel, passwordField.textProperty(), passwordStrength));
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
- BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::hasSamePasswordInBothFields, passwordField.textProperty(), reenterField.textProperty());
+ BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::passwordFieldsMatch, passwordField.textProperty(), reenterField.textProperty());
BooleanBinding reenterFieldNotEmpty = reenterField.textProperty().isNotEmpty();
passwordMatchLabel.visibleProperty().bind(reenterFieldNotEmpty);
passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(passwordMatchCheckmark).otherwise(passwordMatchCross));
passwordMatchLabel.textProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(resourceBundle.getString("newPassword.passwordsMatch")).otherwise(resourceBundle.getString("newPassword.passwordsDoNotMatch")));
- passwordField.textProperty().addListener(this::passwordsDidChange);
- reenterField.textProperty().addListener(this::passwordsDidChange);
+ BooleanBinding sufficientStrength = Bindings.createBooleanBinding(this::sufficientStrength, passwordField.textProperty());
+ goodPassword.bind(passwordsMatch.and(sufficientStrength));
}
private FontAwesome5IconView getIconViewForPasswordStrengthLabel() {
@@ -66,20 +66,24 @@ public class NewPasswordController implements FxController {
}
}
- private void passwordsDidChange(@SuppressWarnings("unused") Observable observable) {
- if (hasSamePasswordInBothFields() && strengthRater.fulfillsMinimumRequirements(passwordField.getCharacters())) {
- password.set(passwordField.getCharacters());
- } else {
- password.set("");
- }
- }
-
- private boolean hasSamePasswordInBothFields() {
+ private boolean passwordFieldsMatch() {
return CharSequence.compare(passwordField.getCharacters(), reenterField.getCharacters()) == 0;
}
+ private boolean sufficientStrength() {
+ return strengthRater.fulfillsMinimumRequirements(passwordField.getCharacters());
+ }
+
/* Getter/Setter */
+ public ReadOnlyBooleanProperty goodPasswordProperty() {
+ return goodPassword;
+ }
+
+ public boolean isGoodPassword() {
+ return goodPassword.get();
+ }
+
public IntegerProperty passwordStrengthProperty() {
return passwordStrength;
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java b/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java
rename to src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/StageFactory.java b/src/main/java/org/cryptomator/ui/common/StageFactory.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/StageFactory.java
rename to src/main/java/org/cryptomator/ui/common/StageFactory.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/Tasks.java b/src/main/java/org/cryptomator/ui/common/Tasks.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/Tasks.java
rename to src/main/java/org/cryptomator/ui/common/Tasks.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/UserInteractionLock.java b/src/main/java/org/cryptomator/ui/common/UserInteractionLock.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/UserInteractionLock.java
rename to src/main/java/org/cryptomator/ui/common/UserInteractionLock.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/src/main/java/org/cryptomator/ui/common/VaultService.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java
rename to src/main/java/org/cryptomator/ui/common/VaultService.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java b/src/main/java/org/cryptomator/ui/common/WeakBindings.java
similarity index 96%
rename from main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java
rename to src/main/java/org/cryptomator/ui/common/WeakBindings.java
index e6071df1d..6efa747c9 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java
+++ b/src/main/java/org/cryptomator/ui/common/WeakBindings.java
@@ -77,7 +77,7 @@ public final class WeakBindings {
* @param observable The observable
* @return a IntegerBinding weakly referenced from the given observable
*/
- public static IntegerBinding bindInterger(ObservableValue observable) {
+ public static IntegerBinding bindInteger(ObservableValue observable) {
return new IntegerBinding() {
{
bind(observable);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/AlphanumericTextField.java b/src/main/java/org/cryptomator/ui/controls/AlphanumericTextField.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/AlphanumericTextField.java
rename to src/main/java/org/cryptomator/ui/controls/AlphanumericTextField.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DataLabel.java b/src/main/java/org/cryptomator/ui/controls/DataLabel.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/DataLabel.java
rename to src/main/java/org/cryptomator/ui/controls/DataLabel.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java b/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java
similarity index 100%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java
rename to src/main/java/org/cryptomator/ui/controls/DraggableListCell.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
similarity index 87%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
rename to src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
index f4d0d058a..66eda7556 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
+++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java
@@ -6,8 +6,13 @@ package org.cryptomator.ui.controls;
public enum FontAwesome5Icon {
ANCHOR("\uF13D"), //
ARROW_UP("\uF062"), //
+ BAN("\uF05E"), //
BUG("\uF188"), //
+ CARET_DOWN("\uF0D7"), //
+ CARET_RIGHT("\uF0Da"), //
CHECK("\uF00C"), //
+ CLOCK("\uF017"), //
+ CLIPBOARD("\uF328"), //
COG("\uF013"), //
COGS("\uF085"), //
COPY("\uF0C5"), //
@@ -18,6 +23,7 @@ public enum FontAwesome5Icon {
EXCLAMATION_TRIANGLE("\uF071"), //
EYE("\uF06E"), //
EYE_SLASH("\uF070"), //
+ FAST_FORWARD("\uF050"), //
FILE("\uF15B"), //
FILE_IMPORT("\uF56F"), //
FOLDER_OPEN("\uF07C"), //
@@ -37,6 +43,7 @@ public enum FontAwesome5Icon {
REDO("\uF01E"), //
SEARCH("\uF002"), //
SPINNER("\uF110"), //
+ STETHOSCOPE("\uF0f1"), //
SYNC("\uF021"), //
TIMES("\uF00D"), //
TRASH("\uF1F8"), //
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
similarity index 88%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
rename to src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
index 4d0797eaa..4c89ca674 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
+++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5IconView.java
@@ -21,8 +21,8 @@ public class FontAwesome5IconView extends Text {
private static final String FONT_PATH = "/css/fontawesome5-free-solid.otf";
private static final Font FONT;
- private ObjectProperty glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
- private DoubleProperty glyphSize = new SimpleDoubleProperty(this, "glyphSize", DEFAULT_GLYPH_SIZE);
+ protected final ObjectProperty glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
+ protected final DoubleProperty glyphSize = new SimpleDoubleProperty(this, "glyphSize", DEFAULT_GLYPH_SIZE);
static {
try {
@@ -42,7 +42,7 @@ public class FontAwesome5IconView extends Text {
}
private void glyphChanged(@SuppressWarnings("unused") ObservableValue extends FontAwesome5Icon> observable, @SuppressWarnings("unused") FontAwesome5Icon oldValue, FontAwesome5Icon newValue) {
- setText(newValue.unicode());
+ setText(newValue == null ? null : newValue.unicode());
}
private void glyphSizeChanged(@SuppressWarnings("unused") ObservableValue extends Number> observable, @SuppressWarnings("unused") Number oldValue, Number newValue) {
diff --git a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Spinner.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Spinner.java
new file mode 100644
index 000000000..eb28a90a9
--- /dev/null
+++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Spinner.java
@@ -0,0 +1,44 @@
+package org.cryptomator.ui.controls;
+
+import org.cryptomator.ui.common.Animations;
+import org.cryptomator.ui.common.AutoAnimator;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+
+/**
+ * An animated progress spinner using the {@link FontAwesome5IconView} with the spinner glyph.
+ *
+ * Using the default constructor, the animation is always played if the icon is visible. To animate on other conditions, use the constructor with the "spinning" property.
+ */
+public class FontAwesome5Spinner extends FontAwesome5IconView {
+
+ protected final BooleanProperty spinning = new SimpleBooleanProperty(this, "spinning", true);
+
+ private AutoAnimator animator;
+
+ public FontAwesome5Spinner() {
+ setGlyph(FontAwesome5Icon.SPINNER);
+ var animation = Animations.createDiscrete360Rotation(this);
+ this.animator = AutoAnimator.animate(animation) //
+ .afterStop(() -> setRotate(0)) //
+ .onCondition(spinning.and(visibleProperty())) //
+ .build();
+ }
+
+ /* Getter/Setter */
+
+ public BooleanProperty spinningProperty() {
+ return spinning;
+ }
+
+ public boolean isSpinning() {
+ return spinning.get();
+ }
+
+ public void setSpinning(boolean spinning) {
+ this.spinning.set(spinning);
+ }
+
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel.java b/src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
similarity index 78%
rename from main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
rename to src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
index c99cc62ea..04ed7e477 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
+++ b/src/main/java/org/cryptomator/ui/controls/FormattedLabel.java
@@ -12,6 +12,7 @@ public class FormattedLabel extends Label {
private final StringProperty format = new SimpleStringProperty("");
private final ObjectProperty