Merge branch 'develop' into feature/hub

# Conflicts:
#	.idea/runConfigurations/Cryptomator_Linux.xml
#	.idea/runConfigurations/Cryptomator_Linux_Dev.xml
#	.idea/runConfigurations/Cryptomator_Windows.xml
#	.idea/runConfigurations/Cryptomator_Windows_Dev.xml
#	.idea/runConfigurations/Cryptomator_macOS.xml
#	.idea/runConfigurations/Cryptomator_macOS_Dev.xml
#	pom.xml
This commit is contained in:
Sebastian Stenzel
2021-11-02 15:53:23 +01:00
151 changed files with 3380 additions and 1357 deletions

View File

@@ -2,9 +2,11 @@ name: Build
on:
push:
pull_request_target:
types: [labeled]
env:
JAVA_VERSION: 16
JAVA_VERSION: 17
defaults:
run:
@@ -17,19 +19,16 @@ jobs:
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
- uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
- uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
cache: 'maven'
- name: Build and Test
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check
- name: Upload code coverage report
id: codacyCoverageReporter
if: "github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'pr:safe')"
run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}

26
.github/workflows/pullrequest.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Pull Request
on:
pull_request:
env:
JAVA_VERSION: 17
defaults:
run:
shell: bash
jobs:
test:
name: Compile and Test
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check

View File

@@ -2,13 +2,18 @@ name: Installers and Release
on:
workflow_dispatch:
inputs:
semver:
description: 'SemVer'
required: true
default: '0.99.99-SNAPSHOT'
push:
tags: # see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-*'
env:
JAVA_VERSION: 16
JAVA_VERSION: 17
defaults:
run:
@@ -61,7 +66,7 @@ jobs:
target/libs
target/mods
target/LICENSE.txt
target/${{ matrix.launcher }}
target/launcher*
if-no-files-found: error
#
@@ -71,8 +76,9 @@ jobs:
name: Determine Version Metadata
runs-on: ubuntu-latest
outputs:
versionStr: ${{ steps.versions.outputs.versionStr }}
versionNum: ${{ steps.versions.outputs.versionNum }}
semVerNum: ${{ steps.versions.outputs.semVerNum }}
semVerStr: ${{ steps.versions.outputs.semVerStr }}
ppaVerStr: ${{ steps.versions.outputs.ppaVerStr }}
revNum: ${{ steps.versions.outputs.revNum }}
steps:
- uses: actions/checkout@v2
@@ -81,14 +87,19 @@ jobs:
- id: versions
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION_NUM=`echo ${GITHUB_REF##*/} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
echo "::set-output name=versionStr::${GITHUB_REF##*/}"
echo "::set-output name=versionNum::${VERSION_NUM}"
SEM_VER_STR=${GITHUB_REF##*/}
else
echo "::set-output name=versionStr::SNAPSHOT"
echo "::set-output name=versionNum::99.0.0"
SEM_VER_STR=${{ github.event.inputs.semver }}
fi
echo "::set-output name=revNum::`git rev-list --count HEAD`"
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "::set-output name=semVerStr::${SEM_VER_STR}"
echo "::set-output name=semVerNum::${SEM_VER_NUM}"
echo "::set-output name=ppaVerStr::${SEM_VER_STR/-/\~}-${REVCOUNT}"
echo "::set-output name=revNum::${REVCOUNT}"
- uses: skymatic/semver-validation-action@v1
with:
version: ${{ steps.versions.outputs.semVerStr }}
#
# Application Directory
@@ -104,9 +115,10 @@ jobs:
- os: ubuntu-latest
profile: linux
jpackageoptions: >
--app-version "${{ needs.metadata.outputs.versionNum }}.${{ needs.metadata.outputs.revNum }}"
--app-version "${{ needs.metadata.outputs.semVerNum }}.${{ needs.metadata.outputs.revNum }}"
--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\""
@@ -116,9 +128,10 @@ jobs:
- os: windows-latest
profile: win
jpackageoptions: >
--app-version "${{ needs.metadata.outputs.versionNum }}.${{ needs.metadata.outputs.revNum }}"
--app-version "${{ needs.metadata.outputs.semVerNum }}.${{ needs.metadata.outputs.revNum }}"
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.keychainPath=\"~/AppData/Roaming/Cryptomator/keychain.json\""
@@ -130,9 +143,11 @@ jobs:
- os: macos-latest
profile: mac
jpackageoptions: >
--app-version "${{ needs.metadata.outputs.versionNum }}"
--app-version "${{ needs.metadata.outputs.semVerNum }}"
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dapple.awt.enableTemplateImages=true"
--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"
@@ -175,6 +190,7 @@ jobs:
--copyright "(C) 2016 - 2021 Skymatic GmbH"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.metadata.outputs.semVerStr }}\""
${{ matrix.jpackageoptions }}
- name: Create appdir.tar
run: tar -cvf appdir.tar appdir
@@ -185,6 +201,69 @@ jobs:
path: appdir.tar
if-no-files-found: error
#
# Linux PPA Source Package
#
ppa:
name: Upload source package to PPA
needs: [buildkit, metadata]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install build tools
run: |
sudo apt-get update
sudo apt-get install debhelper devscripts dput
- name: Download linux-buildkit
uses: actions/download-artifact@v2
with:
name: linux-buildkit
path: pkgdir
- name: create orig.tar.gz
run: tar -cJf cryptomator_${{ needs.metadata.outputs.ppaVerStr }}.orig.tar.xz -C pkgdir .
- name: patch and rename pkgdir
run: |
cp -r dist/linux/debian/ pkgdir
cp -r dist/linux/resources/ pkgdir
export RFC2822_TIMESTAMP=`date --rfc-2822`
envsubst '${VERSION_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${VERSION_STR}' < dist/linux/debian/org.cryptomator.Cryptomator.desktop > pkgdir/debian/org.cryptomator.Cryptomator.desktop
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
mv pkgdir cryptomator_${{ needs.metadata.outputs.ppaVerStr }}
env:
VERSION_STR: ${{ needs.metadata.outputs.semVerStr }}
VERSION_NUM: ${{ needs.metadata.outputs.semVerNum }}
REVISION_NUM: ${{ needs.metadata.outputs.revNum }}
PPA_VERSION: ${{ needs.metadata.outputs.ppaVerStr }}-0ppa1
- name: import gpg key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --dry-run --sign dist/linux/debian/rules
env:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: debuild
run: debuild -S -sa -d
env:
DEBSIGN_PROGRAM: gpg --batch --pinentry-mode loopback
DEBSIGN_KEYID: 615D449FE6E6A235
working-directory: cryptomator_${{ needs.metadata.outputs.ppaVerStr }}
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: linux-deb-source-package
path: |
cryptomator_*.dsc
cryptomator_*.orig.tar.xz
cryptomator_*.debian.tar.xz
cryptomator_*_source.changes
cryptomator_*_source.buildinfo
- name: dput to beta repo
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_${PPA_VERSION}_source.changes
env:
PPA_VERSION: ${{ needs.metadata.outputs.ppaVerStr }}-0ppa1
#
# Linux Cryptomator.AppImage
#
@@ -205,7 +284,7 @@ jobs:
run: |
mv appdir/Cryptomator Cryptomator.AppDir
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
envsubst '${REVISION_NO}' < dist/linux/appimage/resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
envsubst '${REVISION_NO} ${SEMVER_STR}' < dist/linux/appimage/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
@@ -213,6 +292,7 @@ jobs:
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
env:
REVISION_NO: ${{ needs.metadata.outputs.revNum }}
SEMVER_STR: ${{ needs.metadata.outputs.semVerStr }}
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
run: |
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
@@ -233,7 +313,7 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.metadata.outputs.versionStr }}-x86_64.AppImage
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.metadata.outputs.semVerStr }}-x86_64.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Upload AppImage
@@ -263,10 +343,11 @@ jobs:
- name: Patch Cryptomator.app
run: |
mv appdir/Cryptomator.app Cryptomator.app
mv dist/mac/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
env:
VERSION_NO: ${{ needs.metadata.outputs.versionNum }}
VERSION_NO: ${{ needs.metadata.outputs.semVerNum }}
REVISION_NO: ${{ needs.metadata.outputs.revNum }}
- name: Install codesign certificate
env:
@@ -300,8 +381,8 @@ jobs:
OUTPUT_PATH=${JAR_PATH%.*}
echo "Codesigning libs in ${JAR_FILENAME}..."
unzip -q ${JAR_PATH} -d ${OUTPUT_PATH}
find ${OUTPUT_PATH} -name '*.dylib' -exec codesign -s ${CODESIGN_IDENTITY} {} \;
find ${OUTPUT_PATH} -name '*.jnilib' -exec codesign -s ${CODESIGN_IDENTITY} {} \;
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} *
@@ -368,7 +449,7 @@ jobs:
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}.dmg dmg
env:
VERSION_NO: ${{ needs.metadata.outputs.versionNum }}
VERSION_NO: ${{ needs.metadata.outputs.semVerNum }}
- name: Install notarization credentials
env:
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
@@ -397,6 +478,8 @@ jobs:
- name: Clean up notarization credentials
if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/notarization.keychain-db
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.metadata.outputs.semVerStr }}.dmg
- name: Upload mac-dmg
uses: actions/upload-artifact@v2
with:
@@ -449,12 +532,14 @@ jobs:
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2021 Skymatic GmbH"
--app-version "${{ needs.metadata.outputs.versionNum }}"
--app-version "${{ needs.metadata.outputs.semVerNum }}"
--win-menu
--win-dir-chooser
--win-shortcut-prompt
--win-update-url "https:\\cryptomator.org"
--win-menu-group Cryptomator
--resource-dir dist/win/resources
--license-file dist/win/resources/license.rtf
--file-associations dist/win/resources/FAencryptedData.properties
--file-associations dist/win/resources/FAvaultFile.properties
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
@@ -467,6 +552,8 @@ jobs:
description: Cryptomator Installer
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-*.msi installer/Cryptomator-${{ needs.metadata.outputs.semVerStr }}-x64.msi
- name: Upload win-msi
uses: actions/upload-artifact@v2
with:
@@ -480,12 +567,12 @@ jobs:
release:
name: Draft a release on Github
runs-on: ubuntu-latest
needs: [metadata,linux-appimage,mac-dmg,win-msi]
needs: [metadata,linux-appimage,mac-dmg,win-msi,ppa]
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'cryptomator/cryptomator'
steps:
- uses: actions/checkout@v2
- name: Create tarball
run: git archive --prefix="cryptomator-${{ needs.metadata.outputs.versionStr }}/" -o "cryptomator-${{ needs.metadata.outputs.versionStr }}.tar.gz" ${{ github.ref }}
run: git archive --prefix="cryptomator-${{ needs.metadata.outputs.semVerStr }}/" -o "cryptomator-${{ needs.metadata.outputs.semVerStr }}.tar.gz" ${{ github.ref }}
- name: Download linux appimage
uses: actions/download-artifact@v2
with:
@@ -507,6 +594,13 @@ jobs:
env:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Compute SHA256 checksums of release artifacts
run: |
SHA256_SUMS=`find . -name "*.AppImage" -o -name "*.dmg" -o -name "*.msi" -o -name "*.tar.gz" | xargs sha256sum`
echo "SHA256_SUMS<<EOF" >> $GITHUB_ENV
echo "${SHA256_SUMS}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
continue-on-error: true
- name: Create release draft
uses: softprops/action-gh-release@v1
with:
@@ -527,3 +621,8 @@ jobs:
## Misc
---
:scroll: A complete list of closed issues is available [here](LINK)
---
:floppy_disk: SHA-256 checksums of release artifacts:
```
${{ env.SHA256_SUMS }}
```

1
.idea/icon.svg generated Normal file
View File

@@ -0,0 +1 @@
<svg width="1110" height="942" viewBox="0 0 1108.12 940.2" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg"><path d="m552.69 0c-169.1 0-262.45 143.46-262.45 283.52h524.9c0-140.06-105.33-283.52-262.45-283.52z" fill="#cfcfcf"/><path d="m552.69 53.2c-137.37 0-213.21 116.54-213.21 230.32l213.21 24.18 213.21-24.18c0-113.78-85.57-230.32-213.21-230.32z" fill="#585e62"/><path d="m89.8 739.52a20 20 0 0 1 -2.23-39.88 42.8 42.8 0 1 0 -42.22-21.82 20 20 0 0 1 -35 19.31 82.79 82.79 0 1 1 81.76 42.26 21.78 21.78 0 0 1 -2.31.13z" fill="#585e62"/><rect fill="#cfcfcf" height="144.19" rx="49.42" transform="matrix(.8902923 .45538953 -.45538953 .8902923 261.57 -5.69)" width="104.68" x="90.27" y="467.98"/><path d="m149.47 401.27h62.8a0 0 0 0 1 0 0v94.55a31.4 31.4 0 0 1 -31.4 31.4 31.4 31.4 0 0 1 -31.4-31.4v-94.55a0 0 0 0 1 0 0z" fill="#585e62" transform="matrix(.8902923 .45538953 -.45538953 .8902923 231.23 -31.44)"/><circle cx="222.59" cy="387.53" fill="#cfcfcf" r="75.05"/><path d="m258.09 321.8a75.09 75.09 0 0 0 -91.23 14.64 75.06 75.06 0 0 1 51.58 126.06 75.06 75.06 0 0 0 39.65-140.7z" fill="#b1b1b1"/><path d="m1018.31 739.52a22.09 22.09 0 0 1 -2.28-.13 82.8 82.8 0 1 1 81.77-42.26 20 20 0 1 1 -35-19.31 42.8 42.8 0 1 0 -42.23 21.82 20 20 0 0 1 -2.23 39.88z" fill="#585e62"/><rect fill="#cfcfcf" height="144.19" rx="49.42" transform="matrix(-.8902923 .45538953 -.45538953 -.8902923 2071.05 581.27)" width="104.68" x="913.18" y="467.98"/><path d="m927.25 401.27a31.4 31.4 0 0 1 31.4 31.4v94.55a0 0 0 0 1 0 0h-62.8a0 0 0 0 1 0 0v-94.55a31.4 31.4 0 0 1 31.4-31.4z" fill="#585e62" transform="matrix(-.8902923 .45538953 -.45538953 -.8902923 1964.18 455.35)"/><circle cx="885.53" cy="387.53" fill="#cfcfcf" r="75.05"/><path d="m850 321.8a75.08 75.08 0 0 1 91.22 14.64 75.06 75.06 0 0 0 -51.54 126.06 75.05 75.05 0 0 1 -39.68-140.7z" fill="#b1b1b1"/><path d="m248.78 940.2c-36.67 0-67-39.85-75.51-99.15-4.56-31.83-2.26-65.19 6.48-94 9.41-31 25.51-53.59 45.32-63.72a51.72 51.72 0 0 1 23.69-5.84h103.52v262.71z" fill="#585e62"/><path d="m351.43 940.2c-21.26 0-38.85-39.85-43.78-99.15-2.64-31.83-1.31-65.19 3.76-94 5.45-31 14.78-53.59 26.27-63.72 4.39-3.87 9-5.84 13.73-5.84s9.34 2 13.75 5.8l49.7 43.83c17.37 15.32 23.39 64 20.23 102-2.43 29.28-10 52.18-20.19 61.28l-49.69 43.82c-4.44 3.99-9.08 5.98-13.78 5.98z" fill="#585e62"/><path d="m360.57 699 49.65 43.79c11.18 9.9 17.78 47.45 14.78 83.91-2 24.27-7.83 41.95-14.77 48.13l-49.65 43.79c-18.58 16.38-37.79-19.43-42.83-80.07s6-123.1 24.58-139.51c6.15-5.42 12.5-5.04 18.24-.04z" fill="#35393b"/><path d="m850.73 940.2c36.66 0 67-39.85 75.51-99.15 4.56-31.83 2.26-65.19-6.48-94-9.41-31-25.51-53.59-45.32-63.72a51.72 51.72 0 0 0 -23.69-5.84h-103.53v262.71z" fill="#585e62"/><path d="m748.08 940.2c21.26 0 38.85-39.85 43.78-99.15 2.64-31.83 1.31-65.19-3.76-94-5.45-31-14.79-53.59-26.27-63.72-4.4-3.87-9-5.84-13.73-5.84s-9.34 2-13.76 5.8l-49.69 43.83c-17.38 15.32-23.39 64-20.23 102 2.43 29.28 10 52.18 20.19 61.28l49.68 43.82c4.45 3.99 9.09 5.98 13.79 5.98z" fill="#585e62"/><path d="m738.94 699-49.65 43.79c-11.19 9.86-17.8 47.41-14.77 83.87 2 24.27 7.83 41.95 14.77 48.13l49.65 43.79c18.61 16.41 37.78-19.43 42.82-80.07s-6-123.1-24.58-139.51c-6.18-5.38-12.5-5-18.24 0z" fill="#35393b"/><path d="m848.63 451.38a83.62 83.62 0 0 1 -.56-45.6c14.74-53.13 5.06-111.78 5.06-111.78-185.07-57.77-300.13-.48-300.13-.48s-114.79-57.64-300-.45c0 0-9.86 58.64 4.72 111.83a83.69 83.69 0 0 1 -.69 45.59c-5.14 17.57-10.72 44.5-10.77 78.8-.37 249 306 326.08 306 326.08s306.58-76.15 306.95-325.16c0-34.3-5.49-61.21-10.58-78.83z" fill="#cfcfcf"/><path d="m552.34 808.87c-50.72-15.87-261.7-93.25-261.42-279.51 0-29.65 4.89-52.42 9-66.31a128.3 128.3 0 0 0 .91-70c-6.2-22.58-6.9-47.13-6.17-65.29 40.48-10.23 80.2-15.37 118.41-15.32 75.66.12 119.86 21 120.3 21.17l20.39 10.23 19.24-10.32c.11 0 44.36-20.75 120-20.64 38.21.06 77.91 5.32 118.37 15.68.67 18.14-.1 42.71-6.37 65.27a128.33 128.33 0 0 0 .69 70c4 13.91 8.82 36.69 8.77 66.35-.28 187.06-211.26 263.09-262.12 278.69z" fill="#49b04a"/><path d="m610.15 478.76a57.46 57.46 0 1 0 -70.15 55.92l-32.29 135.47 44.67 12.85 44.71-12.71-31.84-135.57a57.46 57.46 0 0 0 44.9-55.96z" fill="#35393b"/><g fill="#49b04a"><path d="m454.94 131.08a60.64 60.64 0 0 0 -60.64 60.64h121.28a60.64 60.64 0 0 0 -60.64-60.64z"/><path d="m642.38 131.08a60.64 60.64 0 0 0 -60.64 60.64h121.26a60.64 60.64 0 0 0 -60.62-60.64z"/><circle cx="483.23" cy="229.43" r="11.52"/><circle cx="528.52" cy="229.43" r="11.52"/><circle cx="573.8" cy="229.43" r="11.52"/><circle cx="619.09" cy="229.43" r="11.52"/></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

2
.idea/misc.xml generated
View File

@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="16" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -72,7 +72,7 @@ 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))

View File

@@ -8,8 +8,11 @@ if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/pat
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
mvn -B -f ../../../pom.xml clean package -DskipTests -Plinux
cp ../../../target/cryptomator-*.jar ../../../target/mods
# add runtime
${JAVA_HOME}/bin/jlink \
@@ -26,8 +29,8 @@ ${JAVA_HOME}/bin/jpackage \
--verbose \
--type app-image \
--runtime-image runtime \
--input buildkit/libs \
--module-path buildkit/mods \
--input ../../../target/libs \
--module-path ../../../target/mods \
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
--dest . \
--name Cryptomator \
@@ -35,19 +38,20 @@ ${JAVA_HOME}/bin/jpackage \
--copyright "(C) 2016 - 2021 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--app-version "${{ needs.metadata.outputs.versionNum }}.${{ needs.metadata.outputs.revNum }}" \
--app-version "${VERSION}.${REVISION_NO}" \
--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=\"appimage-${{ needs.metadata.outputs.revNum }}\"" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--resource-dir ../resources
# transform AppDir
mv Cryptomator Cryptomator.AppDir
cp -r dist/linux/appimage/resources/AppDir/* 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
@@ -70,4 +74,4 @@ chmod +x /tmp/appimagetool.AppImage
/tmp/appimagetool.AppImage \
Cryptomator.AppDir \
cryptomator-SNAPSHOT-x86_64.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'

View File

@@ -26,12 +26,15 @@ export LD_PRELOAD=lib/app/libjffi.so
./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 \
-Xss2m \
-Xmx512m \
-Xss5m \
-Xmx256m \
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator

5
dist/linux/debian/changelog vendored Normal file
View File

@@ -0,0 +1,5 @@
cryptomator (${PPA_VERSION}) focal; urgency=low
* Full changelog can be found on https://github.com/cryptomator/cryptomator/releases
-- Cryptobot <releases@cryptomator.org> ${RFC2822_TIMESTAMP}

1
dist/linux/debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
10

23
dist/linux/debian/control vendored Normal file
View File

@@ -0,0 +1,23 @@
Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
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.

39
dist/linux/debian/copyright vendored Normal file
View File

@@ -0,0 +1,39 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cryptomator
Upstream-Contact: Cryptomator <info@cryptomator.org>
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
<https://www.gnu.org/licenses/>.
.
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.

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-vnd.cryptomator-vault-metadata">
<comment>Cryptomator Vault Metadata</comment>
<glob pattern="*.cryptomator"/>
</mime-type>
</mime-info>

7
dist/linux/debian/cryptomator.install vendored Normal file
View File

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

1
dist/linux/debian/cryptomator.links vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/cryptomator/bin/cryptomator.sh usr/bin/cryptomator

6
dist/linux/debian/cryptomator.sh vendored Normal file
View File

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

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018 Armin Schrenk <armin.schrenk@zoho.eu> -->
<component type="desktop-application">
<id>org.cryptomator.Cryptomator</id>
<metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
<name>Cryptomator</name>
<summary>Multi-platform client-side encryption tool optimized for cloud storages</summary>
<description>
<p>
Cryptomator offers multi-platform transparent client-side encryption of your files in the cloud.
</p>
<p>
Features:
<ul>
<li>Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory</li>
<li>Open Source means: No backdoors, control is better than trust</li>
<li>Client-side: No accounts, no data shared with any online service</li>
<li>Totally transparent: Just work on the virtual drive as if it were a USB flash drive</li>
<li>AES encryption with 256-bit key length</li>
<li>File names get encrypted</li>
<li>Folder structure gets obfuscated</li>
<li>Use as many vaults in your Dropbox as you want, each having individual passwords</li>
<li>One thousand commits for the security of your data!! :tada:</li>
</ul>
</p>
<p>
Privacy:
<ul>
<li>256-bit keys (unlimited strength policy bundled with native binaries)</li>
<li>Scrypt key derivation</li>
<li>Cryptographically secure random numbers for salts, IVs and the masterkey of course</li>
<li>Sensitive data is wiped from the heap asap</li>
<li>Lightweight: Complexity kills security</li>
</ul>
</p>
<p>
Consistency:
<ul>
<li>HMAC over file contents to recognize changed ciphertext before decryption</li>
<li>I/O operations are transactional and atomic, if the filesystems support it</li>
<li>Each file contains all information needed for decryption (except for the key of course), no common metadata means no Single Point of Failure</li>
</ul>
</p>
</description>
<categories>
<category>Office</category>
<category>Security</category>
<category>FileTools</category>
<category>Java</category>
</categories>
<url type="homepage">http://cryptomator.org</url>
<url type="bugtracker">https://github.com/cryptomator/cryptomator/issues</url>
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
<url type="help">https://community.cryptomator.org/</url>
<url type="donation">https://cryptomator.org/</url>
<content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute>
<content_attribute id="drugs-alcohol">none</content_attribute>
<content_attribute id="sex-nudity">none</content_attribute>
<content_attribute id="language-profanity">none</content_attribute>
<content_attribute id="social-info">mild</content_attribute> <!-- update checker conencts to https://api.cryptomator.org/updates/latestVersion.json -->
</content_rating>
<project_group>Cryptomator</project_group>
<provides>
<binary>cryptomator</binary>
</provides>
<launchable type="desktop-id">org.cryptomator.Cryptomator.desktop</launchable>
</component>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1 @@
<svg width="1110" height="1110" viewBox="0 0 1108.12 940.2" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg"><path d="m552.69 0c-169.1 0-262.45 143.46-262.45 283.52h524.9c0-140.06-105.33-283.52-262.45-283.52z" fill="#cfcfcf"/><path d="m552.69 53.2c-137.37 0-213.21 116.54-213.21 230.32l213.21 24.18 213.21-24.18c0-113.78-85.57-230.32-213.21-230.32z" fill="#585e62"/><path d="m89.8 739.52a20 20 0 0 1 -2.23-39.88 42.8 42.8 0 1 0 -42.22-21.82 20 20 0 0 1 -35 19.31 82.79 82.79 0 1 1 81.76 42.26 21.78 21.78 0 0 1 -2.31.13z" fill="#585e62"/><rect fill="#cfcfcf" height="144.19" rx="49.42" transform="matrix(.8902923 .45538953 -.45538953 .8902923 261.57 -5.69)" width="104.68" x="90.27" y="467.98"/><path d="m149.47 401.27h62.8a0 0 0 0 1 0 0v94.55a31.4 31.4 0 0 1 -31.4 31.4 31.4 31.4 0 0 1 -31.4-31.4v-94.55a0 0 0 0 1 0 0z" fill="#585e62" transform="matrix(.8902923 .45538953 -.45538953 .8902923 231.23 -31.44)"/><circle cx="222.59" cy="387.53" fill="#cfcfcf" r="75.05"/><path d="m258.09 321.8a75.09 75.09 0 0 0 -91.23 14.64 75.06 75.06 0 0 1 51.58 126.06 75.06 75.06 0 0 0 39.65-140.7z" fill="#b1b1b1"/><path d="m1018.31 739.52a22.09 22.09 0 0 1 -2.28-.13 82.8 82.8 0 1 1 81.77-42.26 20 20 0 1 1 -35-19.31 42.8 42.8 0 1 0 -42.23 21.82 20 20 0 0 1 -2.23 39.88z" fill="#585e62"/><rect fill="#cfcfcf" height="144.19" rx="49.42" transform="matrix(-.8902923 .45538953 -.45538953 -.8902923 2071.05 581.27)" width="104.68" x="913.18" y="467.98"/><path d="m927.25 401.27a31.4 31.4 0 0 1 31.4 31.4v94.55a0 0 0 0 1 0 0h-62.8a0 0 0 0 1 0 0v-94.55a31.4 31.4 0 0 1 31.4-31.4z" fill="#585e62" transform="matrix(-.8902923 .45538953 -.45538953 -.8902923 1964.18 455.35)"/><circle cx="885.53" cy="387.53" fill="#cfcfcf" r="75.05"/><path d="m850 321.8a75.08 75.08 0 0 1 91.22 14.64 75.06 75.06 0 0 0 -51.54 126.06 75.05 75.05 0 0 1 -39.68-140.7z" fill="#b1b1b1"/><path d="m248.78 940.2c-36.67 0-67-39.85-75.51-99.15-4.56-31.83-2.26-65.19 6.48-94 9.41-31 25.51-53.59 45.32-63.72a51.72 51.72 0 0 1 23.69-5.84h103.52v262.71z" fill="#585e62"/><path d="m351.43 940.2c-21.26 0-38.85-39.85-43.78-99.15-2.64-31.83-1.31-65.19 3.76-94 5.45-31 14.78-53.59 26.27-63.72 4.39-3.87 9-5.84 13.73-5.84s9.34 2 13.75 5.8l49.7 43.83c17.37 15.32 23.39 64 20.23 102-2.43 29.28-10 52.18-20.19 61.28l-49.69 43.82c-4.44 3.99-9.08 5.98-13.78 5.98z" fill="#585e62"/><path d="m360.57 699 49.65 43.79c11.18 9.9 17.78 47.45 14.78 83.91-2 24.27-7.83 41.95-14.77 48.13l-49.65 43.79c-18.58 16.38-37.79-19.43-42.83-80.07s6-123.1 24.58-139.51c6.15-5.42 12.5-5.04 18.24-.04z" fill="#35393b"/><path d="m850.73 940.2c36.66 0 67-39.85 75.51-99.15 4.56-31.83 2.26-65.19-6.48-94-9.41-31-25.51-53.59-45.32-63.72a51.72 51.72 0 0 0 -23.69-5.84h-103.53v262.71z" fill="#585e62"/><path d="m748.08 940.2c21.26 0 38.85-39.85 43.78-99.15 2.64-31.83 1.31-65.19-3.76-94-5.45-31-14.79-53.59-26.27-63.72-4.4-3.87-9-5.84-13.73-5.84s-9.34 2-13.76 5.8l-49.69 43.83c-17.38 15.32-23.39 64-20.23 102 2.43 29.28 10 52.18 20.19 61.28l49.68 43.82c4.45 3.99 9.09 5.98 13.79 5.98z" fill="#585e62"/><path d="m738.94 699-49.65 43.79c-11.19 9.86-17.8 47.41-14.77 83.87 2 24.27 7.83 41.95 14.77 48.13l49.65 43.79c18.61 16.41 37.78-19.43 42.82-80.07s-6-123.1-24.58-139.51c-6.18-5.38-12.5-5-18.24 0z" fill="#35393b"/><path d="m848.63 451.38a83.62 83.62 0 0 1 -.56-45.6c14.74-53.13 5.06-111.78 5.06-111.78-185.07-57.77-300.13-.48-300.13-.48s-114.79-57.64-300-.45c0 0-9.86 58.64 4.72 111.83a83.69 83.69 0 0 1 -.69 45.59c-5.14 17.57-10.72 44.5-10.77 78.8-.37 249 306 326.08 306 326.08s306.58-76.15 306.95-325.16c0-34.3-5.49-61.21-10.58-78.83z" fill="#cfcfcf"/><path d="m552.34 808.87c-50.72-15.87-261.7-93.25-261.42-279.51 0-29.65 4.89-52.42 9-66.31a128.3 128.3 0 0 0 .91-70c-6.2-22.58-6.9-47.13-6.17-65.29 40.48-10.23 80.2-15.37 118.41-15.32 75.66.12 119.86 21 120.3 21.17l20.39 10.23 19.24-10.32c.11 0 44.36-20.75 120-20.64 38.21.06 77.91 5.32 118.37 15.68.67 18.14-.1 42.71-6.37 65.27a128.33 128.33 0 0 0 .69 70c4 13.91 8.82 36.69 8.77 66.35-.28 187.06-211.26 263.09-262.12 278.69z" fill="#49b04a"/><path d="m610.15 478.76a57.46 57.46 0 1 0 -70.15 55.92l-32.29 135.47 44.67 12.85 44.71-12.71-31.84-135.57a57.46 57.46 0 0 0 44.9-55.96z" fill="#35393b"/><g fill="#49b04a"><path d="m454.94 131.08a60.64 60.64 0 0 0 -60.64 60.64h121.28a60.64 60.64 0 0 0 -60.64-60.64z"/><path d="m642.38 131.08a60.64 60.64 0 0 0 -60.64 60.64h121.26a60.64 60.64 0 0 0 -60.62-60.64z"/><circle cx="483.23" cy="229.43" r="11.52"/><circle cx="528.52" cy="229.43" r="11.52"/><circle cx="573.8" cy="229.43" r="11.52"/><circle cx="619.09" cy="229.43" r="11.52"/></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

44
dist/linux/debian/postinst vendored Normal file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
# postinst script for Cryptomator
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# 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

42
dist/linux/debian/prerm vendored Normal file
View File

@@ -0,0 +1,42 @@
#!/bin/sh
# prerm script for Cryptomator
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# 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

53
dist/linux/debian/rules vendored Executable file
View File

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

1
dist/linux/debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
3.0 (quilt)

View File

@@ -0,0 +1,2 @@
debian/org.cryptomator.Cryptomator.png
resources/cryptomator.png

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

3
dist/mac/dmg/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# created during build
runtime/
*.app/

96
dist/mac/dmg/build.sh vendored Executable file
View File

@@ -0,0 +1,96 @@
#!/bin/bash
# parse options
usage() { echo "Usage: $0 [-s <codesign-identity>]" 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

View File

@@ -84,7 +84,7 @@
</array>
<key>public.mime-type</key>
<array>
<string>application/x-vnd.cryptomator.vault-metadata</string>
<string>application/vnd.cryptomator.vault</string>
</array>
</dict>
</dict>

BIN
dist/win/resources/Cryptomator-Vault.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@@ -1,4 +0,0 @@
mime-type=application/vnd.cryptomator.encrypted
extension=c9r,c9s
description=Cryptomator Encrypted Data
icon=resources/Cryptomator.ico

View File

@@ -1,4 +1,4 @@
mime-type=application/vnd.cryptomator.vault
extension=cryptomator
description=Cryptomator Vault File
icon=resources/Cryptomator.ico
icon=resources/Cryptomator-Vault.ico

View File

@@ -11,36 +11,30 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
\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 49 third-party dependencies under the following licenses:\b0\par
\b Cryptomator uses 40 third-party dependencies under the following licenses:\b0\par
\tab Apache License v2.0:\par
\tab\tab - HKDF-RFC5869 (at.favre.lib:hkdf:1.0.2 - {{\field{\*\fldinst{HYPERLINK https://github.com/patrickfav/hkdf }}{\fldrslt{https://github.com/patrickfav/hkdf\ul0\cf0}}}}\f0\fs16 )\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 - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - {{\field{\*\fldinst{HYPERLINK http://findbugs.sourceforge.net/ }}{\fldrslt{http://findbugs.sourceforge.net/\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - 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\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Dagger (com.google.dagger:dagger:2.27 - {{\field{\*\fldinst{HYPERLINK https://github.com/google/dagger }}{\fldrslt{https://github.com/google/dagger\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - 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_annotations }}{\fldrslt{http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations\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:28.2-jre - {{\field{\*\fldinst{HYPERLINK https://github.com/google/guava/guava }}{\fldrslt{https://github.com/google/guava/guava\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - 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\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - {{\field{\*\fldinst{HYPERLINK https://github.com/google/j2objc/ }}{\fldrslt{https://github.com/google/j2objc/\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.1.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.1.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.9 - {{\field{\*\fldinst{HYPERLINK http://commons.apache.org/proper/commons-lang/ }}{\fldrslt{http://commons.apache.org/proper/commons-lang/\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.19.0 - {{\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:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - 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{{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\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
@@ -48,45 +42,44 @@ You should have received a copy of the GNU General Public License along with thi
\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 :: Http Utility (org.eclipse.jetty:jetty-http:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - 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{http://www.eclipse.org/jetty\ul0\cf0}}}}}\f0\fs16 )\par
\tab\tab - Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.17.v20190418 - {{\field{\*\fldinst{HYPERLINK http://www.eclipse.org/jetty }}{\fldrslt{http://www.eclipse.org/jetty\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 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 - 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\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - javafx-base (org.openjfx:javafx-base:14 - {{\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:14 - {{\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:14 - {{\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:14 - {{\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\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 - dbus-java (com.github.hypfvieh:dbus-java:3.0.2 - {{\field{\*\fldinst{HYPERLINK https://github.com/hypfvieh/dbus-java/dbus-java }}{\fldrslt{https://github.com/hypfvieh/dbus-java/dbus-java\ul0\cf0}}}}\f0\fs16 )\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.1.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.1.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 (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.10.2 - {{\field{\*\fldinst{HYPERLINK https://github.com/auth0/java-jwt }}{\fldrslt{https://github.com/auth0/java-jwt\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - java-utils (com.github.hypfvieh:java-utils:1.0.5 - {{\field{\*\fldinst{HYPERLINK https://github.com/hypfvieh/java-utils }}{\fldrslt{https://github.com/hypfvieh/java-utils\ul0\cf0}}}}\f0\fs16 )\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.4 - no url defined)\par
\tab\tab - zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - {{\field{\*\fldinst{HYPERLINK https://github.com/nulab/zxcvbn4j }}{\fldrslt{https://github.com/nulab/zxcvbn4j\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - secret-service (de.swiesend:secret-service:1.0.0-RC.3 - {{\field{\*\fldinst{HYPERLINK https://github.com/swiesend/secret-service }}{\fldrslt{https://github.com/swiesend/secret-service\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - Checker Qual (org.checkerframework:checker-qual:2.10.0 - {{\field{\*\fldinst{HYPERLINK https://checkerframework.org }}{\fldrslt{https://checkerframework.org\ul0\cf0}}}}\f0\fs16 )\par
\tab\tab - SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - {{\field{\*\fldinst{HYPERLINK http://www.slf4j.org }}{\fldrslt{http://www.slf4j.org\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 (org.fxmisc.easybind:easybind:1.0.3 - {{\field{\*\fldinst{HYPERLINK http://www.fxmisc.org/easybind/ }}{\fldrslt{http://www.fxmisc.org/easybind/\ul0\cf0}}}}\f0\fs16 )\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 )\par
\par
\tab\tab - Font Awesome 5.12.0 ({{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
}

View File

@@ -1,15 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Cryptomator's Jpackage Wix installer file
Remark: Expressions like $(var.name) are preprocessor variables, see also https://wixtoolset.org/documentation/manual/v3/overview/preprocessor.html
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?ifdef JpIsSystemWide ?>
<?define JpInstallScope="perMachine"?>
<?define JpInstallScope="perMachine"?>
<?else?>
<?define JpInstallScope="perUser"?>
<?define JpInstallScope="perUser"?>
<?endif?>
<?define JpProductLanguage=1033 ?>
@@ -17,27 +13,49 @@
<?define JpCompressedMsi=yes ?>
<?ifdef JpAllowUpgrades ?>
<?define JpUpgradeVersionOnlyDetectUpgrade="no"?>
<?define JpUpgradeVersionOnlyDetectUpgrade="no"?>
<?else?>
<?define JpUpgradeVersionOnlyDetectUpgrade="yes"?>
<?define JpUpgradeVersionOnlyDetectUpgrade="yes"?>
<?endif?>
<?ifdef JpAllowDowngrades ?>
<?define JpUpgradeVersionOnlyDetectDowngrade="no"?>
<?define JpUpgradeVersionOnlyDetectDowngrade="no"?>
<?else?>
<?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
<?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
<?endif?>
<?include $(var.JpConfigDir)/overrides.wxi ?>
<Product Id="$(var.JpProductCode)" Name="$(var.JpAppName)" Language="$(var.JpProductLanguage)" Version="$(var.JpAppVersion)" Manufacturer="$(var.JpAppVendor)" UpgradeCode="$(var.JpProductUpgradeCode)">
<Product
Id="$(var.JpProductCode)"
Name="$(var.JpAppName)"
Language="$(var.JpProductLanguage)"
Version="$(var.JpAppVersion)"
Manufacturer="$(var.JpAppVendor)"
UpgradeCode="$(var.JpProductUpgradeCode)">
<Package Description="$(var.JpAppDescription)" Manufacturer="$(var.JpAppVendor)" InstallerVersion="$(var.JpInstallerVersion)" Compressed="$(var.JpCompressedMsi)" InstallScope="$(var.JpInstallScope)" Platform="x64" />
<Package
Description="$(var.JpAppDescription)"
Manufacturer="$(var.JpAppVendor)"
InstallerVersion="$(var.JpInstallerVersion)"
Compressed="$(var.JpCompressedMsi)"
InstallScope="$(var.JpInstallScope)" Platform="x64"
/>
<Media Id="1" Cabinet="Data.cab" EmbedCab="yes" />
<Upgrade Id="$(var.JpProductUpgradeCode)">
<UpgradeVersion OnlyDetect="$(var.JpUpgradeVersionOnlyDetectUpgrade)" Property="JP_UPGRADABLE_FOUND" Maximum="$(var.JpAppVersion)" MigrateFeatures="yes" IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
<UpgradeVersion OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)" Property="JP_DOWNGRADABLE_FOUND" Minimum="$(var.JpAppVersion)" MigrateFeatures="yes" IncludeMinimum="$(var.JpUpgradeVersionOnlyDetectDowngrade)" />
<UpgradeVersion
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectUpgrade)"
Property="JP_UPGRADABLE_FOUND"
Maximum="$(var.JpAppVersion)"
MigrateFeatures="yes"
IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
<UpgradeVersion
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
Property="JP_DOWNGRADABLE_FOUND"
Minimum="$(var.JpAppVersion)"
MigrateFeatures="yes"
IncludeMinimum="$(var.JpUpgradeVersionOnlyDetectDowngrade)" />
</Upgrade>
<?ifndef JpAllowUpgrades ?>
@@ -47,6 +65,37 @@
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>
<!-- Looking for legacy Cryptomator versions-->
<Property Id="OLDEXEINSTALLER">
<RegistrySearch Id="InnoSetupInstallation" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Cryptomator_is1" Type="raw" Name="DisplayName" />
</Property>
<!-- Block installation if innosetup entry of Cryptomator is found -->
<!-- TODO: localize -->
<Condition Message="A lower version of [ProductName] is already installed. Uninstall it first and then start the setup again. Setup will now exit.">
<![CDATA[Installed OR NOT OLDEXEINSTALLER]]>
</Condition>
<!-- Cryptomator uses UNIX Sockets, which are supported starting with Windows 10 v1803-->
<Property Id="WINDOWSBUILDNUMBER" Secure="yes">
<RegistrySearch Id="BuildNumberSearch" Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion" Name="CurrentBuildNumber" Type="raw" />
</Property>
<Condition Message="This application requires Windows 10 version 1803 (build 17134) or newer.">
<![CDATA[Installed OR (WINDOWSBUILDNUMBER >= 17134)]]>
</Condition>
<!-- Non-Opening ProgID -->
<DirectoryRef Id="INSTALLDIR">
<Component Win64="yes" Id="nonStartingProgID" >
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\Cryptomator-Vault.ico" Name="Cryptomator-Vault.ico"></File>
<ProgId Id="Cryptomator.Encrypted.1" Description="Cryptomator Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="application/vnd.cryptomator.encrypted">
<MIME ContentType="application/vnd.cryptomator.encrypted" Default="yes"></MIME>
</Extension>
<Extension Id="c9s" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"/>
</ProgId>
</Component>
</DirectoryRef>
<!-- Standard required root -->
<Directory Id="TARGETDIR" Name="SourceDir"/>
@@ -54,70 +103,49 @@
<ComponentGroupRef Id="Shortcuts"/>
<ComponentGroupRef Id="Files"/>
<ComponentGroupRef Id="FileAssociations"/>
<!-- Ref to additional ProgIDs -->
<ComponentRef Id="nonStartingProgID" />
</Feature>
<?ifdef JpInstallDirChooser ?>
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
<CustomAction Id="JpCheckInstallDir" BinaryKey="JpCaDll" DllEntry="CheckInstallDir" />
<CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
<CustomAction Id="JpSetARPCOMMENTS" Property="ARPCOMMENTS" Value="$(var.JpAppDescription)" />
<CustomAction Id="JpSetARPCONTACT" Property="ARPCONTACT" Value="$(var.JpAppVendor)" />
<CustomAction Id="JpSetARPSIZE" Property="ARPSIZE" Value="$(var.JpAppSizeKb)" />
<?ifdef JpHelpURL ?>
<CustomAction Id="JpSetARPHELPLINK" Property="ARPHELPLINK" Value="$(var.JpHelpURL)" />
<?endif?>
<CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
<?ifdef JpAboutURL ?>
<CustomAction Id="JpSetARPURLINFOABOUT" Property="ARPURLINFOABOUT" Value="$(var.JpAboutURL)" />
<?endif?>
<?ifdef JpUpdateURL ?>
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
<?endif?>
<?ifdef JpIcon ?>
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
<?endif?>
<UI>
<?ifdef JpInstallDirChooser ?>
<Dialog Id="JpInvalidInstallDir" Width="300" Height="85" Title="[ProductName] Setup" NoMinimize="yes">
<Control Id="JpInvalidInstallDirYes" Type="PushButton" X="100" Y="55" Width="50" Height="15" Default="no" Cancel="no" Text="Yes">
<Publish Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
</Control>
<Control Id="JpInvalidInstallDirNo" Type="PushButton" X="150" Y="55" Width="50" Height="15" Default="yes" Cancel="yes" Text="No">
<Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
</Control>
<Control Id="Text" Type="Text" X="25" Y="15" Width="250" Height="30" TabSkip="no">
<Text>!(loc.message.install.dir.exist)</Text>
</Control>
</Dialog>
<!--
Run our custom wizard in the default install directory.
-->
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR"/>
<!-- included at the end -->
<UIRef Id="CustomWizard" />
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="JpCheckInstallDir" Order="3">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="JpInvalidInstallDir" Order="5">INSTALLDIR_VALID="0"</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="5">INSTALLDIR_VALID="1"</Publish>
<?ifndef JpLicenseRtf ?>
<!--
No license file provided.
Override the dialog sequence in built-in dialog set "WixUI_InstallDir"
to exclude license dialog.
-->
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">1</Publish>
<?endif?>
<?else?>
<?ifdef JpLicenseRtf ?>
<UIRef Id="WixUI_Minimal" />
<?endif?>
<?endif?>
</UI>
<?ifdef JpLicenseRtf ?>
<WixVariable Id="WixUILicenseRtf" Value="$(var.JpLicenseRtf)"/>
<?endif?>
<UIRef Id="JpUI"/>
<InstallExecuteSequence>
<Custom Action="JpSetARPINSTALLLOCATION" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPCOMMENTS" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPCONTACT" After="CostFinalize">Not Installed</Custom>
<Custom Action="JpSetARPSIZE" After="CostFinalize">Not Installed</Custom>
<?ifdef JpHelpURL ?>
<Custom Action="JpSetARPHELPLINK" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifdef JpAboutURL ?>
<Custom Action="JpSetARPURLINFOABOUT" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifdef JpUpdateURL ?>
<Custom Action="JpSetARPURLUPDATEINFO" After="CostFinalize">Not Installed</Custom>
<?endif?>
<?ifndef JpAllowUpgrades ?>
<Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<?endif?>
@@ -131,5 +159,4 @@
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
</Product>
<?include $(env.JP_WIXWIZARD_RESOURCES)\customWizard.wxi ?>
</Wix>

16
dist/win/resources/ui.wxf vendored Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR"></Property>
<WixVariable Id="WixUILicenseRtf" Value="$(var.JpLicenseRtf)"></WixVariable>
<UI Id="JpUI">
<UIRef Id="CustomWizard" />
<DialogRef Id="InstallDirNotEmptyDlg"></DialogRef>
<Publish Dialog="ShortcutPromptDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg">1</Publish>
<Publish Dialog="ShortcutPromptDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ShortcutPromptDlg" Order="6">NOT Installed</Publish>
<UIRef Id="CustomWizard" />
</UI>
</Fragment>
<?include $(env.JP_WIXWIZARD_RESOURCES)\customWizard.wxi ?>
</Wix>

41
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.7.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -21,39 +21,38 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>16</project.jdk.version>
<project.jdk.version>17</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<nonModularGroupIds>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</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptolib.version>2.1.0-beta2</cryptomator.cryptolib.version>
<cryptomator.cryptofs.version>2.1.0-beta12</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.0.0-rc1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.0-beta1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.1</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.1</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.5</cryptomator.webdav.version>
<cryptomator.cryptofs.version>2.1.1</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.0.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.6</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>16</javafx.version>
<javafx.version>17.0.0.1</javafx.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<bouncycastle.version>1.69</bouncycastle.version>
<jwt.version>3.18.1</jwt.version>
<jwt.version>3.18.2</jwt.version>
<easybind.version>2.2</easybind.version>
<guava.version>30.1.1-jre</guava.version>
<dagger.version>2.37</dagger.version>
<gson.version>2.8.7</gson.version>
<guava.version>31.0-jre</guava.version>
<dagger.version>2.39</dagger.version>
<gson.version>2.8.8</gson.version>
<zxcvbn.version>1.5.2</zxcvbn.version>
<slf4j.version>1.7.31</slf4j.version>
<logback.version>1.2.3</logback.version>
<slf4j.version>1.7.32</slf4j.version>
<logback.version>1.2.6</logback.version>
<jetty.version>10.0.6</jetty.version>
<!-- test dependencies -->
<junit.jupiter.version>5.7.2</junit.jupiter.version>
<mockito.version>3.11.2</mockito.version>
<junit.jupiter.version>5.8.1</junit.jupiter.version>
<mockito.version>3.12.4</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
</properties>
@@ -287,7 +286,7 @@
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.2.2</version>
<version>6.3.1</version>
</plugin>
</plugins>
</pluginManagement>

View File

@@ -56,6 +56,7 @@ module org.cryptomator.desktop {
opens org.cryptomator.ui.health to javafx.fxml;
opens org.cryptomator.ui.keyloading.hub 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;

View File

@@ -5,6 +5,7 @@ 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];
}

View File

@@ -37,8 +37,10 @@ public class Environment {
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"));
@@ -68,10 +70,18 @@ public class Environment {
return getPath("cryptomator.logDir").map(this::replaceHomeDir);
}
public Optional<Path> getPluginDir() {
return getPath("cryptomator.pluginDir").map(this::replaceHomeDir);
}
public Optional<Path> getMountPointsDir() {
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
}
public Optional<String> getAppVersion() {
return Optional.ofNullable(System.getProperty("cryptomator.appVersion"));
}
public Optional<String> getBuildNumber() {
return Optional.ofNullable(System.getProperty("cryptomator.buildNumber"));
}

View File

@@ -0,0 +1,142 @@
package org.cryptomator.common;
import com.google.common.base.Preconditions;
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.
* <p>
* 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 <code>cause</code>.
* <p>
* The code consists of three parts separated by {@value DELIM}:
* <ul>
* <li>The first part depends on the root cause and the method that threw it</li>
* <li>The second part depends on the root cause and its stack trace</li>
* <li>The third part depends on all the cause hierarchy</li>
* </ul>
* <p>
* 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 = countTopmostFrames(rootCause.getStackTrace(), parentOfRootCause.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;
}
/**
* Counts the number of <em>additional</em> frames contained in <code>allFrames</code> but not in <code>bottomFrames</code>.
* <p>
* If <code>allFrames</code> does not end with <code>bottomFrames</code>, it is considered distinct and all its frames are counted.
*
* @param allFrames Some stack frames
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
*/
// visible for testing
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
if (allFrames.length < bottomFrames.length) {
// if frames had been stacked on top of bottomFrames, allFrames would be larger
return allFrames.length;
} else {
return allFrames.length - commonSuffixLength(allFrames, bottomFrames);
}
}
// visible for testing
static <T> int commonSuffixLength(T[] set, T[] subset) {
Preconditions.checkArgument(set.length >= subset.length);
// iterate items backwards as long as they are identical
var iterator = reverseStream(subset).iterator();
return (int) reverseStream(set).takeWhile(item -> iterator.hasNext() && iterator.next().equals(item)).count();
}
private static <T> Stream<T> reverseStream(T[] array) {
return IntStream.rangeClosed(1, array.length).mapToObj(i -> array[array.length - i]);
}
}

View File

@@ -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<Path> {
private final List<URL> 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;
}
}
}

View File

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

View File

@@ -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<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories() {
return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet());
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
}
@Provides

View File

@@ -24,6 +24,6 @@ class AvailableDriveLetterChooser implements MountPointChooser {
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return this.windowsDriveLetters.getAvailableDriveLetterPath();
return this.windowsDriveLetters.getDesiredAvailableDriveLetterPath();
}
}

View File

@@ -43,6 +43,8 @@ public class Settings {
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<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
@@ -59,6 +61,12 @@ public class Settings {
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<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
private Consumer<Settings> saveCmd;
@@ -83,6 +91,11 @@ public class Settings {
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<Settings> saveCmd) {
@@ -141,7 +154,7 @@ public class Settings {
return theme;
}
public ObjectProperty<String> keychainProvider() { return keychainProvider; }
public ObjectProperty<String> keychainProvider() {return keychainProvider;}
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
return userInterfaceOrientation;
@@ -158,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<String> displayConfigurationProperty() {
return displayConfiguration;
}
}

View File

@@ -52,6 +52,12 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
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();
}
@@ -86,6 +92,12 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
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();

View File

@@ -17,9 +17,6 @@ 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.VaultConfig;
import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
@@ -38,8 +35,6 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
@@ -62,6 +57,7 @@ public class Vault {
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
private final VaultState state;
private final ObjectProperty<Exception> lastKnownException;
private final VaultConfigCache configCache;
private final VaultStats stats;
private final StringBinding displayName;
private final StringBinding displayablePath;
@@ -78,8 +74,9 @@ public class Vault {
private volatile Volume volume;
@Inject
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
this.vaultSettings = vaultSettings;
this.configCache = configCache;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
this.cryptoFileSystem = cryptoFileSystem;
@@ -107,10 +104,10 @@ public class Vault {
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
} else if(vaultSettings.maxCleartextFilenameLength().get() == -1) {
} else if (vaultSettings.maxCleartextFilenameLength().get() == -1) {
LOG.debug("Determining cleartext filename length limitations...");
var checker = new FileSystemCapabilityChecker();
int shorteningThreshold = getUnverifiedVaultConfig().allegedShorteningThreshold();
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
if (ciphertextLimit < shorteningThreshold) {
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
@@ -328,19 +325,6 @@ public class Vault {
return stats;
}
/**
* Attempts to read the vault config file and parse it without verifying its integrity.
*
* @return an unverified vault config
* @throws VaultConfigLoadException if the read file cannot be properly parsed
* @throws IOException if reading the file fails
*
*/
public UnverifiedVaultConfig getUnverifiedVaultConfig() throws IOException {
Path configPath = getPath().resolve(org.cryptomator.common.Constants.VAULTCONFIG_FILENAME);
String token = Files.readString(configPath, StandardCharsets.US_ASCII);
return VaultConfig.decode(token);
}
public Observable[] observables() {
return new Observable[]{state};
@@ -375,6 +359,10 @@ public class Vault {
}
}
public VaultConfigCache getVaultConfigCache() {
return configCache;
}
public void setCustomMountFlags(String mountFlags) {
vaultSettings.mountFlags().set(mountFlags);
}

View File

@@ -25,6 +25,9 @@ public interface VaultComponent {
@BindsInstance
Builder vaultSettings(VaultSettings vaultSettings);
@BindsInstance
Builder vaultConfigCache(VaultConfigCache configCache);
@BindsInstance
Builder initialVaultState(VaultState.Value vaultState);

View File

@@ -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<VaultConfig.UnverifiedVaultConfig> 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);
}
}

View File

@@ -18,7 +18,6 @@ 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;
@@ -31,6 +30,7 @@ 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 {
@@ -96,6 +96,11 @@ 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);
@@ -112,6 +117,9 @@ public class VaultListManager {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
try {
var determinedState = determineVaultState(vault.getPath());
if (determinedState == LOCKED) {
vault.getVaultConfigCache().reloadConfig();
}
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
@@ -132,7 +140,9 @@ public class VaultListManager {
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;
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? //
VaultState.Value.NEEDS_MIGRATION //
: VaultState.Value.MISSING;
};
}

View File

@@ -70,7 +70,7 @@ public class WebDavVolume implements Volume {
//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<String> 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();
}

View File

@@ -22,11 +22,11 @@ import java.util.stream.StreamSupport;
@Singleton
public final class WindowsDriveLetters {
private static final Set<String> C_TO_Z;
private static final Set<String> 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<String> getAllDriveLetters() {
return C_TO_Z;
return A_TO_Z;
}
public Set<String> 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<String> 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<Path> getDesiredAvailableDriveLetterPath() {
return getDesiredAvailableDriveLetter().map(this::toPath);
}
public Path toPath(String driveLetter) {
return Path.of(driveLetter + ":\\");
}

View File

@@ -44,7 +44,9 @@ public interface IpcCommunicator extends Closeable {
}
// 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());
final var socketPath = socketPaths.iterator().next();
Files.deleteIfExists(socketPath); // ensure path does not exist before creating it
return Server.create(socketPath);
} catch (IOException e) {
LOG.warn("Failed to create IPC server", e);
return new LoopbackCommunicator();

View File

@@ -27,6 +27,7 @@ class Server implements IpcCommunicator {
}
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);

View File

@@ -38,18 +38,16 @@ public class Cryptomator {
private final DebugMode debugMode;
private final Environment env;
private final Lazy<IpcMessageHandler> ipcMessageHandler;
private final Optional<String> applicationVersion;
private final CountDownLatch shutdownLatch;
private final ShutdownHook shutdownHook;
private final Lazy<UiLauncher> uiLauncher;
@Inject
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("applicationVersion") Optional<String> applicationVersion, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
this.logConfig = logConfig;
this.debugMode = debugMode;
this.env = env;
this.ipcMessageHandler = ipcMessageHandler;
this.applicationVersion = applicationVersion;
this.shutdownLatch = shutdownLatch;
this.shutdownHook = shutdownHook;
this.uiLauncher = uiLauncher;
@@ -69,7 +67,7 @@ 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();
/*

View File

@@ -18,11 +18,4 @@ class CryptomatorModule {
return new CountDownLatch(1);
}
@Provides
@Singleton
@Named("applicationVersion")
static Optional<String> provideApplicationVersion() {
return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
}
}

View File

@@ -56,11 +56,10 @@ public class ChooseExistingVaultController implements FxController {
@FXML
public void initialize() {
final String resource = SystemUtils.IS_OS_MAC ? "/img/select-masterkey-mac.png" : "/img/select-masterkey-win.png";
try (InputStream in = getClass().getResourceAsStream(resource)) {
this.screenshot = new Image(in);
} catch (IOException e) {
throw new UncheckedIOException(e);
if (SystemUtils.IS_OS_MAC) {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac.png").toString());
} else {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString());
}
}
@@ -73,7 +72,7 @@ public class ChooseExistingVaultController implements FxController {
public void chooseFileAndNext() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Vault", "*.cryptomator"));
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent());

View File

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

View File

@@ -44,8 +44,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -195,12 +197,28 @@ public class CreateNewVaultPasswordController implements FxController {
} catch (CryptoException e) {
throw new IOException("Failed initialize vault.", e);
}
} finally {
AtomicBoolean cleanupFailed = new AtomicBoolean(false);
Files.walk(path)
.sorted(Comparator.reverseOrder())
.forEach(p -> {
try {
Files.deleteIfExists(p);
} catch (IOException e) {
cleanupFailed.set(false);
}
});
if(cleanupFailed.get()) {
LOG.warn("Failed to cleanup after failed vault creation at {}. Leftovers need to be deleted manually.", path);
}
}
// 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()));
} catch (IOException e) {
LOG.warn("Unable to create vault storage location readme.", e);
}
LOG.info("Created vault at {}", path);

View File

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

View File

@@ -1,22 +1,47 @@
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 = """
<!-- ✏️ Please describe what happened as accurately as possible. -->
<!-- 📋 Please also copy and paste the detail text from the error window. -->
""";
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(@Named("stackTrace") String stackTrace, @Nullable Scene previousScene, Stage window) {
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;
}
@@ -33,6 +58,31 @@ public class ErrorController implements FxController {
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() {
@@ -42,4 +92,20 @@ public class ErrorController implements FxController {
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();
}
}

View File

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

View File

@@ -12,7 +12,6 @@ public enum FxmlFile {
ERROR("/fxml/error.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
HEALTH_START("/fxml/health_start.fxml"), //
HEALTH_START_FAIL("/fxml/health_start_fail.fxml"), //
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //

View File

@@ -12,6 +12,7 @@ public enum FontAwesome5Icon {
CARET_RIGHT("\uF0Da"), //
CHECK("\uF00C"), //
CLOCK("\uF017"), //
CLIPBOARD("\uF328"), //
COG("\uF013"), //
COGS("\uF085"), //
COPY("\uF0C5"), //

View File

@@ -10,6 +10,7 @@ package org.cryptomator.ui.controls;
import com.google.common.base.Strings;
import javafx.application.Platform;
import javafx.beans.NamedArg;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
@@ -27,7 +28,6 @@ import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode;
import java.awt.Toolkit;
import java.nio.CharBuffer;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -123,8 +123,7 @@ public class SecurePasswordField extends TextField {
}
private void updateCapsLocked() {
//TODO: fixed in JavaFX 17. AWT code needed until update (see https://bugs.openjdk.java.net/browse/JDK-8259680)
capsLocked.set(isFocused() && Toolkit.getDefaultToolkit().getLockingKeyState(java.awt.event.KeyEvent.VK_CAPS_LOCK));
capsLocked.set(Platform.isKeyLocked(KeyCode.CAPS).orElse(false));
}
private void updateContainingNonPrintableChars() {

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,9 +32,9 @@ public class UpdateChecker {
private final ScheduledService<String> updateCheckerService;
@Inject
UpdateChecker(Settings settings, @Named("applicationVersion") Optional<String> applicationVersion, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
this.settings = settings;
this.applicationVersion = applicationVersion;
this.applicationVersion = env.getAppVersion();
this.latestVersionProperty = latestVersionProperty;
this.semVerComparator = semVerComparator;
this.updateCheckerService = updateCheckerService;

View File

@@ -3,6 +3,7 @@ package org.cryptomator.ui.fxapp;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,9 +52,9 @@ public abstract class UpdateCheckerModule {
@Provides
@FxApplicationScoped
static HttpRequest provideCheckForUpdatesRequest(@Named("applicationVersion") Optional<String> applicationVersion) {
static HttpRequest provideCheckForUpdatesRequest(Environment env) {
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", //
applicationVersion.orElse("SNAPSHOT"), //
env.getAppVersion().orElse("SNAPSHOT"), //
SystemUtils.OS_NAME, //
SystemUtils.OS_VERSION, //
SystemUtils.OS_ARCH); //

View File

@@ -1,16 +1,14 @@
package org.cryptomator.ui.health;
import com.tobiasdiez.easybind.EasyBind;
import com.tobiasdiez.easybind.Subscription;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.CheckBox;
import java.util.ArrayList;
import java.util.List;
public class CheckListCellController implements FxController {
@@ -18,26 +16,26 @@ public class CheckListCellController implements FxController {
private final ObjectProperty<Check> check;
private final Binding<String> checkName;
private final Binding<Boolean> checkRunnable;
private final List<Subscription> subscriptions;
/* FXML */
public CheckBox forRunSelectedCheckBox;
public CheckBox checkbox;
@Inject
public CheckListCellController() {
check = new SimpleObjectProperty<>();
checkRunnable = EasyBind.wrapNullable(check).mapObservable(Check::stateProperty).map(Check.CheckState.RUNNABLE::equals).orElse(false);
checkName = EasyBind.wrapNullable(check).map(Check::getName).orElse("");
subscriptions = new ArrayList<>();
}
public void initialize() {
subscriptions.add(EasyBind.subscribe(check, c -> {
forRunSelectedCheckBox.selectedProperty().unbind();
if (c != null) {
forRunSelectedCheckBox.selectedProperty().bindBidirectional(c.chosenForExecutionProperty());
check.addListener((observable, oldVal, newVal) -> {
if (oldVal != null) {
Bindings.unbindBidirectional(checkbox.selectedProperty(), oldVal.chosenForExecutionProperty());
}
}));
if (newVal != null) {
Bindings.bindBidirectional(checkbox.selectedProperty(), newVal.chosenForExecutionProperty());
}
});
}
public ObjectProperty<Check> checkProperty() {

View File

@@ -16,26 +16,15 @@ import javafx.stage.Stage;
@Subcomponent(modules = {HealthCheckModule.class})
public interface HealthCheckComponent {
LoadUnverifiedConfigResult loadConfig();
@HealthCheckWindow
Stage window();
@FxmlScene(FxmlFile.HEALTH_START)
Lazy<Scene> startScene();
@FxmlScene(FxmlFile.HEALTH_START_FAIL)
Lazy<Scene> failScene();
default Stage showHealthCheckWindow() {
Stage stage = window();
// TODO reevaluate config loading, as soon as we have the new generic error screen
var unverifiedConf = loadConfig();
if (unverifiedConf.config() != null) {
stage.setScene(startScene().get());
} else {
stage.setScene(failScene().get());
}
stage.setScene(startScene().get());
stage.show();
return stage;
}
@@ -52,5 +41,4 @@ public interface HealthCheckComponent {
HealthCheckComponent build();
}
record LoadUnverifiedConfigResult(VaultConfig.UnverifiedVaultConfig config, Throwable error) {}
}

View File

@@ -27,7 +27,6 @@ import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -37,18 +36,6 @@ import java.util.concurrent.atomic.AtomicReference;
@Module(subcomponents = {KeyLoadingComponent.class})
abstract class HealthCheckModule {
// TODO reevaluate config loading, as soon as we have the new generic error screen
@Provides
@HealthCheckScoped
static HealthCheckComponent.LoadUnverifiedConfigResult provideLoadConfigResult(@HealthCheckWindow Vault vault) {
try {
return new HealthCheckComponent.LoadUnverifiedConfigResult(vault.getUnverifiedVaultConfig(), null);
} catch (IOException e) {
return new HealthCheckComponent.LoadUnverifiedConfigResult(null, e);
}
}
@Provides
@HealthCheckScoped
static AtomicReference<Masterkey> provideMasterkeyRef() {
@@ -129,13 +116,6 @@ abstract class HealthCheckModule {
return fxmlLoaders.createScene(FxmlFile.HEALTH_START);
}
@Provides
@FxmlScene(FxmlFile.HEALTH_START_FAIL)
@HealthCheckScoped
static Scene provideHealthStartFailScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HEALTH_START_FAIL);
}
@Provides
@FxmlScene(FxmlFile.HEALTH_CHECK_LIST)
@HealthCheckScoped
@@ -148,11 +128,6 @@ abstract class HealthCheckModule {
@FxControllerKey(StartController.class)
abstract FxController bindStartController(StartController controller);
@Binds
@IntoMap
@FxControllerKey(StartFailController.class)
abstract FxController bindStartFailController(StartFailController controller);
@Binds
@IntoMap
@FxControllerKey(CheckListController.class)

View File

@@ -1,7 +1,8 @@
package org.cryptomator.ui.health;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultConfigCache;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException;
@@ -18,8 +19,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -35,7 +34,7 @@ public class StartController implements FxController {
private final Stage window;
private final Stage unlockWindow;
private final ObjectProperty<VaultConfig.UnverifiedVaultConfig> unverifiedVaultConfig;
private final VaultConfigCache vaultConfig;
private final KeyLoadingStrategy keyLoadingStrategy;
private final ExecutorService executor;
private final AtomicReference<Masterkey> masterkeyRef;
@@ -44,11 +43,10 @@ public class StartController implements FxController {
private final Lazy<ErrorComponent.Builder> errorComponent;
@Inject
public StartController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) {
Preconditions.checkNotNull(configLoadResult.config());
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) {
this.window = window;
this.unlockWindow = unlockWindow;
this.unverifiedVaultConfig = new SimpleObjectProperty<>(configLoadResult.config());
this.vaultConfig = vault.getVaultConfigCache();
this.keyLoadingStrategy = keyLoadingStrategy;
this.executor = executor;
this.masterkeyRef = masterkeyRef;
@@ -71,7 +69,6 @@ public class StartController implements FxController {
private void loadKey() {
assert !Platform.isFxApplicationThread();
assert unverifiedVaultConfig.get() != null;
try {
keyLoadingStrategy.use(this::verifyVaultConfig);
} catch (VaultConfigLoadException | UnlockCancelledException e) {
@@ -80,7 +77,7 @@ public class StartController implements FxController {
}
private void verifyVaultConfig(KeyLoadingStrategy keyLoadingStrategy) throws VaultConfigLoadException {
var unverifiedCfg = unverifiedVaultConfig.get();
var unverifiedCfg = vaultConfig.getUnchecked();
try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) {
var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion());
vaultConfigRef.set(verifiedCfg);

View File

@@ -1,79 +0,0 @@
package org.cryptomator.ui.health;
import com.google.common.base.Preconditions;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.TitledPane;
import javafx.stage.Stage;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
// TODO reevaluate config loading, as soon as we have the new generic error screen
@HealthCheckScoped
public class StartFailController implements FxController {
private final Stage window;
private final ObjectProperty<Throwable> loadError;
private final ObjectProperty<FontAwesome5Icon> moreInfoIcon;
/* FXML */
public TitledPane moreInfoPane;
@Inject
public StartFailController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult) {
Preconditions.checkNotNull(configLoadResult.error());
this.window = window;
this.loadError = new SimpleObjectProperty<>(configLoadResult.error());
this.moreInfoIcon = new SimpleObjectProperty<>(FontAwesome5Icon.CARET_RIGHT);
}
public void initialize() {
moreInfoPane.expandedProperty().addListener(this::setMoreInfoIcon);
}
private void setMoreInfoIcon(ObservableValue<? extends Boolean> observable, boolean wasExpanded, boolean willExpand) {
moreInfoIcon.set(willExpand ? FontAwesome5Icon.CARET_DOWN : FontAwesome5Icon.CARET_RIGHT);
}
@FXML
public void close() {
window.close();
}
/* Getter & Setter */
public ObjectProperty<FontAwesome5Icon> moreInfoIconProperty() {
return moreInfoIcon;
}
public FontAwesome5Icon getMoreInfoIcon() {
return moreInfoIcon.getValue();
}
public String getStackTrace() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
loadError.get().printStackTrace(new PrintStream(baos));
return baos.toString(StandardCharsets.UTF_8);
}
public String getLocalizedErrorMessage() {
return loadError.get().getLocalizedMessage();
}
public boolean isParseException() {
return loadError.get() instanceof VaultConfigLoadException;
}
public boolean isIoException() {
return !isParseException();
}
}

View File

@@ -3,7 +3,6 @@ package org.cryptomator.ui.keyloading;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlLoaderFactory;
@@ -12,9 +11,7 @@ import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule;
import javax.inject.Provider;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
@Module(includes = {MasterkeyFileLoadingModule.class, HubKeyLoadingModule.class})
@@ -32,7 +29,7 @@ abstract class KeyLoadingModule {
@KeyLoadingScoped
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map<String, Provider<KeyLoadingStrategy>> strategies) {
try {
String scheme = vault.getUnverifiedVaultConfig().getKeyId().getScheme();
String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme();
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
return strategies.getOrDefault(scheme, () -> fallback).get();
} catch (IOException e) {

View File

@@ -77,6 +77,7 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
boolean success = false;
try {
user.use(this);
success = true;
} catch (MasterkeyLoadingFailedException e) {
if (recoverFromException(e)) {
LOG.info("Unlock attempt threw {}. Reattempting...", e.getClass().getSimpleName());

View File

@@ -40,7 +40,7 @@ public abstract class HubKeyLoadingModule {
@KeyLoadingScoped
static HubConfig provideHubConfig(@KeyLoading Vault vault) {
try {
return vault.getUnverifiedVaultConfig().getHeader("hub", HubConfig.class);
return vault.getVaultConfigCache().get().getHeader("hub", HubConfig.class);
} catch (IOException e) {
throw new UncheckedIOException(e);
}

View File

@@ -150,7 +150,7 @@ public class ReceiveKeyController implements FxController {
private static URI getVaultBaseUri(Vault vault) {
try {
var kid = vault.getUnverifiedVaultConfig().getKeyId();
var kid = vault.getVaultConfigCache().get().getKeyId();
assert kid.getScheme().startsWith(SCHEME_PREFIX);
var hubUriScheme = kid.getScheme().substring(SCHEME_PREFIX.length());
return new URI(hubUriScheme, kid.getSchemeSpecificPart(), kid.getFragment());

View File

@@ -11,12 +11,11 @@ import javax.inject.Named;
import javax.inject.Singleton;
import javafx.application.Platform;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
@Singleton
class AppLaunchEventHandler {
@@ -69,7 +68,7 @@ class AppLaunchEventHandler {
assert Platform.isFxApplicationThread();
try {
final Vault v;
if (potentialVaultPath.getFileName().toString().equals(MASTERKEY_FILENAME)) {
if (potentialVaultPath.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) {
v = vaultListManager.add(potentialVaultPath.getParent());
} else {
v = vaultListManager.add(potentialVaultPath);

View File

@@ -2,6 +2,7 @@ package org.cryptomator.ui.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
@@ -33,21 +34,21 @@ public abstract class UiLauncherModule {
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
return ServiceLoader.load(UiAppearanceProvider.class).findFirst();
static Optional<UiAppearanceProvider> provideAppearanceProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
}
@Provides
@Singleton
static Optional<AutoStartProvider> provideAutostartProvider() {
return ServiceLoader.load(AutoStartProvider.class).findFirst();
static Optional<AutoStartProvider> provideAutostartProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
}
@Provides
@Singleton
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
return ServiceLoader.load(TrayIntegrationProvider.class).findFirst();
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
}
@Provides

View File

@@ -23,12 +23,11 @@ import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
@@ -55,7 +54,7 @@ public class MainWindowController implements FxController {
@FXML
public void initialize() {
LOG.debug("init MainWindowController");
LOG.trace("init MainWindowController");
root.setOnDragEntered(this::handleDragEvent);
root.setOnDragOver(this::handleDragEvent);
root.setOnDragDropped(this::handleDragEvent);
@@ -96,6 +95,9 @@ public class MainWindowController implements FxController {
private boolean containsVault(Path path) {
try {
if (path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) {
path = path.getParent();
}
return CryptoFileSystemProvider.checkDirStructureForVault(path, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) != DirStructure.UNRELATED;
} catch (IOException e) {
return false;
@@ -104,7 +106,7 @@ public class MainWindowController implements FxController {
private void addVault(Path pathToVault) {
try {
if (pathToVault.getFileName().toString().equals(VAULTCONFIG_FILENAME)) {
if (pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) {
vaultListManager.add(pathToVault.getParent());
} else {
vaultListManager.add(pathToVault);

View File

@@ -6,29 +6,32 @@ import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.ErrorComponent;
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.StageFactory;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
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;
import javafx.stage.StageStyle;
import java.util.Map;
import java.util.ResourceBundle;
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class})
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
abstract class MainWindowModule {
@Provides
@@ -49,15 +52,23 @@ abstract class MainWindowModule {
@MainWindowScoped
static Stage provideStage(StageFactory factory) {
Stage stage = factory.create(StageStyle.UNDECORATED);
// TODO: min/max values chosen arbitrarily. We might wanna take a look at the user's resolution...
stage.setMinWidth(650);
stage.setMinHeight(440);
stage.setMaxWidth(1000);
stage.setMaxHeight(700);
stage.setTitle("Cryptomator");
return stage;
}
@Provides
@MainWindowScoped
@Named("errorWindow")
static Stage provideErrorStage(@MainWindow Stage window, StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create(StageStyle.DECORATED);
stage.setTitle(resourceBundle.getString("main.vaultDetail.error.windowTitle"));
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(window);
return stage;
}
@Provides
@FxmlScene(FxmlFile.MAIN_WINDOW)
@MainWindowScoped

View File

@@ -16,6 +16,7 @@ import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
@@ -53,22 +54,43 @@ public class MainWindowTitleController implements FxController {
@FXML
public void initialize() {
LOG.debug("init MainWindowTitleController");
LOG.trace("init MainWindowTitleController");
updateChecker.automaticallyCheckForUpdatesIfEnabled();
titleBar.setOnMousePressed(event -> {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
});
titleBar.setOnMouseClicked(event -> {
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
window.setFullScreen(!window.isFullScreen());
}
});
titleBar.setOnMouseDragged(event -> {
if (window.isFullScreen()) return;
window.setX(event.getScreenX() - xOffset);
window.setY(event.getScreenY() - yOffset);
});
titleBar.setOnDragDetected(mouseDragEvent -> {
titleBar.startFullDrag();
});
titleBar.setOnMouseDragReleased(mouseDragEvent -> {
saveWindowSettings();
});
window.setOnCloseRequest(event -> {
close();
event.consume();
});
}
private void saveWindowSettings() {
settings.windowYPositionProperty().setValue(window.getY());
settings.windowXPositionProperty().setValue(window.getX());
settings.windowWidthProperty().setValue(window.getWidth());
settings.windowHeightProperty().setValue(window.getHeight());
}
@FXML
public void close() {
if (trayMenuInitialized) {

View File

@@ -1,41 +1,100 @@
package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.stage.Screen;
import javafx.stage.Stage;
@MainWindow
public class ResizeController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ResizeController.class);
private final Stage window;
public Region tlResizer;
public Region trResizer;
public Region blResizer;
public Region brResizer;
public Region tResizer;
public Region rResizer;
public Region bResizer;
public Region lResizer;
public Region lDefaultRegion;
public Region tDefaultRegion;
public Region rDefaultRegion;
public Region bDefaultRegion;
private double origX, origY, origW, origH;
private final Settings settings;
private final BooleanBinding showResizingArrows;
@Inject
ResizeController(@MainWindow Stage window) {
ResizeController(@MainWindow Stage window, Settings settings) {
this.window = window;
// TODO inject settings and save current position and size
this.settings = settings;
this.showResizingArrows = Bindings.createBooleanBinding(this::isShowResizingArrows, window.fullScreenProperty());
}
@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);
LOG.trace("init ResizeController");
if (neverTouched()) {
settings.displayConfigurationProperty().setValue(getMonitorSizes());
return;
} else {
if (didDisplayConfigurationChange()) {
//If the position is illegal, then the window appears on the main screen in the middle of the window.
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
window.setWidth(window.getMinWidth());
window.setHeight(window.getMinHeight());
} else {
window.setHeight(settings.windowHeightProperty().get() > window.getMinHeight() ? settings.windowHeightProperty().get() : window.getMinHeight());
window.setWidth(settings.windowWidthProperty().get() > window.getMinWidth() ? settings.windowWidthProperty().get() : window.getMinWidth());
window.setX(settings.windowXPositionProperty().get());
window.setY(settings.windowYPositionProperty().get());
}
}
savePositionalSettings();
}
private boolean neverTouched() {
return (settings.windowHeightProperty().get() == 0) && (settings.windowWidthProperty().get() == 0) && (settings.windowXPositionProperty().get() == 0) && (settings.windowYPositionProperty().get() == 0);
}
private boolean didDisplayConfigurationChange() {
String currentDisplayConfiguration = getMonitorSizes();
String settingsDisplayConfiguration = settings.displayConfigurationProperty().get();
boolean configurationHasChanged = !settingsDisplayConfiguration.equals(currentDisplayConfiguration);
if (configurationHasChanged) settings.displayConfigurationProperty().setValue(currentDisplayConfiguration);
return configurationHasChanged;
}
private String getMonitorSizes() {
ObservableList<Screen> screens = Screen.getScreens();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < screens.size(); i++) {
Rectangle2D screenBounds = screens.get(i).getBounds();
if (!sb.isEmpty()) sb.append(" ");
sb.append("displayId: " + i + ", " + screenBounds.getWidth() + "x" + screenBounds.getHeight() + ";");
}
return sb.toString();
}
private void startResize(MouseEvent evt) {
@@ -45,27 +104,33 @@ public class ResizeController implements FxController {
origH = window.getHeight();
}
@FXML
private void resizeTopLeft(MouseEvent evt) {
resizeTop(evt);
resizeLeft(evt);
}
@FXML
private void resizeTopRight(MouseEvent evt) {
resizeTop(evt);
resizeRight(evt);
}
@FXML
private void resizeBottomLeft(MouseEvent evt) {
resizeBottom(evt);
resizeLeft(evt);
}
@FXML
private void resizeBottomRight(MouseEvent evt) {
resizeBottom(evt);
resizeRight(evt);
}
@FXML
private void resizeTop(MouseEvent evt) {
startResize(evt);
double newY = evt.getScreenY();
double dy = newY - origY;
double newH = origH - dy;
@@ -75,7 +140,9 @@ public class ResizeController implements FxController {
}
}
@FXML
private void resizeLeft(MouseEvent evt) {
startResize(evt);
double newX = evt.getScreenX();
double dx = newX - origX;
double newW = origW - dx;
@@ -85,6 +152,7 @@ public class ResizeController implements FxController {
}
}
@FXML
private void resizeBottom(MouseEvent evt) {
double newH = evt.getSceneY();
if (newH < window.getMaxHeight() && newH > window.getMinHeight()) {
@@ -92,6 +160,7 @@ public class ResizeController implements FxController {
}
}
@FXML
private void resizeRight(MouseEvent evt) {
double newW = evt.getSceneX();
if (newW < window.getMaxWidth() && newW > window.getMinWidth()) {
@@ -99,4 +168,21 @@ public class ResizeController implements FxController {
}
}
}
@FXML
public void savePositionalSettings() {
settings.windowHeightProperty().setValue(window.getHeight());
settings.windowWidthProperty().setValue(window.getWidth());
settings.windowYPositionProperty().setValue(window.getY());
settings.windowXPositionProperty().setValue(window.getX());
}
public BooleanBinding showResizingArrowsProperty() {
return showResizingArrows;
}
public boolean isShowResizingArrows() {
//If in fullscreen resizing is not be possible;
return !window.isFullScreen();
}
}

View File

@@ -2,45 +2,46 @@ package org.cryptomator.ui.mainwindow;
import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.binding.Binding;
import javafx.beans.property.ObjectProperty;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@MainWindowScoped
public class VaultDetailUnknownErrorController implements FxController {
private final Binding<String> stackTrace;
private final ObjectProperty<Vault> vault;
private final ErrorComponent.Builder errorComponentBuilder;
private final Stage errorWindow;
private final RemoveVaultComponent.Builder removeVault;
@Inject
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault) {
this.stackTrace = EasyBind.select(vault) //
.selectObject(Vault::lastKnownExceptionProperty) //
.map(this::provideStackTrace);
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault, ErrorComponent.Builder errorComponentBuilder, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
this.vault = vault;
this.errorComponentBuilder = errorComponentBuilder;
this.errorWindow = errorWindow;
this.removeVault = removeVault;
}
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 "";
}
@FXML
public void showError() {
errorComponentBuilder.window(errorWindow).cause(vault.get().getLastKnownException()).build().showErrorScene();
}
/* Getter/Setter */
public Binding<String> stackTraceProperty() {
return stackTrace;
@FXML
public void reload() {
VaultListManager.redetermineVaultState(vault.get());
}
public String getStackTrace() {
return stackTrace.getValue();
@FXML
void didClickRemoveVault() {
removeVault.vault(vault.get()).build().showRemoveVault();
}
}

View File

@@ -42,7 +42,6 @@ abstract class PreferencesModule {
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("preferences.title"));
stage.setResizable(false);
return stage;
}

View File

@@ -2,9 +2,6 @@ package org.cryptomator.ui.traymenu;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,38 +9,25 @@ import javax.inject.Inject;
import java.awt.AWTException;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.util.Optional;
@TrayMenuScoped
public class TrayIconController {
private static final Logger LOG = LoggerFactory.getLogger(TrayIconController.class);
private final TrayImageFactory imageFactory;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final TrayMenuController trayMenuController;
private final TrayIcon trayIcon;
private volatile boolean initialized;
@Inject
TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController, Optional<UiAppearanceProvider> appearanceProvider) {
TrayIconController(TrayImageFactory imageFactory, TrayMenuController trayMenuController) {
this.trayMenuController = trayMenuController;
this.imageFactory = imageFactory;
this.appearanceProvider = appearanceProvider;
this.trayIcon = new TrayIcon(imageFactory.loadImage(), "Cryptomator", trayMenuController.getMenu());
}
public synchronized void initializeTrayIcon() throws IllegalStateException {
Preconditions.checkState(!initialized);
appearanceProvider.ifPresent(appearanceProvider -> {
try {
appearanceProvider.addListener(this::systemInterfaceThemeChanged);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic tray icon theme switching.");
}
});
trayIcon.setImageAutoSize(true);
if (SystemUtils.IS_OS_WINDOWS) {
trayIcon.addActionListener(trayMenuController::showMainWindow);
@@ -61,10 +45,6 @@ public class TrayIconController {
this.initialized = true;
}
private void systemInterfaceThemeChanged(Theme theme) {
trayIcon.setImage(imageFactory.loadImage()); // TODO refactor "theme" is re-queried in loadImage()
}
public boolean isInitialized() {
return initialized;
}

View File

@@ -25,11 +25,7 @@ class TrayImageFactory {
}
private String getMacResourceName() {
var theme = appearanceProvider.map(UiAppearanceProvider::getSystemTheme).orElse(Theme.LIGHT);
return switch (theme) {
case DARK -> "/img/tray_icon_mac_white.png";
case LIGHT -> "/img/tray_icon_mac_black.png";
};
return "/img/tray_icon_mac.png";
}
private String getWinOrLinuxResourceName() {

View File

@@ -27,6 +27,7 @@ import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.io.File;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.Set;
@@ -94,7 +95,9 @@ public class MountOptionsController implements FxController {
driveLetterSelection.setConverter(new WinDriveLetterLabelConverter(windowsDriveLetters, resourceBundle));
driveLetterSelection.setValue(vault.getVaultSettings().winDriveLetter().get());
if (vault.getVaultSettings().useCustomMountPath().get() && !getRestrictToStableFuseOnWindows() /* to prevent invalid states */) {
if (vault.getVaultSettings().useCustomMountPath().get()
&& vault.getVaultSettings().getCustomMountPath().isPresent()
&& !getRestrictToStableFuseOnWindows() /* to prevent invalid states */) {
mountPoint.selectToggle(mountPointCustomDir);
} else if (!Strings.isNullOrEmpty(vault.getVaultSettings().winDriveLetter().get())) {
mountPoint.selectToggle(mountPointWinDriveLetter);
@@ -125,25 +128,30 @@ public class MountOptionsController implements FxController {
}
@FXML
private void chooseCustomMountPoint() {
public void chooseCustomMountPoint() {
chooseCustomMountPointOrReset(mountPointCustomDir);
}
private void chooseCustomMountPointOrReset(Toggle previousMountToggle) {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle(resourceBundle.getString("vaultOptions.mount.mountPoint.directoryPickerTitle"));
try {
directoryChooser.setInitialDirectory(Path.of(System.getProperty("user.home")).toFile());
} catch (Exception e) {
//NO-OP
var initialDir = vault.getVaultSettings().getCustomMountPath().orElse(System.getProperty("user.home"));
directoryChooser.setInitialDirectory(Path.of(initialDir).toFile());
} catch (InvalidPathException e) {
// no-op
}
File file = directoryChooser.showDialog(window);
if (file != null) {
vault.getVaultSettings().customMountPath().set(file.getAbsolutePath());
} else {
vault.getVaultSettings().customMountPath().set(null);
mountPoint.selectToggle(previousMountToggle);
}
}
private void toggleMountPoint(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
private void toggleMountPoint(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
if (mountPointCustomDir.equals(newValue) && Strings.isNullOrEmpty(vault.getVaultSettings().customMountPath().get())) {
chooseCustomMountPoint();
chooseCustomMountPointOrReset(oldValue);
}
}

View File

@@ -21,8 +21,4 @@ public enum SelectedVaultOptionsTab {
*/
KEY,
/**
* Show Auto-Lock tab
*/
AUTOLOCK,
}

View File

@@ -23,7 +23,6 @@ public class VaultOptionsController implements FxController {
public Tab generalTab;
public Tab mountTab;
public Tab keyTab;
public Tab autoLockTab;
@Inject
VaultOptionsController(@VaultOptionsWindow Stage window, ObjectProperty<SelectedVaultOptionsTab> selectedTabProperty) {
@@ -48,7 +47,6 @@ public class VaultOptionsController implements FxController {
case ANY, GENERAL -> generalTab;
case MOUNT -> mountTab;
case KEY -> keyTab;
case AUTOLOCK -> autoLockTab;
};
}

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
@@ -15,7 +18,7 @@
fx:controller="org.cryptomator.ui.common.ErrorController"
prefWidth="450"
prefHeight="450"
spacing="12"
spacing="18"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="24"/>
@@ -27,12 +30,38 @@
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
</StackPane>
<VBox spacing="6" HBox.hgrow="ALWAYS">
<Label text="%generic.error.title" wrapText="true"/>
<FormattedLabel styleClass="label-large" format="%generic.error.title" arg1="${controller.errorCode}"/>
<Label text="%generic.error.instruction" wrapText="true"/>
<Hyperlink styleClass="hyperlink-underline" text="%generic.error.hyperlink.lookup" onAction="#searchError" contentDisplay="LEFT">
<graphic>
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
</graphic>
</Hyperlink>
<Hyperlink styleClass="hyperlink-underline" text="%generic.error.hyperlink.report" onAction="#reportError" contentDisplay="LEFT">
<graphic>
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
</graphic>
</Hyperlink>
</VBox>
</HBox>
<TextArea VBox.vgrow="ALWAYS" text="${controller.stackTrace}" prefRowCount="5" editable="false"/>
<VBox spacing="6" VBox.vgrow="ALWAYS">
<HBox>
<Label text="%generic.error.technicalDetails"/>
<Region HBox.hgrow="ALWAYS"/>
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
<graphic>
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="12"/>
</graphic>
</Hyperlink>
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copied" onAction="#copyDetails" contentDisplay="LEFT" visible="${controller.copiedDetails}" managed="${controller.copiedDetails}">
<graphic>
<FontAwesome5IconView glyph="CHECK" glyphSize="12"/>
</graphic>
</Hyperlink>
</HBox>
<TextArea VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
</VBox>
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
<buttons>

View File

@@ -3,7 +3,6 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
@@ -27,7 +26,7 @@
<ListView fx:id="checksListView" VBox.vgrow="ALWAYS" minWidth="175" maxWidth="175"/>
<VBox alignment="CENTER" visible="${!controller.mainRunStarted}" managed="${!controller.mainRunStarted}" HBox.hgrow="ALWAYS" spacing="12">
<Label text="%health.checkList.description" wrapText="true"/>
<HBox alignment="CENTER">
<HBox alignment="CENTER" spacing="6">
<Button onAction="#selectAllChecks" text="%health.checkList.selectAllButton" />
<Button onAction="#deselectAllChecks" text="%health.checkList.deselectAllButton" />
</HBox>

View File

@@ -18,7 +18,7 @@
</padding>
<StackPane minWidth="20" minHeight="20" alignment="CENTER">
<CheckBox fx:id="forRunSelectedCheckBox" visible="${controller.checkRunnable}" />
<CheckBox fx:id="checkbox" visible="${controller.checkRunnable}"/>
<CheckStateIconView check="${controller.check}" glyphSize="20" visible="${!controller.checkRunnable}"/>
</StackPane>
<Label text="${controller.checkName}"/>

View File

@@ -1,44 +0,0 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TitledPane?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.TextFlow?>
<?import javafx.scene.text.Text?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.health.StartFailController"
prefWidth="600"
prefHeight="400"
spacing="12">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<Label text="%health.fail.header" styleClass="label-large" />
<TextFlow fx:id="ioErrorLabel" visible="${controller.ioException}" managed="${controller.ioException}">
<Text text="%health.fail.ioError" />
<Text text="${controller.localizedErrorMessage}"/>
</TextFlow>
<Label fx:id="parseErrorLabel" text="%health.fail.parseError" visible="${controller.parseException}" managed="${controller.parseException}"/>
<TitledPane fx:id="moreInfoPane" text="%health.fail.moreInfo" expanded="false">
<graphic>
<HBox alignment="CENTER" minWidth="8">
<FontAwesome5IconView glyph="${controller.moreInfoIcon}"/>
</HBox>
</graphic>
<content>
<TextArea VBox.vgrow="ALWAYS" text="${controller.stackTrace}" prefRowCount="20" editable="false" />
</content>
</TitledPane>
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
</buttons>
</ButtonBar>
</VBox>

View File

@@ -12,7 +12,7 @@
fx:id="root"
fx:controller="org.cryptomator.ui.mainwindow.MainWindowController"
styleClass="main-window">
<VBox>
<VBox minWidth="650">
<fx:include source="main_window_title.fxml" VBox.vgrow="NEVER"/>
<StackPane VBox.vgrow="ALWAYS">
<SplitPane dividerPositions="0.33" orientation="HORIZONTAL">

Some files were not shown because too many files have changed in this diff Show More