Compare commits

..
Author SHA1 Message Date
Armin Schrenk 1fb987607c use single method to initialize JavaFX 2025-01-21 17:03:42 +01:00
Armin Schrenk 5d4066d29a fix test 2025-01-21 16:01:49 +01:00
Armin Schrenk d3909134d5 actually use authentication 2025-01-21 15:55:47 +01:00
Armin Schrenk fd9d1bf0cf fix unit test 2025-01-16 10:49:57 +01:00
Armin Schrenk 2f812377c3 bump integrations-win to 1.4.1 to fix jvm crashes 2025-01-16 10:31:38 +01:00
Armin Schrenk ebce90eb74 weakValues are too agressive 2025-01-15 10:02:42 +01:00
Armin Schrenk c888b52ebb rely on the cache directly after unlock and inject in the cache on keychain changes 2025-01-15 10:02:21 +01:00
Armin Schrenk e422c7ce4b use caffeine for building a cache
(cherry picked from commit fc709eb700)
2025-01-15 09:38:50 +01:00
Armin Schrenk 41ad8d5dcc response in th ui to keychain impl change 2025-01-14 18:42:08 +01:00
Armin Schrenk 526c3973d6 bump dependencies to add biometric auth providers
* integrations-win to 1.4.0
* integrations-mac to 1.3.0
2025-01-14 15:42:10 +01:00
Armin Schrenk dc16c961af only store password, if it is not already stored 2025-01-14 15:41:17 +01:00
Ralph Plawetzki c54a603078 Merge branch 'develop' into 2ndfactor 2025-01-11 17:59:54 +01:00
Ralph Plawetzki be59097a53 Merge branch 'develop' into 2ndfactor 2024-12-14 07:37:30 +01:00
Ralph Plawetzki ccc4842839 Use widened integrations-api 2024-12-02 08:16:28 +01:00
Ralph Plawetzki d95625faa1 Revert "Add CheckBox to enable Touch ID per vault"
This reverts commit cfa3093dd0.
2024-12-02 08:12:03 +01:00
Ralph Plawetzki c5bfdf32a9 Revert "Save Touch ID setting per vault"
This reverts commit 10bce1fb06.
2024-12-02 08:12:03 +01:00
Ralph Plawetzki 0598681a23 Revert "Decide on Touch ID setting whether user needs"
This reverts commit 2194360c8a.
2024-12-02 08:12:03 +01:00
Ralph Plawetzki 3b8bff0d47 Revert "loadPassphraseForAuthenticatedUser is not needed to"
This reverts commit 2378227756.
2024-12-02 08:12:02 +01:00
Ralph Plawetzki 55051072cb Revert "Actually save preference on checkbox change"
This reverts commit 62827b69cc.
2024-12-02 08:12:02 +01:00
Ralph Plawetzki 6cb409eeaf Revert "Grab new API"
This reverts commit 9516928529.
2024-12-02 08:12:02 +01:00
Ralph Plawetzki b38a350c48 Revert "Remove log statement used for testing"
This reverts commit 14ba852351.
2024-12-02 08:12:02 +01:00
Ralph Plawetzki 0e5857015a Revert "Change existing keychain entry"
This reverts commit 9cc863ae79.
2024-12-02 08:12:02 +01:00
Ralph Plawetzki 9e9aa20692 Revert "Disable feature for other platforms than Mac"
This reverts commit d8b798ff0f.
2024-12-02 08:12:01 +01:00
Ralph Plawetzki ad49ca2cb4 Revert "Generalize naming"
This reverts commit 953aee560f.
2024-12-02 08:12:01 +01:00
Ralph Plawetzki ba84da71f3 Revert "Code cleanups following coderabbitai recommendations"
This reverts commit af3779ba2e.
2024-12-02 08:12:01 +01:00
Ralph Plawetzki 5c34f59d09 Revert "Don't use SNAPSHOTS"
This reverts commit 175ed500a2.
2024-12-02 08:12:01 +01:00
Ralph Plawetzki 7499867665 Revert "Use overloaded method storePassword instead of a new one"
This reverts commit aa34ad52e6.
2024-12-02 08:12:00 +01:00
Ralph Plawetzki 7b293b8082 Revert "New, widened integrations-api"
This reverts commit 2f1b5109d6.
2024-12-02 08:12:00 +01:00
Ralph Plawetzki 46e8a13555 Revert "Improve thread handling"
This reverts commit c8075dbc19.
2024-12-02 08:11:01 +01:00
Ralph Plawetzki 63a3150ec3 Revert "Remove unneeded saving a keychain entry right after"
This reverts commit e8126e68ce.
2024-12-02 08:11:01 +01:00
Ralph Plawetzki 0a78987d30 Revert "Fix typo"
This reverts commit 78675c9638.
2024-12-02 08:11:00 +01:00
Ralph Plawetzki 929782c318 Revert "Fix doubled method due to merge error"
This reverts commit b5dbfd3209.
2024-12-02 08:11:00 +01:00
Ralph Plawetzki 97a0f9c435 Migrate keychain entries on Mac on provider change 2024-12-02 07:56:23 +01:00
Ralph Plawetzki f7e65f4eec Add cryptomator.integrationsWin.windowsHelloKeychainPaths to Environment 2024-11-30 14:39:56 +01:00
Ralph Plawetzki b3c56f3aab Add Windows Hello provider 2024-11-30 12:25:36 +01:00
Ralph Plawetzki b5dbfd3209 Fix doubled method due to merge error 2024-11-24 18:14:26 +01:00
Ralph Plawetzki 78675c9638 Fix typo 2024-11-24 15:02:06 +01:00
Ralph Plawetzki e8126e68ce Remove unneeded saving a keychain entry right after
it was loaded
2024-11-24 15:02:06 +01:00
Ralph Plawetzki c8075dbc19 Improve thread handling 2024-11-24 15:02:05 +01:00
Ralph Plawetzki 2f1b5109d6 New, widened integrations-api 2024-11-24 15:01:44 +01:00
Ralph Plawetzki aa34ad52e6 Use overloaded method storePassword instead of a new one 2024-11-24 15:00:54 +01:00
Ralph Plawetzki 175ed500a2 Don't use SNAPSHOTS 2024-11-24 14:57:16 +01:00
Ralph Plawetzki af3779ba2e Code cleanups following coderabbitai recommendations 2024-11-24 14:56:38 +01:00
Ralph Plawetzki 953aee560f Generalize naming 2024-11-24 14:56:38 +01:00
Ralph Plawetzki d8b798ff0f Disable feature for other platforms than Mac 2024-11-24 14:56:38 +01:00
Ralph Plawetzki 9cc863ae79 Change existing keychain entry 2024-11-24 14:56:38 +01:00
Ralph Plawetzki 14ba852351 Remove log statement used for testing 2024-11-24 14:56:37 +01:00
Ralph Plawetzki 9516928529 Grab new API 2024-11-24 14:56:30 +01:00
Ralph Plawetzki 62827b69cc Actually save preference on checkbox change 2024-11-24 14:48:32 +01:00
Ralph Plawetzki 2378227756 loadPassphraseForAuthenticatedUser is not needed to
load as an authenticated user; loadPassphrase is
sufficient
2024-11-24 14:48:32 +01:00
Ralph Plawetzki 2194360c8a Decide on Touch ID setting whether user needs
to authenticate on loading and storing a passphrase
2024-11-24 14:48:32 +01:00
Ralph Plawetzki 10bce1fb06 Save Touch ID setting per vault 2024-11-24 14:48:32 +01:00
Ralph Plawetzki cfa3093dd0 Add CheckBox to enable Touch ID per vault 2024-11-24 14:48:31 +01:00
161 changed files with 883 additions and 3056 deletions
+9 -9
View File
@@ -1,14 +1,7 @@
name: Bug Report name: Bug Report
description: Create a report to help us improve description: Create a report to help us improve
type: "Bug" labels: ["type:bug"]
body: body:
- type: input
id: summary
attributes:
label: Summary
placeholder: Please summarize your problem.
validations:
required: true
- type: checkboxes - type: checkboxes
id: terms id: terms
attributes: attributes:
@@ -18,6 +11,13 @@ body:
required: true required: true
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
required: true required: true
- type: input
id: summary
attributes:
label: Summary
placeholder: Please summarize your problem.
validations:
required: true
- type: textarea - type: textarea
id: software-versions id: software-versions
attributes: attributes:
@@ -97,4 +97,4 @@ body:
id: further-info id: further-info
attributes: attributes:
label: Anything else? label: Anything else?
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
+8 -8
View File
@@ -1,14 +1,7 @@
name: Feature Request name: Feature Request
description: Suggest an idea for this project description: Suggest an idea for this project
type: "Feature" labels: ["type:feature-request"]
body: body:
- type: input
id: summary
attributes:
label: Summary
placeholder: Please summarize your feature request.
validations:
required: true
- type: checkboxes - type: checkboxes
id: terms id: terms
attributes: attributes:
@@ -18,6 +11,13 @@ body:
required: true required: true
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
required: true required: true
- type: input
id: summary
attributes:
label: Summary
placeholder: Please summarize your feature request.
validations:
required: true
- type: textarea - type: textarea
id: motivation id: motivation
attributes: attributes:
+12 -13
View File
@@ -10,7 +10,7 @@ on:
required: false required: false
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: '23.0.1+11' JAVA_VERSION: '23.0.1+11'
jobs: jobs:
@@ -29,12 +29,12 @@ jobs:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
appimage-suffix: x86_64 appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' openjfx-sha: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
- os: ubuntu-24.04-arm - os: [self-hosted, Linux, ARM64]
appimage-suffix: aarch64 appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' openjfx-sha: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
@@ -98,7 +98,7 @@ jobs:
--dest appdir --dest appdir
--name Cryptomator --name Cryptomator
--vendor "Skymatic GmbH" --vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH" --copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" --java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
@@ -116,7 +116,6 @@ jobs:
--java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
--resource-dir dist/linux/resources --resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir - name: Patch Cryptomator.AppDir
run: | run: |
@@ -133,13 +132,13 @@ jobs:
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
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/org.cryptomator.Cryptomator.svg
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/Cryptomator.svg
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit - name: Download AppImageKit
run: | run: |
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
chmod +x appimagetool.AppImage chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract ./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235 - name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -153,7 +152,7 @@ jobs:
run: > run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync' -u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Create detached GPG signatures - name: Create detached GPG signatures
run: | run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
@@ -176,4 +175,4 @@ jobs:
files: | files: |
cryptomator-*.AppImage cryptomator-*.AppImage
cryptomator-*.zsync cryptomator-*.zsync
cryptomator-*.asc cryptomator-*.asc
+1 -1
View File
@@ -6,7 +6,7 @@ on:
types: [labeled] types: [labeled]
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: 23 JAVA_VERSION: 23
defaults: defaults:
+1 -1
View File
@@ -7,7 +7,7 @@ on:
env: env:
JDK_VERSION: '23.0.1+11' JDK_VERSION: '23.0.1+11'
JDK_VENDOR: temurin JDK_VENDOR: zulu
RUNTIME_VERSION_HELPER: > RUNTIME_VERSION_HELPER: >
public class Test { public class Test {
public static void main(String[] args) { public static void main(String[] args) {
+6 -6
View File
@@ -16,19 +16,19 @@ on:
type: boolean type: boolean
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: '23.0.1+11' JAVA_VERSION: '23.0.1+11'
COFFEELIBS_JDK: 23 COFFEELIBS_JDK: 23
COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1' COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' OPENJFX_JMODS_AMD64_HASH: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip' OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' OPENJFX_JMODS_AARCH64_HASH: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
jobs: jobs:
build: build:
name: Build Debian Package name: Build Debian Package
runs-on: ubuntu-22.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- id: versions - id: versions
+1 -1
View File
@@ -22,7 +22,7 @@ on:
value: ${{ jobs.determine-version.outputs.type }} value: ${{ jobs.determine-version.outputs.type }}
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: 23 JAVA_VERSION: 23
jobs: jobs:
+4 -4
View File
@@ -14,7 +14,7 @@ on:
types: [published] types: [published]
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: '23.0.1+11' JAVA_VERSION: '23.0.1+11'
jobs: jobs:
@@ -35,8 +35,8 @@ jobs:
architecture: x64 architecture: x64
output-suffix: x64 output-suffix: x64
fuse-lib: macFUSE fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-x64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-x64_bin-jmods.zip'
openjfx-sha: '8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e' openjfx-sha: '115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
@@ -100,7 +100,7 @@ jobs:
--dest appdir --dest appdir
--name Cryptomator --name Cryptomator
--vendor "Skymatic GmbH" --vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH" --copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.mac" --java-options "--enable-native-access=org.cryptomator.jfuse.mac"
+4 -4
View File
@@ -15,7 +15,7 @@ on:
type: boolean type: boolean
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: '23.0.1+11' JAVA_VERSION: '23.0.1+11'
jobs: jobs:
@@ -36,8 +36,8 @@ jobs:
architecture: aarch64 architecture: aarch64
output-suffix: arm64 output-suffix: arm64
fuse-lib: FUSE-T fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-aarch64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6' openjfx-sha: '813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
@@ -101,7 +101,7 @@ jobs:
--dest appdir --dest appdir
--name Cryptomator --name Cryptomator
--vendor "Skymatic GmbH" --vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH" --copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.mac" --java-options "--enable-native-access=org.cryptomator.jfuse.mac"
+1 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: 23 JAVA_VERSION: 23
defaults: defaults:
+1 -1
View File
@@ -11,7 +11,7 @@ defaults:
shell: bash shell: bash
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'zulu'
JAVA_VERSION: 23 JAVA_VERSION: 23
jobs: jobs:
+7 -6
View File
@@ -17,8 +17,8 @@ on:
env: env:
JAVA_DIST: 'zulu' JAVA_DIST: 'zulu'
JAVA_VERSION: '23.0.1+11' JAVA_VERSION: '23.0.1+11'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_windows-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3' OPENJFX_JMODS_AMD64_HASH: 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi' WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe' WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
@@ -89,7 +89,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "jfxjmods;${JAVA_HOME}/jmods" --module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -110,7 +110,7 @@ jobs:
--dest appdir --dest appdir
--name Cryptomator --name Cryptomator
--vendor "Skymatic GmbH" --vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH" --copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" --java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
@@ -130,6 +130,7 @@ jobs:
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\"" --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json\""
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}" --java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
--resource-dir dist/win/resources --resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico --icon dist/win/resources/Cryptomator.ico
@@ -218,7 +219,7 @@ jobs:
--dest installer --dest installer
--name Cryptomator --name Cryptomator
--vendor "Skymatic GmbH" --vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH" --copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
--win-menu --win-menu
--win-dir-chooser --win-dir-chooser
@@ -304,7 +305,7 @@ jobs:
-out dist/win/bundle/ -out dist/win/bundle/
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" -dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
-dBundleVendor="Skymatic GmbH" -dBundleVendor="Skymatic GmbH"
-dBundleCopyright="(C) 2016 - 2025 Skymatic GmbH" -dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
-dAboutUrl="https://cryptomator.org" -dAboutUrl="https://cryptomator.org"
-dHelpUrl="https://cryptomator.org/contact" -dHelpUrl="https://cryptomator.org/contact"
-dUpdateUrl="https://cryptomator.org/downloads/" -dUpdateUrl="https://cryptomator.org/downloads/"
+8 -9
View File
@@ -14,16 +14,17 @@
<option name="dagger.fastInit" value="enabled" /> <option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" /> <option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.55/dagger-compiler-2.55.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.52/dagger-compiler-2.52.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.55/dagger-2.55.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.52/dagger-2.52.jar" />
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" /> <entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" /> <entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.52/dagger-spi-2.52.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.0.21-1.0.28/symbol-processing-api-2.0.21-1.0.28.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.24-1.0.20/symbol-processing-api-1.9.24-1.0.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar" /> <entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -34,8 +35,6 @@
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" /> <entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" /> <entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" /> <entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" /> <entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
@@ -46,7 +45,7 @@
</component> </component>
<component name="JavacSettings"> <component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE"> <option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" /> <module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled --enable-preview" />
</option> </option>
</component> </component>
</project> </project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" /> <option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" /> <option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+7 -7
View File
@@ -1,9 +1,9 @@
[![cryptomator](cryptomator.png)](https://cryptomator.org/) [![cryptomator](cryptomator.png)](https://cryptomator.org/)
[![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions/workflows/build.yml?query=branch%3Adevelop) [![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
[![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator) [![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cryptomator_cryptomator&metric=alert_status)](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cryptomator_cryptomator&metric=alert_status)](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
[![Mastodon](https://img.shields.io/mastodon/follow/176112?domain=mastodon.online&style=flat)](https://mastodon.online/@cryptomator) [![Twitter](https://img.shields.io/badge/twitter-@Cryptomator-blue.svg?style=flat)](http://twitter.com/Cryptomator)
[![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/) [![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/)
[![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest) [![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest)
[![Community](https://img.shields.io/badge/help-Community-orange.svg)](https://community.cryptomator.org) [![Community](https://img.shields.io/badge/help-Community-orange.svg)](https://community.cryptomator.org)
@@ -32,9 +32,9 @@ Become our Gold Sponsor and showcase your brand to a targeted audience! Please c
### Special Shoutout ### Special Shoutout
Continuous integration hosting for ARM64 builds is provided by [MacStadium](https://www.macstadium.com/company/opensource). Continuous integration hosting for ARM64 builds is provided by [MacStadium](https://www.macstadium.com/opensource).
<a href="https://www.macstadium.com/company/opensource"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="MacStadium" height="100"></a> <a href="https://www.macstadium.com/opensource"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="MacStadium" height="100"></a>
--- ---
@@ -54,7 +54,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
- File names get encrypted - File names get encrypted
- Folder structure gets obfuscated - Folder structure gets obfuscated
- Use as many vaults in your Dropbox as you want, each having individual passwords - Use as many vaults in your Dropbox as you want, each having individual passwords
- More than Five thousand commits for the security of your data!! :tada: - Four thousand commits for the security of your data!! :tada:
### Privacy ### Privacy
@@ -72,13 +72,13 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
### Security Architecture ### Security Architecture
For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/security/architecture/). For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/en/latest/security/architecture/).
## Building ## Building
### Dependencies ### Dependencies
* JDK 23 (e.g. temurin, zulu) * JDK 22 (e.g. temurin, zulu)
* Maven 3 * Maven 3
### Run Maven ### Run Maven
+6 -7
View File
@@ -25,10 +25,10 @@ cp ../../../target/cryptomator-*.jar ../../../target/mods
JAVAFX_VERSION=22.0.2 JAVAFX_VERSION=22.0.2
JAVAFX_ARCH="x64" JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256='2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' JAVAFX_JMODS_SHA256='d44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
if [ "${CPU_ARCH}" = "aarch64" ]; then if [ "${CPU_ARCH}" = "aarch64" ]; then
JAVAFX_ARCH="aarch64" JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256='09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' JAVAFX_JMODS_SHA256='3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
fi fi
# download javaFX jmods # download javaFX jmods
@@ -76,7 +76,7 @@ ${JAVA_HOME}/bin/jpackage \
--vendor "Skymatic GmbH" \ --vendor "Skymatic GmbH" \
--java-options "--enable-preview" \ --java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \ --java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \ --copyright "(C) 2016 - 2024 Skymatic GmbH" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \ --app-version "${VERSION}.${REVISION_NO}" \
@@ -91,7 +91,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.showTrayIcon=true" \ --java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \ --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--resource-dir ../resources --resource-dir ../resources
# transform AppDir # transform AppDir
@@ -109,13 +108,13 @@ cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/ap
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
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/org.cryptomator.Cryptomator.svg
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/Cryptomator.svg
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool # load AppImageTool
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage chmod +x /tmp/appimagetool.AppImage
# create AppImage # create AppImage
+6 -21
View File
@@ -1,16 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018 Armin Schrenk <armin.schrenk@zoho.eu> -->
<component type="desktop-application"> <component type="desktop-application">
<id>org.cryptomator.Cryptomator</id> <id>org.cryptomator.Cryptomator</id>
<metadata_license>FSFAP</metadata_license> <metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0-or-later</project_license> <project_license>GPL-3.0-or-later</project_license>
<name>Cryptomator</name> <name>Cryptomator</name>
<summary>Encryption for your cloud made easy</summary> <summary>Encryption made easy and optimized for the cloud</summary>
<keywords>
<keyword>encryption</keyword>
<keyword>security</keyword>
<keyword>privacy</keyword>
</keywords>
<description> <description>
<p> <p>
@@ -49,16 +44,12 @@
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>Encrypts your data, protects your privacy</caption> <caption>Light theme</caption>
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlockDialog_light.png</image> <image>https://user-images.githubusercontent.com/11858409/156986109-6e58f59c-8b8c-4501-b33b-bb1e33007cea.png</image>
</screenshot> </screenshot>
<screenshot> <screenshot>
<caption>Dark theme available</caption> <caption>Dark theme</caption>
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_dark.png</image> <image>https://user-images.githubusercontent.com/11858409/156986113-6c5d7801-86e0-4643-bc2f-aff9d95d3ce0.png</image>
</screenshot>
<screenshot>
<caption>Easy to use - work on encrypted files as if they were not</caption>
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_light.png</image>
</screenshot> </screenshot>
</screenshots> </screenshots>
@@ -83,12 +74,6 @@
</content_rating> </content_rating>
<releases> <releases>
<release date="2025-02-05" version="1.15.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.15.1</url>
</release>
<release date="2025-02-03" version="1.15.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.15.0</url>
</release>
<release date="2024-11-19" version="1.14.2"> <release date="2024-11-19" version="1.14.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.2</url> <url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.2</url>
</release> </release>
@@ -1,16 +1,12 @@
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg"> <svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
<defs> <style
<style id="current-color-scheme" type="text/css"> id="current-color-scheme" type="text/css">
.ColorScheme-Text { .ColorScheme-Text {
color:#222222; color:#232629;
} }
.ColorScheme-Highlight { </style>
color:#49B04A; <g fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor">
} <path d="m15.591 35.824c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.043c.051 1.03-.161 2.644-.509 2.834zm6.409-6.824h-2l.5-5a2 2 0 1 1 1 0zm-14.544-3.241.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm13.544-10.759a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm14.473 6a8.067 8.067 0 0 0 -8.08 8v2.141a3.891 3.891 0 0 0 -2.893 3.734v5.125a23.166 23.166 0 0 1 -4.174-1.623 7.857 7.857 0 0 1 -.027.878 3.263 3.263 0 0 1 -.729 2.074l-1.794 1.483a.379.379 0 0 1 -.276.188h-4c-1.324 0-2.346-1.336-2.653-3.343a7.058 7.058 0 0 1 .234-3.18 3.477 3.477 0 0 1 1.636-2.157 1.868 1.868 0 0 1 .783-.32h1.5a8.035 8.035 0 0 1 -1.5-5 11.1 11.1 0 0 1 .5-3 2.519 2.519 0 0 0 0-1.5 13.272 13.272 0 0 1 -.5-3.5c6.687-1.936 11 0 11 0s4.319-1.955 11 0"/>
</style> <path d="m39 28h-10v-4a3.13 3.13 0 0 1 3-3 3.087 3.087 0 0 1 3 3v1a1.034 1.034 0 0 0 1 1h1a1.034 1.034 0 0 0 1-1v-1a6 6 0 0 0 -12 0v4h-1a2.073 2.073 0 0 0 -2 2v6a2.073 2.073 0 0 0 2 2h14a2.073 2.073 0 0 0 2-2v-6a2.073 2.073 0 0 0 -2-2zm-5.391 5.94a1.609 1.609 0 0 1 -3.217 0v-1.876a1.609 1.609 0 0 1 3.217 0z"/>
</defs>
<g fill-rule="evenodd">
<path d="m15.591 35.824c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.043c.051 1.03-.161 2.644-.509 2.834zm6.409-6.824h-2l.5-5a2 2 0 1 1 1 0zm-14.544-3.241.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm13.544-10.759a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm14.473 6a8.067 8.067 0 0 0 -8.08 8v2.141a3.891 3.891 0 0 0 -2.893 3.734v5.125a23.166 23.166 0 0 1 -4.174-1.623 7.857 7.857 0 0 1 -.027.878 3.263 3.263 0 0 1 -.729 2.074l-1.794 1.483a.379.379 0 0 1 -.276.188h-4c-1.324 0-2.346-1.336-2.653-3.343a7.058 7.058 0 0 1 .234-3.18 3.477 3.477 0 0 1 1.636-2.157 1.868 1.868 0 0 1 .783-.32h1.5a8.035 8.035 0 0 1 -1.5-5 11.1 11.1 0 0 1 .5-3 2.519 2.519 0 0 0 0-1.5 13.272 13.272 0 0 1 -.5-3.5c6.687-1.936 11 0 11 0s4.319-1.955 11 0" class="ColorScheme-Text" fill="currentColor"/>
<path d="m39 28h-10v-4a3.13 3.13 0 0 1 3-3 3.087 3.087 0 0 1 3 3v1a1.034 1.034 0 0 0 1 1h1a1.034 1.034 0 0 0 1-1v-1a6 6 0 0 0 -12 0v4h-1a2.073 2.073 0 0 0 -2 2v6a2.073 2.073 0 0 0 2 2h14a2.073 2.073 0 0 0 2-2v-6a2.073 2.073 0 0 0 -2-2zm-5.391 5.94a1.609 1.609 0 0 1 -3.217 0v-1.876a1.609 1.609 0 0 1 3.217 0z" class="ColorScheme-Highlight" fill="currentColor"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

+6 -8
View File
@@ -1,10 +1,8 @@
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg"> <svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
<defs> <style id="current-color-scheme" type="text/css">
<style id="current-color-scheme" type="text/css"> .ColorScheme-Text {
.ColorScheme-Text { color:#232629;
color:#222222; }
} </style>
</style> <path d="m32.66 29.319a1.432 1.432 0 0 0 -.66-.319h-1.5a8.125 8.125 0 0 0 1.5-5 11.027 11.027 0 0 0 -.5-3 2.519 2.519 0 0 1 0-1.5 12.987 12.987 0 0 0 .5-3.5c-6.681-1.955-11 0-11 0s-4.313-1.936-11 0a13.272 13.272 0 0 0 .5 3.5 2.519 2.519 0 0 1 0 1.5 11.1 11.1 0 0 0 -.5 3 8.035 8.035 0 0 0 1.5 5h-1.5a1.868 1.868 0 0 0 -.783.319 3.477 3.477 0 0 0 -1.636 2.157 7.058 7.058 0 0 0 -.234 3.18c.307 2.008 1.329 3.344 2.653 3.344h4a.379.379 0 0 0 .277-.187l1.793-1.483a3.263 3.263 0 0 0 .729-2.074 7.857 7.857 0 0 0 .027-.878 23.166 23.166 0 0 0 4.174 1.622 24.4 24.4 0 0 0 4.051-1.614 7.848 7.848 0 0 0 .027.869 3.263 3.263 0 0 0 .729 2.074l1.793 1.484a.61.61 0 0 0 .4.187h4c1.324 0 2.223-1.336 2.529-3.343a7.057 7.057 0 0 0 -.234-3.18 3.477 3.477 0 0 0 -1.635-2.158zm-17.069 6.5c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.048c.051 1.03-.161 2.644-.509 2.834zm6.409-6.819h-2l.5-5a2 2 0 1 1 1 0zm6.38 7.921a.418.418 0 0 1 -.627.111c-.522-.433-1.439-1.2-1.458-1.208-.348-.189-.56-1.8-.505-2.828a15.84 15.84 0 0 0 2.9-2.037 9.322 9.322 0 0 1 -.31 5.962zm-20.924-11.162.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm33.217 1.2a3.021 3.021 0 0 0 -2.658-1.525 1.574 1.574 0 0 0 -.107-1.283l-.745-1.367a1.779 1.779 0 0 0 -1.317-.891l-.7-1.278a2.409 2.409 0 0 0 .229-2.661 2.283 2.283 0 0 0 -2.375-1.454 7.039 7.039 0 0 1 0 3 3.272 3.272 0 0 0 0 1.5c.047.152-.047.3 0 .5.227.04-.069.156.165.14l.653 1.2a1.579 1.579 0 0 0 -.019 1.557l.745 1.367a1.753 1.753 0 0 0 1.045.832 2.66 2.66 0 0 0 -.238 2.916 2.989 2.989 0 0 0 2.326 1.509.79.79 0 0 0 .082 0 .707.707 0 0 0 .717-.6.688.688 0 0 0 -.636-.749 1.546 1.546 0 0 1 -1.2-.78 1.408 1.408 0 0 1 .672-1.95 1.628 1.628 0 0 1 1.179-.089 1.512 1.512 0 0 1 .9.719 1.37 1.37 0 0 1 -.023 1.361.656.656 0 0 0 .284.92.748.748 0 0 0 .981-.266 2.648 2.648 0 0 0 .04-2.633zm-19.673-11.959a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2z" fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor"/>
</defs>
<path d="m32.66 29.319a1.432 1.432 0 0 0 -.66-.319h-1.5a8.125 8.125 0 0 0 1.5-5 11.027 11.027 0 0 0 -.5-3 2.519 2.519 0 0 1 0-1.5 12.987 12.987 0 0 0 .5-3.5c-6.681-1.955-11 0-11 0s-4.313-1.936-11 0a13.272 13.272 0 0 0 .5 3.5 2.519 2.519 0 0 1 0 1.5 11.1 11.1 0 0 0 -.5 3 8.035 8.035 0 0 0 1.5 5h-1.5a1.868 1.868 0 0 0 -.783.319 3.477 3.477 0 0 0 -1.636 2.157 7.058 7.058 0 0 0 -.234 3.18c.307 2.008 1.329 3.344 2.653 3.344h4a.379.379 0 0 0 .277-.187l1.793-1.483a3.263 3.263 0 0 0 .729-2.074 7.857 7.857 0 0 0 .027-.878 23.166 23.166 0 0 0 4.174 1.622 24.4 24.4 0 0 0 4.051-1.614 7.848 7.848 0 0 0 .027.869 3.263 3.263 0 0 0 .729 2.074l1.793 1.484a.61.61 0 0 0 .4.187h4c1.324 0 2.223-1.336 2.529-3.343a7.057 7.057 0 0 0 -.234-3.18 3.477 3.477 0 0 0 -1.635-2.158zm-17.069 6.5c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.048c.051 1.03-.161 2.644-.509 2.834zm6.409-6.819h-2l.5-5a2 2 0 1 1 1 0zm6.38 7.921a.418.418 0 0 1 -.627.111c-.522-.433-1.439-1.2-1.458-1.208-.348-.189-.56-1.8-.505-2.828a15.84 15.84 0 0 0 2.9-2.037 9.322 9.322 0 0 1 -.31 5.962zm-20.924-11.162.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm33.217 1.2a3.021 3.021 0 0 0 -2.658-1.525 1.574 1.574 0 0 0 -.107-1.283l-.745-1.367a1.779 1.779 0 0 0 -1.317-.891l-.7-1.278a2.409 2.409 0 0 0 .229-2.661 2.283 2.283 0 0 0 -2.375-1.454 7.039 7.039 0 0 1 0 3 3.272 3.272 0 0 0 0 1.5c.047.152-.047.3 0 .5.227.04-.069.156.165.14l.653 1.2a1.579 1.579 0 0 0 -.019 1.557l.745 1.367a1.753 1.753 0 0 0 1.045.832 2.66 2.66 0 0 0 -.238 2.916 2.989 2.989 0 0 0 2.326 1.509.79.79 0 0 0 .082 0 .707.707 0 0 0 .717-.6.688.688 0 0 0 -.636-.749 1.546 1.546 0 0 1 -1.2-.78 1.408 1.408 0 0 1 .672-1.95 1.628 1.628 0 0 1 1.179-.089 1.512 1.512 0 0 1 .9.719 1.37 1.37 0 0 1 -.023 1.361.656.656 0 0 0 .284.92.748.748 0 0 0 .981-.266 2.648 2.648 0 0 0 .04-2.633zm-19.673-11.959a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2z" fill-rule="evenodd" class="ColorScheme-Text" fill="currentColor"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org> Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils Section: utils
Priority: optional Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-23 (>= 23.0.1+11-0ppa1), libgtk-3-0, libxxf86vm1, libgl1 Build-Depends: debhelper (>=10), coffeelibs-jdk-22 (>= 22.0.1+8-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0 Standards-Version: 4.5.0
Homepage: https://cryptomator.org Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git Vcs-Git: https://github.com/cryptomator/cryptomator.git
+2 -2
View File
@@ -4,11 +4,11 @@ Upstream-Contact: Cryptomator <info@cryptomator.org>
Source: https://cryptomator.org Source: https://cryptomator.org
Files: * Files: *
Copyright: 2016-2025 Skymatic GmbH Copyright: 2016-2024 Skymatic GmbH
License: GPL-3+ License: GPL-3+
Files: debian/org.cryptomator.Cryptomator.appdata.xml Files: debian/org.cryptomator.Cryptomator.appdata.xml
Copyright: 2016-2025 Skymatic GmbH Copyright: 2016-2024 Skymatic GmbH
License: FSFAP License: FSFAP
License: GPL-3+ License: GPL-3+
+1 -2
View File
@@ -45,7 +45,7 @@ override_dh_auto_build:
--vendor "Skymatic GmbH" \ --vendor "Skymatic GmbH" \
--java-options "--enable-preview" \ --java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \ --java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \ --copyright "(C) 2016 - 2024 Skymatic GmbH" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \ --java-options "-Dfile.encoding=\"utf-8\"" \
@@ -62,7 +62,6 @@ override_dh_auto_build:
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \ --java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \ --java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \ --java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \ --app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \ --resource-dir resources \
--verbose --verbose
+4 -4
View File
@@ -24,7 +24,7 @@ rm -rf runtime dmg *.app *.dmg
# set variables # set variables
APP_NAME="Cryptomator" APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH" VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2025" COPYRIGHT_YEARS="2016 - 2024"
PACKAGE_IDENTIFIER="org.cryptomator" PACKAGE_IDENTIFIER="org.cryptomator"
MAIN_JAR_GLOB="cryptomator-*.jar" MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator" MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
@@ -32,15 +32,15 @@ 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'` VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
FUSE_LIB="FUSE-T" FUSE_LIB="FUSE-T"
JAVAFX_VERSION=23.0.1 JAVAFX_VERSION=22.0.2
JAVAFX_ARCH="undefined" JAVAFX_ARCH="undefined"
JAVAFX_JMODS_SHA256="undefined" JAVAFX_JMODS_SHA256="undefined"
if [ "$(machine)" = "arm64e" ]; then if [ "$(machine)" = "arm64e" ]; then
JAVAFX_ARCH="aarch64" JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256="a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6" JAVAFX_JMODS_SHA256="813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7"
else else
JAVAFX_ARCH="x64" JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256="8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e" JAVAFX_JMODS_SHA256="115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4"
fi fi
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip" JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
+1 -1
View File
@@ -17,7 +17,7 @@
\f1\b0 \ \f1\b0 \
\ \
\f0\b \'a9 2016 \'96 2025 Skymatic GmbH \f0\b \'a9 2016 \'96 2024 Skymatic GmbH
\f1\b0 \ \f1\b0 \
\ \
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\ This program is 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.\
+1 -1
View File
@@ -11,7 +11,7 @@ SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator" SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
SET LOOPBACK_ALIAS="cryptomator-vault" SET LOOPBACK_ALIAS="cryptomator-vault"
pwsh -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^ powershell -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^ -AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^ -MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^ -ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
+5 -5
View File
@@ -51,23 +51,23 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
} }
## download jfx jmods ## download jfx jmods
$javaFxVersion='23.0.1' $javaFxVersion='22.0.2'
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip" $javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
$javaFxJmodsSHA256 = 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3' $javaFxJmodsSHA256 = 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
$javaFxJmods = '.\resources\jfxJmods.zip' $javaFxJmods = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $javaFxJmods) ) { if( !(Test-Path -Path $javaFxJmods) ) {
Write-Output "Downloading ${javaFxJmodsUrl}..." Write-Output "Downloading ${javaFxJmodsUrl}..."
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
} }
$jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash.ToLower() $jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash
if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) { if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256 Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
, actual: $jmodsChecksumActual" , actual: $jmodsChecksumActual"
exit 1; exit 1;
} }
Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\" Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods" -ErrorAction Ignore Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
## create custom runtime ## create custom runtime
@@ -75,7 +75,7 @@ Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\ja
--verbose ` --verbose `
--output runtime ` --output runtime `
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" ` --module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml ` --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--strip-native-commands ` --strip-native-commands `
--no-header-files ` --no-header-files `
--no-man-pages ` --no-man-pages `
+1 -1
View File
@@ -10,7 +10,7 @@
\vieww12000\viewh15840\viewkind0 \vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par \pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par \par
\b\'a9 2016 \'96 2025 Skymatic GmbH \b0\par \b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
\par \par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par \par
Vendored Executable
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@
\vieww12000\viewh15840\viewkind0 \vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par \pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par \par
\b\'a9 2016 \'96 2025 Skymatic GmbH \b0\par \b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
\par \par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par \par
+24 -42
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId> <artifactId>cryptomator</artifactId>
<version>1.16.0</version> <version>1.15.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name> <name>Cryptomator Desktop App</name>
<organization> <organization>
@@ -33,46 +33,43 @@
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds> <nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0-beta2</cryptomator.cryptofs.version> <cryptomator.cryptofs.version>2.7.1</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.5.1</cryptomator.integrations.version> <cryptomator.integrations.version>1.4.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.3.0</cryptomator.integrations.win.version> <cryptomator.integrations.win.version>1.4.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.3.0</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.3.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.5.3</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.0.3</cryptomator.fuse.version> <cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
<cryptomator.webdav.version>2.0.8</cryptomator.webdav.version> <cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
<!-- 3rd party dependencies --> <!-- 3rd party dependencies -->
<commons-lang3.version>3.17.0</commons-lang3.version> <commons-lang3.version>3.17.0</commons-lang3.version>
<dagger.version>2.55</dagger.version> <dagger.version>2.52</dagger.version>
<easybind.version>2.2</easybind.version> <easybind.version>2.2</easybind.version>
<jackson.version>2.18.3</jackson.version> <jackson.version>2.18.1</jackson.version>
<javafx.version>23.0.2</javafx.version> <javafx.version>22.0.2</javafx.version>
<jwt.version>4.5.0</jwt.version> <jwt.version>4.4.0</jwt.version>
<nimbus-jose.version>9.37.3</nimbus-jose.version> <nimbus-jose.version>9.37.3</nimbus-jose.version>
<logback.version>1.5.17</logback.version> <logback.version>1.5.12</logback.version>
<slf4j.version>2.0.17</slf4j.version> <slf4j.version>2.0.16</slf4j.version>
<tinyoauth2.version>0.8.1</tinyoauth2.version> <tinyoauth2.version>0.8.0</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version> <zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies --> <!-- test dependencies -->
<junit.jupiter.version>5.11.4</junit.jupiter.version> <junit.jupiter.version>5.11.3</junit.jupiter.version>
<mockito.version>5.15.2</mockito.version> <mockito.version>5.14.2</mockito.version>
<hamcrest.version>3.0</hamcrest.version> <hamcrest.version>3.0</hamcrest.version>
<!-- build-time dependencies --> <!-- build-time dependencies -->
<jetbrains.annotations.version>26.0.2</jetbrains.annotations.version> <jetbrains.annotations.version>26.0.1</jetbrains.annotations.version>
<dependency-check.version>12.1.0</dependency-check.version> <dependency-check.version>11.1.0</dependency-check.version>
<jacoco.version>0.8.12</jacoco.version> <jacoco.version>0.8.12</jacoco.version>
<license-generator.version>2.5.0</license-generator.version> <license-generator.version>2.4.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version> <junit-tree-reporter.version>1.3.0</junit-tree-reporter.version>
<mvn-compiler.version>3.13.0</mvn-compiler.version> <mvn-compiler.version>3.13.0</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version> <mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.8.1</mvn-dependency.version> <mvn-dependency.version>3.8.1</mvn-dependency.version>
<mvn-surefire.version>3.5.2</mvn-surefire.version> <mvn-surefire.version>3.5.2</mvn-surefire.version>
<mvn-jar.version>3.4.2</mvn-jar.version> <mvn-jar.version>3.4.2</mvn-jar.version>
<!-- Property used by surefire to determine jacoco engine -->
<surefire.jacoco.args></surefire.jacoco.args>
</properties> </properties>
<dependencies> <dependencies>
@@ -80,7 +77,7 @@
<dependency> <dependency>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId> <artifactId>cryptolib</artifactId>
<version>2.2.1</version> <version>2.2.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
@@ -159,12 +156,6 @@
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId> <artifactId>java-jwt</artifactId>
<version>${jwt.version}</version> <version>${jwt.version}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.nimbusds</groupId> <groupId>com.nimbusds</groupId>
@@ -214,7 +205,7 @@
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
<version>3.2.0</version> <version>3.1.8</version>
</dependency> </dependency>
<!-- JUnit / Mockito / Hamcrest --> <!-- JUnit / Mockito / Hamcrest -->
<dependency> <dependency>
@@ -317,6 +308,7 @@
<compilerArgs> <compilerArgs>
<arg>-Adagger.fastInit=enabled</arg> <arg>-Adagger.fastInit=enabled</arg>
<arg>-Adagger.formatGeneratedSource=enabled</arg> <arg>-Adagger.formatGeneratedSource=enabled</arg>
<arg>--enable-preview</arg>
</compilerArgs> </compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
@@ -343,11 +335,11 @@
</dependency> </dependency>
</dependencies> </dependencies>
<configuration> <configuration>
<argLine>--enable-preview</argLine>
<reportFormat>plain</reportFormat> <reportFormat>plain</reportFormat>
<consoleOutputReporter> <consoleOutputReporter>
<disable>true</disable> <disable>true</disable>
</consoleOutputReporter> </consoleOutputReporter>
<argLine>@{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar}</argLine>
<statelessTestsetInfoReporter <statelessTestsetInfoReporter
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter"> implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
</statelessTestsetInfoReporter> </statelessTestsetInfoReporter>
@@ -357,13 +349,6 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<executions> <executions>
<execution>
<id>jar-paths-to-properties</id>
<phase>validate</phase>
<goals>
<goal>properties</goal>
</goals>
</execution>
<!-- sort jars into two buckets (classpath and modulepath). exclude openjfx, which gets jlinked separately --> <!-- sort jars into two buckets (classpath and modulepath). exclude openjfx, which gets jlinked separately -->
<execution> <execution>
<id>copy-mods</id> <id>copy-mods</id>
@@ -436,9 +421,6 @@
<goals> <goals>
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
<configuration>
<propertyName>surefire.jacoco.args</propertyName>
</configuration>
</execution> </execution>
<execution> <execution>
<id>report</id> <id>report</id>
-7
View File
@@ -1,5 +1,4 @@
import ch.qos.logback.classic.spi.Configurator; import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
@@ -14,9 +13,6 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider; import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider; import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.integrations.tray.TrayMenuController; import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory; import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.ui.traymenu.AwtTrayMenuController; import org.cryptomator.ui.traymenu.AwtTrayMenuController;
@@ -58,12 +54,9 @@ open module org.cryptomator.desktop {
requires com.github.benmanes.caffeine; requires com.github.benmanes.caffeine;
uses org.cryptomator.common.locationpresets.LocationPresetsProvider; uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
uses SSLContextProvider;
uses org.cryptomator.event.NotificationHandler;
provides TrayMenuController with AwtTrayMenuController; provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory; provides Configurator with LogbackConfiguratorFactory;
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
provides LocationPresetsProvider with // provides LocationPresetsProvider with //
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, // DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, // GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //
@@ -6,8 +6,6 @@ import java.util.concurrent.TimeUnit;
public class JavaFXUtil { public class JavaFXUtil {
private JavaFXUtil() {}
public static boolean startPlatform() throws InterruptedException { public static boolean startPlatform() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
try { try {
@@ -23,6 +23,7 @@ public class Environment {
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath"; private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath"; private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths"; private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path"; private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir"; private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias"; private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
@@ -45,6 +46,7 @@ public class Environment {
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME); logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME); logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME); logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME); logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME); logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME); logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
@@ -85,6 +87,10 @@ public class Environment {
return getPaths(KEYCHAIN_PATHS_PROP_NAME); return getPaths(KEYCHAIN_PATHS_PROP_NAME);
} }
public Stream<Path> getWindowsHelloKeychainPath() {
return getPaths(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
}
public Stream<Path> getP12Path() { public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME); return getPaths(P12_PATH_PROP_NAME);
} }
@@ -1,160 +0,0 @@
package org.cryptomator.common;
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.event.VaultEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
/**
* Map containing {@link VaultEvent}s.
* The map is keyed by the ciphertext path of the affected resource _and_ the {@link FilesystemEvent}s class in order to group same events
* <p>
* Use {@link EventMap#put(VaultEvent)} to add an element and {@link EventMap#remove(VaultEvent)} to remove it.
* <p>
* The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed.
*/
@Singleton
public class EventMap implements ObservableMap<EventMap.EventKey, VaultEvent> {
private static final int MAX_SIZE = 300;
public record EventKey(Path ciphertextPath, Class<? extends FilesystemEvent> c) {}
private final ObservableMap<EventMap.EventKey, VaultEvent> delegate;
@Inject
public EventMap() {
delegate = FXCollections.observableHashMap();
}
@Override
public void addListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
delegate.addListener(mapChangeListener);
}
@Override
public void removeListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
delegate.removeListener(mapChangeListener);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public VaultEvent get(Object key) {
return delegate.get(key);
}
@Override
public @Nullable VaultEvent put(EventKey key, VaultEvent value) {
return delegate.put(key, value);
}
@Override
public VaultEvent remove(Object key) {
return delegate.remove(key);
}
@Override
public void putAll(@NotNull Map<? extends EventKey, ? extends VaultEvent> m) {
delegate.putAll(m);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public @NotNull Set<EventKey> keySet() {
return delegate.keySet();
}
@Override
public @NotNull Collection<VaultEvent> values() {
return delegate.values();
}
@Override
public @NotNull Set<Entry<EventKey, VaultEvent>> entrySet() {
return delegate.entrySet();
}
@Override
public void addListener(InvalidationListener invalidationListener) {
delegate.addListener(invalidationListener);
}
@Override
public void removeListener(InvalidationListener invalidationListener) {
delegate.removeListener(invalidationListener);
}
public synchronized void put(VaultEvent e) {
//compute key
var key = computeKey(e.actualEvent());
//if-else
var nullOrEntry = delegate.get(key);
if (nullOrEntry == null) {
if (size() == MAX_SIZE) {
delegate.entrySet().stream() //
.min(Comparator.comparing(entry -> entry.getValue().actualEvent().getTimestamp())) //
.ifPresent(oldestEntry -> delegate.remove(oldestEntry.getKey()));
}
delegate.put(key, e);
} else {
delegate.put(key, nullOrEntry.incrementCount(e.actualEvent()));
}
}
public synchronized VaultEvent remove(VaultEvent similar) {
//compute key
var key = computeKey(similar.actualEvent());
return this.remove(key);
}
private EventKey computeKey(FilesystemEvent e) {
var p = switch (e) {
case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath;
case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext;
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
};
return new EventKey(p, e.getClass());
}
}
@@ -2,7 +2,6 @@ package org.cryptomator.common.keychain;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import org.cryptomator.common.Passphrase;
import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.keychain.KeychainAccessProvider;
@@ -14,15 +13,12 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Singleton @Singleton
public class KeychainManager implements KeychainAccessProvider { public class KeychainManager implements KeychainAccessProvider {
private final ObjectExpression<KeychainAccessProvider> keychain; private final ObjectExpression<KeychainAccessProvider> keychain;
private final LoadingCache<String, BooleanProperty> passphraseStoredProperties; private final LoadingCache<String, BooleanProperty> passphraseStoredProperties;
private final ReentrantReadWriteLock lock;
@Inject @Inject
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) { KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
@@ -31,7 +27,6 @@ public class KeychainManager implements KeychainAccessProvider {
.softValues() // .softValues() //
.build(this::createStoredPassphraseProperty); .build(this::createStoredPassphraseProperty);
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll()); keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
this.lock = new ReentrantReadWriteLock(false);
} }
private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException { private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException {
@@ -49,57 +44,32 @@ public class KeychainManager implements KeychainAccessProvider {
@Override @Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
storePassphrase(key, displayName, passphrase, true); storePassphrase(key, displayName, passphrase, true); //TODO: currently only TouchID is using this parameter, so this is okayish
} }
//TODO: remove ignored parameter once the API is fixed
@Override @Override
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean ignored) throws KeychainAccessException { public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
try { getKeychainOrFail().storePassphrase(key, displayName, passphrase, requireOsAuthentication);
lock.writeLock().lock();
var kc = getKeychainOrFail();
//this is the only keychain actually using the parameter
var usesOSAuth = (kc.getClass().getName().equals("org.cryptomator.macos.keychain.TouchIdKeychainAccess"));
kc.storePassphrase(key, displayName, passphrase, usesOSAuth);
} finally {
lock.writeLock().unlock();
}
setPassphraseStored(key, true); setPassphraseStored(key, true);
} }
@Override @Override
public char[] loadPassphrase(String key) throws KeychainAccessException { public char[] loadPassphrase(String key) throws KeychainAccessException {
char[] passphrase = null; char[] passphrase = getKeychainOrFail().loadPassphrase(key);
try {
lock.readLock().lock();
passphrase = getKeychainOrFail().loadPassphrase(key);
} finally {
lock.readLock().unlock();
}
setPassphraseStored(key, passphrase != null); setPassphraseStored(key, passphrase != null);
return passphrase; return passphrase;
} }
@Override @Override
public void deletePassphrase(String key) throws KeychainAccessException { public void deletePassphrase(String key) throws KeychainAccessException {
try { getKeychainOrFail().deletePassphrase(key);
lock.writeLock().lock();
getKeychainOrFail().deletePassphrase(key);
} finally {
lock.writeLock().unlock();
}
setPassphraseStored(key, false); setPassphraseStored(key, false);
} }
@Override @Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) { if (isPassphraseStored(key)) {
try { getKeychainOrFail().changePassphrase(key, displayName, passphrase);
lock.writeLock().lock();
getKeychainOrFail().changePassphrase(key, displayName, passphrase);
} finally {
lock.writeLock().unlock();
}
setPassphraseStored(key, true); setPassphraseStored(key, true);
} }
} }
@@ -171,18 +141,4 @@ public class KeychainManager implements KeychainAccessProvider {
return this.keychain; return this.keychain;
} }
public static void migrate(KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider, Map<String, String> idsAndNames) throws KeychainAccessException {
if (oldProvider instanceof KeychainManager || newProvider instanceof KeychainManager) {
throw new IllegalArgumentException("KeychainManger must not be the source or target of migration");
}
for (var entry : idsAndNames.entrySet()) {
var passphrase = oldProvider.loadPassphrase(entry.getKey());
if (passphrase != null) {
var wrapper = new Passphrase(passphrase);
oldProvider.deletePassphrase(entry.getKey()); //we cannot apply "first-write-then-delete" pattern here, since we can potentially write to the same passphrase store (e.g., touchID and regular keychain)
newProvider.storePassphrase(entry.getKey(), entry.getValue(), wrapper);
wrapper.destroy();
}
}
}
} }
@@ -58,7 +58,6 @@ public class VaultSettings {
public final StringExpression mountName; public final StringExpression mountName;
public final StringProperty mountService; public final StringProperty mountService;
public final IntegerProperty port; public final IntegerProperty port;
public final StringProperty lastKnownKeyLoader;
VaultSettings(VaultSettingsJson json) { VaultSettings(VaultSettingsJson json) {
this.id = json.id; this.id = json.id;
@@ -75,7 +74,6 @@ public class VaultSettings {
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint)); this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService); this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.port = new SimpleIntegerProperty(this, "port", json.port); this.port = new SimpleIntegerProperty(this, "port", json.port);
this.lastKnownKeyLoader = new SimpleStringProperty(this, "lastKnownKeyLoader", json.lastKnownKeyLoader);
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318 // mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> { this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name; final String name;
@@ -101,7 +99,7 @@ public class VaultSettings {
} }
Observable[] observables() { Observable[] observables() {
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService, lastKnownKeyLoader}; return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
} }
public static VaultSettings withRandomId() { public static VaultSettings withRandomId() {
@@ -132,7 +130,6 @@ public class VaultSettings {
json.mountPoint = mountPoint.map(Path::toString).getValue(); json.mountPoint = mountPoint.map(Path::toString).getValue();
json.mountService = mountService.get(); json.mountService = mountService.get();
json.port = port.get(); json.port = port.get();
json.lastKnownKeyLoader = lastKnownKeyLoader.get();
return json; return json;
} }
@@ -48,9 +48,6 @@ class VaultSettingsJson {
@JsonProperty("mountService") @JsonProperty("mountService")
String mountService; String mountService;
@JsonProperty("lastKnownKeyLoader")
String lastKnownKeyLoader;
@JsonProperty("port") @JsonProperty("port")
int port = VaultSettings.DEFAULT_PORT; int port = VaultSettings.DEFAULT_PORT;
@@ -10,7 +10,6 @@ package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants; import org.cryptomator.common.Constants;
import org.cryptomator.common.EventMap;
import org.cryptomator.common.mount.Mounter; import org.cryptomator.common.mount.Mounter;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VaultSettings;
@@ -19,11 +18,9 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags; import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException; import org.cryptomator.integrations.mount.UnmountFailedException;
@@ -35,7 +32,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
@@ -48,7 +44,6 @@ import javafx.beans.property.SimpleBooleanProperty;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.ReadOnlyFileSystemException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@@ -78,7 +73,6 @@ public class Vault {
private final ObjectBinding<Mountpoint> mountPoint; private final ObjectBinding<Mountpoint> mountPoint;
private final Mounter mounter; private final Mounter mounter;
private final Settings settings; private final Settings settings;
private final EventMap eventMap;
private final BooleanProperty showingStats; private final BooleanProperty showingStats;
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null); private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@@ -90,8 +84,7 @@ public class Vault {
VaultState state, // VaultState state, //
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, // @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
VaultStats stats, // VaultStats stats, //
Mounter mounter, Settings settings, // Mounter mounter, Settings settings) {
EventMap eventMap) {
this.vaultSettings = vaultSettings; this.vaultSettings = vaultSettings;
this.configCache = configCache; this.configCache = configCache;
this.cryptoFileSystem = cryptoFileSystem; this.cryptoFileSystem = cryptoFileSystem;
@@ -108,7 +101,6 @@ public class Vault {
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state); this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
this.mounter = mounter; this.mounter = mounter;
this.settings = settings; this.settings = settings;
this.eventMap = eventMap;
this.showingStats = new SimpleBooleanProperty(false); this.showingStats = new SimpleBooleanProperty(false);
this.quickAccessEntry = new AtomicReference<>(null); this.quickAccessEntry = new AtomicReference<>(null);
} }
@@ -119,22 +111,15 @@ public class Vault {
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException { private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class); Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
var createReadOnly = vaultSettings.usesReadOnlyMode.get(); if (vaultSettings.usesReadOnlyMode.get()) {
try {
FileSystemCapabilityChecker.assertWriteAccess(getPath());
} catch (FileSystemCapabilityChecker.MissingCapabilityException e) {
if (!createReadOnly) {
throw new ReadOnlyFileSystemException();
}
}
if (createReadOnly) {
flags.add(FileSystemFlags.READONLY); flags.add(FileSystemFlags.READONLY);
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) { } else if (vaultSettings.maxCleartextFilenameLength.get() == -1) {
LOG.debug("Determining cleartext filename length limitations..."); LOG.debug("Determining cleartext filename length limitations...");
var checker = new FileSystemCapabilityChecker();
int shorteningThreshold = configCache.get().allegedShorteningThreshold(); int shorteningThreshold = configCache.get().allegedShorteningThreshold();
int ciphertextLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(getPath()); int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
if (ciphertextLimit < shorteningThreshold) { if (ciphertextLimit < shorteningThreshold) {
int cleartextLimit = FileSystemCapabilityChecker.determineSupportedCleartextFileNameLength(getPath()); int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit); vaultSettings.maxCleartextFilenameLength.set(cleartextLimit);
} else { } else {
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH); vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH);
@@ -150,7 +135,6 @@ public class Vault {
.withFlags(flags) // .withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) // .withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.withFilesystemEventConsumer(this::consumeVaultEvent) //
.build(); .build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
} }
@@ -259,14 +243,6 @@ public class Vault {
} }
} }
private void consumeVaultEvent(FilesystemEvent e) {
var wrapper = new VaultEvent(this, e);
Platform.runLater(() -> {
eventMap.put(wrapper);
});
}
// ****************************************************************************** // ******************************************************************************
// Observable Properties // Observable Properties
// ******************************************************************************* // *******************************************************************************
@@ -428,17 +404,6 @@ public class Vault {
} }
} }
/**
* Gets the cleartext name from a given path to an encrypted vault file
*/
public String getCleartextName(Path ciphertextPath) throws IOException {
if (!state.getValue().equals(VaultState.Value.UNLOCKED)) {
throw new IllegalStateException("Vault is not unlocked");
}
var fs = cryptoFileSystem.get();
return fs.getCleartextName(ciphertextPath);
}
public VaultConfigCache getVaultConfigCache() { public VaultConfigCache getVaultConfigCache() {
return configCache; return configCache;
} }
@@ -27,7 +27,6 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -50,9 +49,9 @@ public class VaultListManager {
@Inject @Inject
public VaultListManager(ObservableList<Vault> vaultList, // public VaultListManager(ObservableList<Vault> vaultList, //
AutoLocker autoLocker, // AutoLocker autoLocker, //
List<MountService> mountServices, // List<MountService> mountServices,
VaultComponent.Factory vaultComponentFactory, // VaultComponent.Factory vaultComponentFactory,
ResourceBundle resourceBundle, // ResourceBundle resourceBundle,
Settings settings) { Settings settings) {
this.vaultList = vaultList; this.vaultList = vaultList;
this.autoLocker = autoLocker; this.autoLocker = autoLocker;
@@ -115,10 +114,6 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) { private Vault create(VaultSettings vaultSettings) {
var wrapper = new VaultConfigCache(vaultSettings); var wrapper = new VaultConfigCache(vaultSettings);
try { try {
if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) {
var keyIdScheme = wrapper.get().getKeyId().getScheme();
vaultSettings.lastKnownKeyLoader.set(keyIdScheme);
}
var vaultState = determineVaultState(vaultSettings.path.get()); var vaultState = determineVaultState(vaultSettings.path.get());
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig(); wrapper.reloadConfig();
@@ -1,14 +0,0 @@
package org.cryptomator.event;
public sealed interface Answer permits Answer.DoNothing, Answer.DoSomething {
record DoNothing() implements Answer {}
record DoSomething(Runnable action) implements Answer {
void run() {
action.run();
}
}
}
@@ -1,15 +0,0 @@
package org.cryptomator.event;
import org.cryptomator.integrations.common.IntegrationsLoader;
import java.util.ServiceLoader;
import java.util.stream.Stream;
public interface NotificationHandler {
Answer handle(VaultEvent e);
static Stream<NotificationHandler> loadAll() {
return IntegrationsLoader.loadAll(ServiceLoader.load(NotificationHandler.class), NotificationHandler.class);
}
}
@@ -1,27 +0,0 @@
package org.cryptomator.event;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import java.time.Instant;
public record VaultEvent(Vault v, FilesystemEvent actualEvent, int count) implements Comparable<VaultEvent> {
public VaultEvent(Vault v, FilesystemEvent actualEvent) {
this(v, actualEvent, 1);
}
@Override
public int compareTo(VaultEvent other) {
var timeResult = actualEvent.getTimestamp().compareTo(other.actualEvent().getTimestamp());
if(timeResult != 0) {
return timeResult;
} else {
return this.equals(other) ? 0 : this.actualEvent.getClass().getName().compareTo(other.actualEvent.getClass().getName());
}
}
public VaultEvent incrementCount(FilesystemEvent update) {
return new VaultEvent(v, update, count+1);
}
}
@@ -9,9 +9,8 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dagger.Lazy; import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.SubstitutingProperties; import org.cryptomator.common.SubstitutingProperties;
import org.cryptomator.networking.SSLContextProvider; import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator; import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode; import org.cryptomator.logging.DebugMode;
import org.cryptomator.ui.fxapp.FxApplicationComponent; import org.cryptomator.ui.fxapp.FxApplicationComponent;
@@ -20,10 +19,8 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.net.ssl.SSLContext;
import javafx.application.Application; import javafx.application.Application;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -51,16 +48,14 @@ public class Cryptomator {
private final Environment env; private final Environment env;
private final Lazy<IpcMessageHandler> ipcMessageHandler; private final Lazy<IpcMessageHandler> ipcMessageHandler;
private final ShutdownHook shutdownHook; private final ShutdownHook shutdownHook;
private final SecureRandom csprng;
@Inject @Inject
Cryptomator(DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook, SecureRandom csprng) { Cryptomator(DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
this.debugMode = debugMode; this.debugMode = debugMode;
this.supportedLanguages = supportedLanguages; this.supportedLanguages = supportedLanguages;
this.env = env; this.env = env;
this.ipcMessageHandler = ipcMessageHandler; this.ipcMessageHandler = ipcMessageHandler;
this.shutdownHook = shutdownHook; this.shutdownHook = shutdownHook;
this.csprng = csprng;
} }
public static void main(String[] args) { public static void main(String[] args) {
@@ -94,7 +89,7 @@ public class Cryptomator {
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
debugMode.initialize(); debugMode.initialize();
supportedLanguages.applyPreferred(); supportedLanguages.applyPreferred();
changeDefaultSSLContext();
/* /*
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args. * Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
* If no external process could be reached, the args will be handled by the loopback IPC endpoint. * If no external process could be reached, the args will be handled by the loopback IPC endpoint.
@@ -120,17 +115,6 @@ public class Cryptomator {
} }
} }
private void changeDefaultSSLContext() {
SSLContextProvider.loadAll().findFirst().ifPresent(p -> {
try {
var context = p.getContext(csprng);
SSLContext.setDefault(context);
} catch (SSLContextProvider.SSLContextBuildException e) {
LOG.warn("Failed to change default SSL context with provider {}", p.getClass().getName(), e);
}
});
}
/** /**
* Launches the JavaFX application, blocking the main thread until shuts down. * Launches the JavaFX application, blocking the main thread until shuts down.
* *
@@ -1,33 +0,0 @@
package org.cryptomator.networking;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
abstract KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException;
@Override
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
try {
KeyStore truststore = getTruststore();
truststore.load(null, null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), csprng);
return context;
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | KeyManagementException | IOException e) {
throw new SSLContextBuildException(e);
}
}
}
@@ -1,24 +0,0 @@
package org.cryptomator.networking;
import org.cryptomator.integrations.common.IntegrationsLoader;
import javax.net.ssl.SSLContext;
import java.security.SecureRandom;
import java.util.ServiceLoader;
import java.util.stream.Stream;
public interface SSLContextProvider {
SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException;
class SSLContextBuildException extends Exception {
SSLContextBuildException(Throwable t) {
super(t);
}
}
static Stream<SSLContextProvider> loadAll() {
return IntegrationsLoader.loadAll(ServiceLoader.load(SSLContextProvider.class), SSLContextProvider.class);
}
}
@@ -1,21 +0,0 @@
package org.cryptomator.networking;
import org.cryptomator.integrations.common.OperatingSystem;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* SSLContextProvider for macOS using the macOS Keychain as truststore
*/
@OperatingSystem(OperatingSystem.Value.MAC)
public class SSLContextWithMacKeychain extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
@Override
KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
return KeyStore.getInstance("KeychainStore-ROOT");
}
}
@@ -1,42 +0,0 @@
package org.cryptomator.networking;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Optional;
/**
* SSLContextProvider for Linux using a PKCS#12 file as trust store
*/
@OperatingSystem(OperatingSystem.Value.LINUX)
@CheckAvailability
public class SSLContextWithPKCS12TrustStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
private static final String CERT_FILE_LOCATION_PROPERTY = "cryptomator.networking.truststore.p12Path";
@Override
KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
var pkcs12FilePath = Path.of(System.getProperty(CERT_FILE_LOCATION_PROPERTY));
try {
return KeyStore.getInstance(pkcs12FilePath.toFile(), new char[]{});
} catch (IllegalArgumentException e) {
throw new NoSuchFileException(pkcs12FilePath.toString());
}
}
@CheckAvailability
public static boolean isSupported() {
var pkcs12Path = System.getProperty(CERT_FILE_LOCATION_PROPERTY);
return Optional.ofNullable(pkcs12Path) //
.map(Path::of) //
.map(Files::exists).orElse(false);
}
}
@@ -1,21 +0,0 @@
package org.cryptomator.networking;
import org.cryptomator.integrations.common.OperatingSystem;
import java.security.KeyStore;
import java.security.KeyStoreException;
/**
* SSLContextProvider for Windows using the Windows certificate store as trust store
* <p>
* In order to work, the jdk.crypto.mscapi jmod is needed
*/
@OperatingSystem(OperatingSystem.Value.WINDOWS)
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
@Override
KeyStore getTruststore() throws KeyStoreException {
return KeyStore.getInstance("WINDOWS-ROOT");
}
}
@@ -26,7 +26,7 @@ public class CreateNewVaultExpertSettingsController implements FxController {
public static final int MAX_SHORTENING_THRESHOLD = 220; public static final int MAX_SHORTENING_THRESHOLD = 220;
public static final int MIN_SHORTENING_THRESHOLD = 36; public static final int MIN_SHORTENING_THRESHOLD = 36;
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/architecture/#name-shortening"; private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/en/1.7/security/architecture/#name-shortening";
private final Stage window; private final Stage window;
private final Lazy<Application> application; private final Lazy<Application> application;
@@ -40,6 +40,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Optional; import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -49,7 +50,7 @@ public class CreateNewVaultLocationController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultLocationController.class); 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 Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home"));
private static final String TEMP_FILE_PREFIX = ".locationTest.cryptomator"; private static final String TEMP_FILE_FORMAT = ".locationTest.cryptomator.tmp";
private final Stage window; private final Stage window;
private final Lazy<Scene> chooseNameScene; private final Lazy<Scene> chooseNameScene;
@@ -125,19 +126,16 @@ public class CreateNewVaultLocationController implements FxController {
private boolean isActuallyWritable(Path p) { private boolean isActuallyWritable(Path p) {
Path tmpDir = null; Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
try { try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
tmpDir = Files.createTempDirectory(p, TEMP_FILE_PREFIX );
return true; return true;
} catch (IOException e) { } catch (IOException e) {
return false; return false;
} finally { } finally {
if (tmpDir != null) { try {
try { Files.deleteIfExists(tmpFile);
Files.deleteIfExists(tmpDir); } catch (IOException e) {
} catch (IOException e) { LOG.warn("Unable to delete temporary file {}. Needs to be deleted manually.", tmpFile);
LOG.warn("Unable to delete temporary directory {}. Needs to be deleted manually.", tmpDir);
}
} }
} }
} }
@@ -76,10 +76,8 @@ public class ReadmeGenerator {
input.chars().forEachOrdered(c -> { input.chars().forEachOrdered(c -> {
if (c < 128) { if (c < 128) {
sb.append((char) c); sb.append((char) c);
} else if (c <= 0xFF) {
sb.append("\\'").append(String.format("%02X", c));
} else if (c < 0xFFFF) { } else if (c < 0xFFFF) {
sb.append("\\uc1\\u").append(c); sb.append("\\u").append(c);
} }
}); });
} }
@@ -13,7 +13,6 @@ public enum FxmlFile {
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), // CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), // CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
ERROR("/fxml/error.fxml"), // ERROR("/fxml/error.fxml"), //
EVENT_VIEW("/fxml/eventview.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), //
HEALTH_START("/fxml/health_start.fxml"), // HEALTH_START("/fxml/health_start.fxml"), //
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), // HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
@@ -7,7 +7,6 @@ public enum FontAwesome5Icon {
ANCHOR("\uF13D"), // ANCHOR("\uF13D"), //
ARROW_UP("\uF062"), // ARROW_UP("\uF062"), //
BAN("\uF05E"), // BAN("\uF05E"), //
BELL("\uF0F3"), //
BUG("\uF188"), // BUG("\uF188"), //
CARET_DOWN("\uF0D7"), // CARET_DOWN("\uF0D7"), //
CARET_RIGHT("\uF0Da"), // CARET_RIGHT("\uF0Da"), //
@@ -16,12 +15,10 @@ public enum FontAwesome5Icon {
CLIPBOARD("\uF328"), // CLIPBOARD("\uF328"), //
COG("\uF013"), // COG("\uF013"), //
COGS("\uF085"), // COGS("\uF085"), //
COMPRESS_ALT("\uF422"), //
COPY("\uF0C5"), // COPY("\uF0C5"), //
CROWN("\uF521"), // CROWN("\uF521"), //
DONATE("\uF4B9"), // DONATE("\uF4B9"), //
EDIT("\uF044"), // EDIT("\uF044"), //
ELLIPSIS_V("\uF142"), //
EXCHANGE_ALT("\uF362"), // EXCHANGE_ALT("\uF362"), //
EXCLAMATION("\uF12A"), // EXCLAMATION("\uF12A"), //
EXCLAMATION_CIRCLE("\uF06A"), // EXCLAMATION_CIRCLE("\uF06A"), //
@@ -15,7 +15,6 @@ import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory; import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.jetbrains.annotations.VisibleForTesting; import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -109,7 +108,6 @@ public class HubToPasswordConvertController implements FxController {
.thenRunAsync(this::convertInternal, backgroundExecutorService) // .thenRunAsync(this::convertInternal, backgroundExecutorService) //
.whenCompleteAsync((result, exception) -> { .whenCompleteAsync((result, exception) -> {
if (exception == null) { if (exception == null) {
vault.getVaultSettings().lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
LOG.info("Conversion of vault {} succeeded.", vault.getPath()); LOG.info("Conversion of vault {} succeeded.", vault.getPath());
window.setScene(successScene.get()); window.setScene(successScene.get());
} else { } else {
@@ -2,34 +2,31 @@ package org.cryptomator.ui.dialogs;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.function.Consumer; import java.util.function.Consumer;
@FxApplicationScoped @Singleton
public class Dialogs { public class Dialogs {
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
@Inject @Inject
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) { public Dialogs(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
} }
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class); private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
private SimpleDialog.Builder createDialogBuilder() { private SimpleDialog.Builder createDialogBuilder() {
return new SimpleDialog.Builder(resourceBundle, stageFactory); return new SimpleDialog.Builder(resourceBundle);
} }
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) { public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
@@ -38,7 +35,7 @@ public class Dialogs {
.setMessageKey("removeVault.message") // .setMessageKey("removeVault.message") //
.setDescriptionKey("removeVault.description") // .setDescriptionKey("removeVault.description") //
.setIcon(FontAwesome5Icon.QUESTION) // .setIcon(FontAwesome5Icon.QUESTION) //
.setOkButtonKey("generic.button.remove") // .setOkButtonKey("removeVault.confirmBtn") //
.setCancelButtonKey("generic.button.cancel") // .setCancelButtonKey("generic.button.cancel") //
.setOkAction(stage -> { .setOkAction(stage -> {
LOG.debug("Removing vault {}.", vault.getDisplayName()); LOG.debug("Removing vault {}.", vault.getDisplayName());
@@ -54,7 +51,7 @@ public class Dialogs {
.setMessageKey("removeCert.message") // .setMessageKey("removeCert.message") //
.setDescriptionKey("removeCert.description") // .setDescriptionKey("removeCert.description") //
.setIcon(FontAwesome5Icon.QUESTION) // .setIcon(FontAwesome5Icon.QUESTION) //
.setOkButtonKey("generic.button.remove") // .setOkButtonKey("removeCert.confirmBtn") //
.setCancelButtonKey("generic.button.cancel") // .setCancelButtonKey("generic.button.cancel") //
.setOkAction(stage -> { .setOkAction(stage -> {
settings.licenseKey.set(null); settings.licenseKey.set(null);
@@ -68,23 +65,11 @@ public class Dialogs {
.setTitleKey("dokanySupportEnd.title") // .setTitleKey("dokanySupportEnd.title") //
.setMessageKey("dokanySupportEnd.message") // .setMessageKey("dokanySupportEnd.message") //
.setDescriptionKey("dokanySupportEnd.description") // .setDescriptionKey("dokanySupportEnd.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) // .setIcon(FontAwesome5Icon.QUESTION) //
.setOkButtonKey("generic.button.close") // .setOkButtonKey("generic.button.close") //
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") // .setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
.setOkAction(Stage::close) // .setOkAction(Stage::close) //
.setCancelAction(cancelAction); .setCancelAction(cancelAction);
} }
public SimpleDialog.Builder prepareRetryIfReadonlyDialog(Stage window, Consumer<Stage> okAction) {
return createDialogBuilder() //
.setOwner(window) //
.setTitleKey("retryIfReadonly.title") //
.setMessageKey("retryIfReadonly.message") //
.setDescriptionKey("retryIfReadonly.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("retryIfReadonly.retry") //
.setCancelButtonKey("generic.button.close") //
.setOkAction(okAction) //
.setCancelAction(Stage::close);
}
} }
@@ -2,7 +2,6 @@ package org.cryptomator.ui.dialogs;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5Icon;
import javafx.scene.Scene; import javafx.scene.Scene;
@@ -18,24 +17,24 @@ import java.util.function.Consumer;
public class SimpleDialog { public class SimpleDialog {
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final Stage dialogStage; private final Stage dialogStage;
SimpleDialog(Builder builder) throws IOException { SimpleDialog(Builder builder) throws IOException {
this.resourceBundle = builder.resourceBundle; this.resourceBundle = builder.resourceBundle;
dialogStage = builder.stageFactory.create(); dialogStage = new Stage();
dialogStage.initOwner(builder.owner); dialogStage.initOwner(builder.owner);
dialogStage.initModality(Modality.WINDOW_MODAL); dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.setTitle(resolveText(builder.titleKey, builder.titleArgs)); dialogStage.setTitle(resolveText(builder.titleKey, builder.titleArgs));
dialogStage.setResizable(false); dialogStage.setResizable(false);
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( // FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController(
new SimpleDialogController(resolveText(builder.messageKey, null), // new SimpleDialogController(resolveText(builder.messageKey, null),
resolveText(builder.descriptionKey, null), // resolveText(builder.descriptionKey, null),
builder.icon, // builder.icon,resolveText(builder.okButtonKey, null),
resolveText(builder.okButtonKey, null), // resolveText(builder.cancelButtonKey, null),
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, // () -> builder.okAction.accept(dialogStage),
() -> builder.okAction.accept(dialogStage), // () -> builder.cancelAction.accept(dialogStage)),
() -> builder.cancelAction.accept(dialogStage)), //
Scene::new, builder.resourceBundle); Scene::new, builder.resourceBundle);
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot())); dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
@@ -61,20 +60,19 @@ public class SimpleDialog {
private Stage owner; private Stage owner;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
private String titleKey; private String titleKey;
private String[] titleArgs; private String[] titleArgs;
private String messageKey; private String messageKey;
private String descriptionKey; private String descriptionKey;
private String okButtonKey; private String okButtonKey;
private String cancelButtonKey; private String cancelButtonKey;
private FontAwesome5Icon icon; private FontAwesome5Icon icon;
private Consumer<Stage> okAction = Stage::close; private Consumer<Stage> okAction = Stage::close;
private Consumer<Stage> cancelAction = Stage::close; private Consumer<Stage> cancelAction = Stage::close;
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) { public Builder(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
} }
public Builder setOwner(Stage owner) { public Builder setOwner(Stage owner) {
@@ -124,10 +122,11 @@ public class SimpleDialog {
} }
public SimpleDialog build() { public SimpleDialog build() {
Objects.requireNonNull(titleKey, "SimpleDialog titleKey must be set."); Objects.requireNonNull(titleKey,"SimpleDialog titleKey must be set.");
Objects.requireNonNull(messageKey, "SimpleDialog messageKey must be set."); Objects.requireNonNull(messageKey,"SimpleDialog messageKey must be set.");
Objects.requireNonNull(descriptionKey, "SimpleDialog descriptionKey must be set."); Objects.requireNonNull(descriptionKey,"SimpleDialog descriptionKey must be set.");
Objects.requireNonNull(okButtonKey, "SimpleDialog okButtonKey must be set."); Objects.requireNonNull(okButtonKey,"SimpleDialog okButtonKey must be set.");
Objects.requireNonNull(cancelButtonKey,"SimpleDialog cancelButtonKey must be set.");
try { try {
return new SimpleDialog(this); return new SimpleDialog(this);
@@ -14,7 +14,6 @@ public class SimpleDialogController implements FxController {
private final String cancelButtonText; private final String cancelButtonText;
private final Runnable okAction; private final Runnable okAction;
private final Runnable cancelAction; private final Runnable cancelAction;
private final boolean cancelButtonVisible;
public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) { public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) {
this.message = message; this.message = message;
@@ -24,11 +23,6 @@ public class SimpleDialogController implements FxController {
this.cancelButtonText = cancelButtonText; this.cancelButtonText = cancelButtonText;
this.okAction = okAction; this.okAction = okAction;
this.cancelAction = cancelAction; this.cancelAction = cancelAction;
this.cancelButtonVisible = cancelButtonText != null && !cancelButtonText.isEmpty();
}
public boolean isCancelButtonVisible() {
return cancelButtonVisible;
} }
public String getMessage() { public String getMessage() {
@@ -1,323 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.common.EventMap;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.cryptofs.CryptoPath;
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.geometry.Side;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tooltip;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.layout.HBox;
import javafx.util.Duration;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
public class EventListCellController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(EventListCellController.class);
private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
private final EventMap eventMap;
@Nullable
private final RevealPathService revealService;
private final ResourceBundle resourceBundle;
private final ObjectProperty<VaultEvent> event;
private final StringProperty eventMessage;
private final StringProperty eventDescription;
private final ObjectProperty<FontAwesome5Icon> eventIcon;
private final ObservableValue<String> eventCount;
private final ObservableValue<Boolean> vaultUnlocked;
private final ObservableValue<String> readableTime;
private final ObservableValue<String> readableDate;
private final ObservableValue<String> message;
private final ObservableValue<String> description;
private final ObservableValue<FontAwesome5Icon> icon;
private final BooleanProperty actionsButtonVisible;
private final Tooltip eventTooltip;
@FXML
HBox root;
@FXML
ContextMenu eventActionsMenu;
@FXML
Button eventActionsButton;
@Inject
public EventListCellController(EventMap eventMap, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
this.eventMap = eventMap;
this.revealService = revealService.orElseGet(() -> null);
this.resourceBundle = resourceBundle;
this.event = new SimpleObjectProperty<>(null);
this.eventMessage = new SimpleStringProperty();
this.eventDescription = new SimpleStringProperty();
this.eventIcon = new SimpleObjectProperty<>();
this.eventCount = ObservableUtil.mapWithDefault(event, e -> e.count() == 1? "" : "("+ e.count() +")", "");
this.vaultUnlocked = ObservableUtil.mapWithDefault(event.flatMap(e -> e.v().unlockedProperty()), Function.identity(), false);
this.readableTime = ObservableUtil.mapWithDefault(event, e -> LOCAL_TIME_FORMATTER.format(e.actualEvent().getTimestamp()), "");
this.readableDate = ObservableUtil.mapWithDefault(event, e -> LOCAL_DATE_FORMATTER.format(e.actualEvent().getTimestamp()), "");
this.message = Bindings.createStringBinding(this::selectMessage, vaultUnlocked, eventMessage);
this.description = Bindings.createStringBinding(this::selectDescription, vaultUnlocked, eventDescription);
this.icon = Bindings.createObjectBinding(this::selectIcon, vaultUnlocked, eventIcon);
this.actionsButtonVisible = new SimpleBooleanProperty();
this.eventTooltip = new Tooltip();
eventTooltip.setShowDelay(Duration.millis(500.0));
}
@FXML
public void initialize() {
actionsButtonVisible.bind(Bindings.createBooleanBinding(this::determineActionsButtonVisibility, root.hoverProperty(), eventActionsMenu.showingProperty(), vaultUnlocked));
vaultUnlocked.addListener((_, _, newValue) -> eventActionsMenu.hide());
Tooltip.install(root, eventTooltip);
}
private boolean determineActionsButtonVisibility() {
return vaultUnlocked.getValue() && (eventActionsMenu.isShowing() || root.isHover());
}
public void setEvent(@NotNull VaultEvent item) {
event.set(item);
eventActionsMenu.hide();
eventActionsMenu.getItems().clear();
eventTooltip.setText(item.v().getDisplayName());
addAction("generic.action.dismiss", () -> eventMap.remove(item));
switch (item.actualEvent()) {
case ConflictResolvedEvent fse -> this.adjustToConflictResolvedEvent(fse);
case ConflictResolutionFailedEvent fse -> this.adjustToConflictEvent(fse);
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
}
}
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, convertVaultPathToSystemPath(bfe.ciphertextPath())));
} else {
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.ciphertextPath()).toString()));
}
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
}
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
eventIcon.setValue(FontAwesome5Icon.CHECK);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else {
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
}
}
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
} else {
addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
}
}
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
eventIcon.setValue(FontAwesome5Icon.BAN);
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else {
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
}
}
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
if (revealService != null) {
addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else {
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
}
}
private void addAction(String localizationKey, Runnable action) {
var entry = new MenuItem(resourceBundle.getString(localizationKey));
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
entry.setOnAction(_ -> action.run());
eventActionsMenu.getItems().addLast(entry);
}
private FontAwesome5Icon selectIcon() {
if (vaultUnlocked.getValue()) {
return eventIcon.getValue();
} else {
return FontAwesome5Icon.LOCK;
}
}
private String selectMessage() {
if (vaultUnlocked.getValue()) {
return eventMessage.getValue();
} else {
return resourceBundle.getString("eventView.entry.vaultLocked.message");
}
}
private String selectDescription() {
if (vaultUnlocked.getValue()) {
return eventDescription.getValue();
} else {
var e = event.getValue();
return resourceBundle.getString("eventView.entry.vaultLocked.description").formatted(e != null ? e.v().getDisplayName() : "");
}
}
@FXML
public void toggleEventActionsMenu() {
var e = event.get();
if (e != null) {
if (eventActionsMenu.isShowing()) {
eventActionsMenu.hide();
} else {
eventActionsMenu.show(eventActionsButton, Side.BOTTOM, 0.0, 0.0);
}
}
}
private Path convertVaultPathToSystemPath(Path p) {
if (!(p instanceof CryptoPath)) {
throw new IllegalArgumentException("Path " + p + " is not a vault path");
}
var v = event.getValue().v();
if (!v.isUnlocked()) {
return Path.of(System.getProperty("user.home"));
}
var mountUri = v.getMountPoint().uri();
var internalPath = p.toString().substring(1);
return Path.of(mountUri.getPath().concat(internalPath).substring(1));
}
private void reveal(RevealPathService s, Path p) {
try {
s.reveal(p);
} catch (RevealFailedException e) {
LOG.warn("Failed to show path {}", p, e);
}
}
private void copyToClipboard(String s) {
var content = new ClipboardContent();
content.putString(s);
Clipboard.getSystemClipboard().setContent(content);
}
//-- property accessors --
public ObservableValue<String> messageProperty() {
return message;
}
public String getMessage() {
return message.getValue();
}
public ObservableValue<String> countProperty() {
return eventCount;
}
public String getCount() {
return eventCount.getValue();
}
public ObservableValue<String> descriptionProperty() {
return description;
}
public String getDescription() {
return description.getValue();
}
public ObservableValue<FontAwesome5Icon> iconProperty() {
return icon;
}
public FontAwesome5Icon getIcon() {
return icon.getValue();
}
public ObservableValue<Boolean> actionsButtonVisibleProperty() {
return actionsButtonVisible;
}
public boolean isActionsButtonVisible() {
return actionsButtonVisible.getValue();
}
public ObservableValue<String> eventLocalTimeProperty() {
return readableTime;
}
public String getEventLocalTime() {
return readableTime.getValue();
}
public ObservableValue<String> eventLocalDateProperty() {
return readableDate;
}
public String getEventLocalDate() {
return readableDate.getValue();
}
public ObservableValue<Boolean> vaultUnlockedProperty() {
return vaultUnlocked;
}
public boolean isVaultUnlocked() {
return vaultUnlocked.getValue();
}
}
@@ -1,64 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import javax.inject.Inject;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
import java.io.IOException;
import java.io.UncheckedIOException;
@EventViewScoped
public class EventListCellFactory implements Callback<ListView<VaultEvent>, ListCell<VaultEvent>> {
private static final String FXML_PATH = "/fxml/eventview_cell.fxml";
private final FxmlLoaderFactory fxmlLoaders;
@Inject
EventListCellFactory(@EventViewWindow FxmlLoaderFactory fxmlLoaders) {
this.fxmlLoaders = fxmlLoaders;
}
@Override
public ListCell<VaultEvent> call(ListView<VaultEvent> eventListView) {
try {
FXMLLoader fxmlLoader = fxmlLoaders.load(FXML_PATH);
return new Cell(fxmlLoader.getRoot(), fxmlLoader.getController());
} catch (IOException e) {
throw new UncheckedIOException("Failed to load %s.".formatted(FXML_PATH), e);
}
}
private static class Cell extends ListCell<VaultEvent> {
private final Parent root;
private final EventListCellController controller;
public Cell(Parent root, EventListCellController controller) {
this.root = root;
this.controller = controller;
}
@Override
protected void updateItem(VaultEvent item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
this.getStyleClass().remove("list-cell");
} else {
this.getStyleClass().addLast("list-cell");
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(root);
controller.setEvent(item);
}
}
}
}
@@ -1,35 +0,0 @@
package org.cryptomator.ui.eventview;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
@EventViewScoped
@Subcomponent(modules = {EventViewModule.class})
public interface EventViewComponent {
@EventViewWindow
Stage window();
@FxmlScene(FxmlFile.EVENT_VIEW)
Lazy<Scene> scene();
default Stage showEventViewerWindow() {
Stage stage = window();
stage.setScene(scene().get());
stage.sizeToScene();
stage.show();
stage.requestFocus();
return stage;
}
@Subcomponent.Factory
interface Factory {
EventViewComponent create();
}
}
@@ -1,121 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.common.EventMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListView;
import javafx.util.StringConverter;
import java.util.Comparator;
import java.util.ResourceBundle;
@EventViewScoped
public class EventViewController implements FxController {
private final EventMap eventMap;
private final ObservableList<VaultEvent> eventList;
private final FilteredList<VaultEvent> filteredEventList;
private final ObservableList<Vault> vaults;
private final SortedList<VaultEvent> reversedEventList;
private final ObservableList<Vault> choiceBoxEntries;
private final ResourceBundle resourceBundle;
private final EventListCellFactory cellFactory;
@FXML
ChoiceBox<Vault> vaultFilterChoiceBox;
@FXML
ListView<VaultEvent> eventListView;
@Inject
public EventViewController(EventMap eventMap, ObservableList<Vault> vaults, ResourceBundle resourceBundle, EventListCellFactory cellFactory) {
this.eventMap = eventMap;
this.eventList = FXCollections.observableArrayList();
this.filteredEventList = eventList.filtered(_ -> true);
this.vaults = vaults;
this.reversedEventList = new SortedList<>(filteredEventList, Comparator.reverseOrder());
this.choiceBoxEntries = FXCollections.observableArrayList();
this.resourceBundle = resourceBundle;
this.cellFactory = cellFactory;
}
@FXML
public void initialize() {
choiceBoxEntries.add(null);
choiceBoxEntries.addAll(vaults);
vaults.addListener((ListChangeListener<? super Vault>) c -> {
while (c.next()) {
choiceBoxEntries.removeAll(c.getRemoved());
choiceBoxEntries.addAll(c.getAddedSubList());
}
});
eventList.addAll(eventMap.values());
eventMap.addListener((MapChangeListener<? super EventMap.EventKey, ? super VaultEvent>) this::updateList);
eventListView.setCellFactory(cellFactory);
eventListView.setItems(reversedEventList);
vaultFilterChoiceBox.setItems(choiceBoxEntries);
vaultFilterChoiceBox.valueProperty().addListener(this::applyVaultFilter);
vaultFilterChoiceBox.setConverter(new VaultConverter(resourceBundle));
}
private void updateList(MapChangeListener.Change<? extends EventMap.EventKey, ? extends VaultEvent> change) {
if (change.wasAdded() && change.wasRemoved()) {
//entry updated
eventList.remove(change.getValueRemoved());
eventList.addLast(change.getValueAdded());
} else if (change.wasAdded()) {
eventList.addLast(change.getValueAdded());
} else { //removed
eventList.remove(change.getValueRemoved());
}
}
private void applyVaultFilter(ObservableValue<? extends Vault> v, Vault oldV, Vault newV) {
if (newV == null) {
filteredEventList.setPredicate(_ -> true);
} else {
filteredEventList.setPredicate(e -> e.v().equals(newV));
}
}
@FXML
void clearEvents() {
eventMap.clear();
}
private static class VaultConverter extends StringConverter<Vault> {
private final ResourceBundle resourceBundle;
VaultConverter(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
}
@Override
public String toString(Vault v) {
if (v == null) {
return resourceBundle.getString("eventView.filter.allVaults");
} else {
return v.getDisplayName();
}
}
@Override
public Vault fromString(String displayLanguage) {
throw new UnsupportedOperationException();
}
}
}
@@ -1,61 +0,0 @@
package org.cryptomator.ui.eventview;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
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 javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class EventViewModule {
@Provides
@EventViewScoped
@EventViewWindow
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setHeight(498);
stage.setTitle(resourceBundle.getString("eventView.title"));
stage.setResizable(true);
stage.initModality(Modality.NONE);
return stage;
}
@Provides
@EventViewScoped
@EventViewWindow
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@FxmlScene(FxmlFile.EVENT_VIEW)
@EventViewScoped
static Scene provideEventViewerScene(@EventViewWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.EVENT_VIEW);
}
@Binds
@IntoMap
@FxControllerKey(EventViewController.class)
abstract FxController bindEventViewController(EventViewController controller);
@Binds
@IntoMap
@FxControllerKey(EventListCellController.class)
abstract FxController bindEventListCellController(EventListCellController controller);
}
@@ -1,13 +0,0 @@
package org.cryptomator.ui.eventview;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface EventViewScoped {
}
@@ -1,14 +0,0 @@
package org.cryptomator.ui.eventview;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface EventViewWindow {
}
@@ -1,14 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
@EventViewScoped
public class UpdateEventViewController implements FxController {
@Inject
public UpdateEventViewController() {
}
}
@@ -43,8 +43,8 @@ public class AutoUnlocker {
private CompletionStage<Void> unlockSequentially(Stream<Vault> vaultStream) { private CompletionStage<Void> unlockSequentially(Stream<Vault> vaultStream) {
// this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow. // this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow.
return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null), return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null),
(prevUnlock, nextVault) -> prevUnlock.thenCompose(_ -> appWindows.startUnlockWorkflow(nextVault, null)), (prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)),
(_, nextUnlock) -> nextUnlock.exceptionally(_ -> null) // we don't care here about the exception, logged elsewhere (prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere
); );
} }
@@ -8,7 +8,6 @@ package org.cryptomator.ui.fxapp;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.cryptomator.ui.error.ErrorComponent; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.health.HealthCheckComponent; import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
@@ -34,8 +33,7 @@ import java.io.InputStream;
ErrorComponent.class, // ErrorComponent.class, //
HealthCheckComponent.class, // HealthCheckComponent.class, //
UpdateReminderComponent.class, // UpdateReminderComponent.class, //
ShareVaultComponent.class, // ShareVaultComponent.class})
EventViewComponent.class})
abstract class FxApplicationModule { abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException { private static Image createImageFromResource(String resourceName) throws IOException {
@@ -68,10 +66,4 @@ abstract class FxApplicationModule {
return builder.build(); return builder.build();
} }
@Provides
@FxApplicationScoped
static EventViewComponent provideEventViewComponent(EventViewComponent.Factory factory) {
return factory.create();
}
} }
@@ -6,9 +6,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider; import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.dialogs.Dialogs; import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.dialogs.SimpleDialog;
import org.cryptomator.ui.error.ErrorComponent; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
@@ -53,7 +51,6 @@ public class FxApplicationWindows {
private final UpdateReminderComponent.Factory updateReminderWindowFactory; private final UpdateReminderComponent.Factory updateReminderWindowFactory;
private final LockComponent.Factory lockWorkflowFactory; private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory; private final ErrorComponent.Factory errorWindowFactory;
private final Lazy<EventViewComponent> eventViewWindow;
private final ExecutorService executor; private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow; private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow; private final ShareVaultComponent.Factory shareVaultWindow;
@@ -72,7 +69,6 @@ public class FxApplicationWindows {
ErrorComponent.Factory errorWindowFactory, // ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, // VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, // ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, //
ExecutorService executor, // ExecutorService executor, //
Dialogs dialogs) { Dialogs dialogs) {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
@@ -84,7 +80,6 @@ public class FxApplicationWindows {
this.updateReminderWindowFactory = updateReminderWindowFactory; this.updateReminderWindowFactory = updateReminderWindowFactory;
this.lockWorkflowFactory = lockWorkflowFactory; this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory; this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow;
this.executor = executor; this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow; this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow; this.shareVaultWindow = shareVaultWindow;
@@ -98,17 +93,17 @@ public class FxApplicationWindows {
// register preferences shortcut // register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) { if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
desktop.setPreferencesHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ANY)); desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
} }
// register preferences shortcut // register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
desktop.setAboutHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ABOUT)); desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
} }
// register app reopen listener // register app reopen listener
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) { if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
desktop.addAppEventListener((AppReopenedListener) _ -> showMainWindow()); desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
} }
// observe visible windows // observe visible windows
@@ -140,12 +135,11 @@ public class FxApplicationWindows {
} }
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) { public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
return showMainWindow().thenApplyAsync(_ -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater) // return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
.whenComplete(this::reportErrors);
} }
public void showQuitWindow(QuitResponse response, boolean forced) { public void showQuitWindow(QuitResponse response, boolean forced) {
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response, forced), Platform::runLater); CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
} }
public void showUpdateReminderWindow() { public void showUpdateReminderWindow() {
@@ -153,14 +147,13 @@ public class FxApplicationWindows {
} }
public void showDokanySupportEndWindow() { public void showDokanySupportEndWindow() {
CompletableFuture.runAsync(() -> createDokanySupportEndDialog().showAndWait(), Platform::runLater); CompletableFuture.runAsync(() -> dialogs.prepareDokanySupportEndDialog(
} mainWindow.get().window(),
stage -> {
private SimpleDialog createDokanySupportEndDialog() { showPreferencesWindow(SelectedPreferencesTab.VOLUME);
return dialogs.prepareDokanySupportEndDialog(mainWindow.get().window(), stage -> { stage.close();
showPreferencesWindow(SelectedPreferencesTab.VOLUME); }
stage.close(); ).build().showAndWait(), Platform::runLater);
}).build();
} }
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) { public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
@@ -169,7 +162,8 @@ public class FxApplicationWindows {
LOG.debug("Start unlock workflow for {}", vault.getDisplayName()); LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow(); return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
}, Platform::runLater) // }, Platform::runLater) //
.thenAcceptAsync(UnlockWorkflow::run, executor).exceptionally(e -> { .thenAcceptAsync(UnlockWorkflow::run, executor)
.exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null); showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null; return null;
}); });
@@ -188,11 +182,6 @@ public class FxApplicationWindows {
}); });
} }
public CompletionStage<Stage> showEventViewer() {
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
}
/** /**
* Displays the generic error scene in the given window. * Displays the generic error scene in the given window.
* *
@@ -210,4 +199,5 @@ public class FxApplicationWindows {
LOG.error("Failed to display stage", error); LOG.error("Failed to display stage", error);
} }
} }
} }
@@ -3,8 +3,6 @@ package org.cryptomator.ui.keyloading;
import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -30,33 +28,6 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
@Override @Override
Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException; Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException;
/**
* Determines whether the provided key loader scheme corresponds to a Hub Vault.
* <p>
* This method compares the {@code keyLoader} parameter with the known Hub Vault schemes
* {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTP} and {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTPS}.
*
* @param keyLoader A string representing the key loader scheme to be checked.
* @return {@code true} if the given key loader scheme represents a Hub Vault; {@code false} otherwise.
*/
static boolean isHubVault(String keyLoader) {
return HubKeyLoadingStrategy.SCHEME_HUB_HTTP.equals(keyLoader) || HubKeyLoadingStrategy.SCHEME_HUB_HTTPS.equals(keyLoader);
}
/**
* Determines whether the provided key loader scheme corresponds to a Masterkey File Vault.
* <p>
* This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme
* {@link MasterkeyFileLoadingStrategy#SCHEME}.
* </p>
*
* @param keyLoader A string representing the key loader scheme to be checked.
* @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise.
*/
static boolean isMasterkeyFileVault(String keyLoader) {
return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader);
}
/** /**
* Allows the loader to try and recover from an exception thrown during the last attempt. * Allows the loader to try and recover from an exception thrown during the last attempt.
* *
@@ -165,7 +165,6 @@ public class ReceiveKeyController implements FxController {
var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token"); var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token");
var request = HttpRequest.newBuilder(vaultKeyUri) // var request = HttpRequest.newBuilder(vaultKeyUri) //
.header("Authorization", "Bearer " + bearerToken) // .header("Authorization", "Bearer " + bearerToken) //
.header("Hub-Device-ID", deviceId) //
.GET() // .GET() //
.timeout(REQ_TIMEOUT) // .timeout(REQ_TIMEOUT) //
.build(); .build();
@@ -113,7 +113,7 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
private void savePasswordToSystemkeychain(Passphrase passphrase) { private void savePasswordToSystemkeychain(Passphrase passphrase) {
try { try {
if (keychain.isSupported() && !keychain.getPassphraseStoredProperty(vault.getId()).get()) { if (keychain.isSupported() && !keychain.getPassphraseStoredProperty(vault.getId()).getValue()) {
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase); keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase);
} }
} catch (KeychainAccessException e) { } catch (KeychainAccessException e) {
@@ -1,5 +1,13 @@
package org.cryptomator.ui.mainwindow; package org.cryptomator.ui.mainwindow;
import javafx.beans.Observable;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
@@ -13,17 +21,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.Observable;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
@MainWindowScoped @MainWindowScoped
public class MainWindowController implements FxController { public class MainWindowController implements FxController {
@@ -66,66 +63,27 @@ public class MainWindowController implements FxController {
} }
window.focusedProperty().addListener(this::mainWindowFocusChanged); window.focusedProperty().addListener(this::mainWindowFocusChanged);
int x = settings.windowXPosition.get(); if (!neverTouched()) {
int y = settings.windowYPosition.get(); window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
int width = settings.windowWidth.get(); window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
int height = settings.windowHeight.get(); window.setX(settings.windowXPosition.get());
if (windowPositionSaved(x, y, width, height)) { window.setY(settings.windowYPosition.get());
window.setX(x);
window.setY(y);
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
} }
window.widthProperty().addListener((_, _, _) -> savePositionalSettings());
window.setOnShowing(this::checkDisplayBounds); window.heightProperty().addListener((_, _, _) -> savePositionalSettings());
window.xProperty().addListener((_, _, _) -> savePositionalSettings());
settings.windowXPosition.bind(window.xProperty()); window.yProperty().addListener((_, _, _) -> savePositionalSettings());
settings.windowYPosition.bind(window.yProperty());
settings.windowWidth.bind(window.widthProperty());
settings.windowHeight.bind(window.heightProperty());
} }
private boolean windowPositionSaved(int x, int y, int width, int height) { private boolean neverTouched() {
return x != 0 || y != 0 || width != 0 || height != 0; return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
} }
private void checkDisplayBounds(WindowEvent windowEvent) { public void savePositionalSettings() {
int x = settings.windowXPosition.get(); settings.windowWidth.setValue(window.getWidth());
int y = settings.windowYPosition.get(); settings.windowHeight.setValue(window.getHeight());
int width = settings.windowWidth.get(); settings.windowXPosition.setValue(window.getX());
int height = settings.windowHeight.get(); settings.windowYPosition.setValue(window.getY());
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
if (!isWithinDisplayBounds(x, y, width, height)) { //use stored window position
LOG.debug("Resetting window position due to insufficient screen overlap");
var centeredX = (primaryScreenBounds.getWidth() - window.getMinWidth()) / 2;
var centeredY = (primaryScreenBounds.getHeight() - window.getMinHeight()) / 2;
//check if we can keep width and height
if (isWithinDisplayBounds((int) centeredX, (int) centeredY, width, height)) {
//if so, keep window size
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
}
//reset position of upper left corner
window.setX(centeredX);
window.setY(centeredY);
}
}
private boolean isWithinDisplayBounds(int x, int y, int width, int height) {
// define a rect which is inset on all sides from the window's rect:
final int shrinkedX = x + 20; // 20px left
final int shrinkedY = y + 5; // 5px top
final int shrinkedWidth = width - 40; // 20px left + 20px right
final int shrinkedHeigth = height - 25; // 5px top + 20px bottom
return isRectangleWithinBounds(shrinkedX, shrinkedY, 0, shrinkedHeigth) // Left pixel column
&& isRectangleWithinBounds(shrinkedX + shrinkedWidth, shrinkedY, 0, shrinkedHeigth) // Right pixel column
&& isRectangleWithinBounds(shrinkedX, shrinkedY, shrinkedWidth, 0) // Top pixel row
&& isRectangleWithinBounds(shrinkedX, shrinkedY + shrinkedHeigth, shrinkedWidth, 0); // Bottom pixel row
}
private boolean isRectangleWithinBounds(int x, int y, int width, int height) {
return !Screen.getScreensForRectangle(x, y, width, height).isEmpty();
} }
private void mainWindowFocusChanged(Observable observable) { private void mainWindowFocusChanged(Observable observable) {
@@ -166,7 +124,7 @@ public class MainWindowController implements FxController {
return updateAvailable.get(); return updateAvailable.get();
} }
public BooleanBinding licenseValidProperty() { public BooleanBinding licenseValidProperty(){
return licenseHolder.validLicenseProperty(); return licenseHolder.validLicenseProperty();
} }
@@ -1,7 +1,6 @@
package org.cryptomator.ui.mainwindow; package org.cryptomator.ui.mainwindow;
import dagger.Binds; import dagger.Binds;
import dagger.Lazy;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
@@ -15,11 +14,9 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.common.StageInitializer; import org.cryptomator.ui.common.StageInitializer;
import org.cryptomator.ui.error.ErrorComponent; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.fxapp.FxApplicationTerminator;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.migration.MigrationComponent; import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent; import org.cryptomator.ui.stats.VaultStatisticsComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent; import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
import javax.inject.Named; import javax.inject.Named;
@@ -38,19 +35,11 @@ abstract class MainWindowModule {
@Provides @Provides
@MainWindow @MainWindow
@MainWindowScoped @MainWindowScoped
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer, FxApplicationTerminator terminator, Lazy<TrayMenuComponent> trayMenu) { static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
initializer.accept(stage); initializer.accept(stage);
stage.setTitle("Cryptomator"); stage.setTitle("Cryptomator");
stage.setMinWidth(650); stage.setMinWidth(650);
stage.setMinHeight(498); stage.setMinHeight(498);
stage.setOnCloseRequest(e -> {
if (!trayMenu.get().isInitialized()) {
terminator.terminate();
e.consume();
} else {
stage.close();
}
});
return stage; return stage;
} }
@@ -6,7 +6,6 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.tobiasdiez.easybind.EasyBind; import com.tobiasdiez.easybind.EasyBind;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.revealpath.RevealFailedException; import org.cryptomator.integrations.revealpath.RevealFailedException;
@@ -40,14 +39,10 @@ import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@MainWindowScoped @MainWindowScoped
public class VaultDetailUnlockedController implements FxController { public class VaultDetailUnlockedController implements FxController {
@@ -67,15 +62,11 @@ public class VaultDetailUnlockedController implements FxController {
private final ObservableValue<Boolean> accessibleViaPath; private final ObservableValue<Boolean> accessibleViaPath;
private final ObservableValue<Boolean> accessibleViaUri; private final ObservableValue<Boolean> accessibleViaUri;
private final ObservableValue<String> mountPoint; private final ObservableValue<String> mountPoint;
private final BooleanProperty draggingOverLocateEncrypted = new SimpleBooleanProperty(); private final BooleanProperty draggingOver = new SimpleBooleanProperty();
private final BooleanProperty draggingOverDecryptName = new SimpleBooleanProperty();
private final BooleanProperty ciphertextPathsCopied = new SimpleBooleanProperty(); private final BooleanProperty ciphertextPathsCopied = new SimpleBooleanProperty();
private final BooleanProperty cleartextNamesCopied = new SimpleBooleanProperty();
@FXML //FXML
public Button revealEncryptedDropZone; public Button dropZone;
@FXML
public Button decryptNameDropZone;
@Inject @Inject
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, Optional<RevealPathService> revealPathService, ResourceBundle resourceBundle) { public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, Optional<RevealPathService> revealPathService, ResourceBundle resourceBundle) {
@@ -101,90 +92,72 @@ public class VaultDetailUnlockedController implements FxController {
} }
public void initialize() { public void initialize() {
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted)); dropZone.setOnDragEntered(this::handleDragEvent);
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths)); dropZone.setOnDragOver(this::handleDragEvent);
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false)); dropZone.setOnDragDropped(this::handleDragEvent);
dropZone.setOnDragExited(this::handleDragEvent);
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName)); EasyBind.includeWhen(dropZone.getStyleClass(), ACTIVE_CLASS, draggingOver);
decryptNameDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCleartextName, this::copyDecryptedNamesToClipboard));
decryptNameDropZone.setOnDragExited(_ -> draggingOverDecryptName.setValue(false));
EasyBind.includeWhen(revealEncryptedDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverLocateEncrypted);
EasyBind.includeWhen(decryptNameDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverDecryptName);
} }
private void handleDragOver(DragEvent event, BooleanProperty prop) { private void handleDragEvent(DragEvent event) {
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) { if(SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) {
event.acceptTransferModes(TransferMode.LINK); event.acceptTransferModes(TransferMode.LINK);
} else { } else {
event.acceptTransferModes(TransferMode.ANY); event.acceptTransferModes(TransferMode.ANY);
} }
prop.set(true); draggingOver.set(true);
} else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
List<Path> ciphertextPaths = event.getDragboard().getFiles().stream().map(File::toPath).map(this::getCiphertextPath).flatMap(Optional::stream).toList();
if (ciphertextPaths.isEmpty()) {
wrongFileAlert.build().showWrongFileAlertWindow();
} else {
revealOrCopyPaths(ciphertextPaths);
}
event.setDropCompleted(!ciphertextPaths.isEmpty());
event.consume();
} else if (DragEvent.DRAG_EXITED.equals(event.getEventType())) {
draggingOver.set(false);
} }
} }
private <T> void handleDragDropped(DragEvent event, Function<Path, T> computation, Consumer<List<T>> positiveAction) { private VaultStatisticsComponent buildVaultStats(Vault vault) {
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { return vaultStatsBuilder.vault(vault).build();
List<T> objects = event.getDragboard().getFiles().stream().map(File::toPath).map(computation).filter(Objects::nonNull).toList();
if (objects.isEmpty()) {
wrongFileAlert.build().showWrongFileAlertWindow();
} else {
positiveAction.accept(objects);
}
event.setDropCompleted(!objects.isEmpty());
event.consume();
}
} }
@FXML @FXML
public void chooseDecryptedFileAndReveal() { public void revealAccessLocation() {
vaultService.reveal(vault.get());
}
@FXML
public void copyMountUri() {
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(mountPoint.getValue());
Clipboard.getSystemClipboard().setContent(clipboardContent);
}
@FXML
public void lock() {
appWindows.startLockWorkflow(vault.get(), mainWindow);
}
@FXML
public void showVaultStatistics() {
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
}
@FXML
public void chooseFileAndReveal() {
Preconditions.checkState(accessibleViaPath.getValue()); Preconditions.checkState(accessibleViaPath.getValue());
var fileChooser = new FileChooser(); var fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.locateEncrypted.filePickerTitle")); fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.filePickerTitle"));
fileChooser.setInitialDirectory(Path.of(mountPoint.getValue()).toFile()); fileChooser.setInitialDirectory(Path.of(mountPoint.getValue()).toFile());
var cleartextFile = fileChooser.showOpenDialog(mainWindow); var cleartextFile = fileChooser.showOpenDialog(mainWindow);
if (cleartextFile != null) { if (cleartextFile != null) {
var ciphertextPath = getCiphertextPath(cleartextFile.toPath()); var ciphertextPaths = getCiphertextPath(cleartextFile.toPath()).stream().toList();
if (ciphertextPath != null) { revealOrCopyPaths(ciphertextPaths);
revealOrCopyPaths(List.of(ciphertextPath));
}
}
}
@FXML
public void chooseEncryptedFileAndCopyNames() {
var fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.decryptName.filePickerTitle"));
fileChooser.setInitialDirectory(vault.getValue().getPath().toFile());
var ciphertextNode = fileChooser.showOpenDialog(mainWindow);
if (ciphertextNode != null) {
var nodeName = getCleartextName(ciphertextNode.toPath());
copyDecryptedNamesToClipboard(List.of(nodeName));
}
}
private void copyDecryptedNamesToClipboard(List<CipherToCleartext> mapping) {
if (mapping.size() == 1) {
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, mapping.getFirst().cleartext));
} else {
var content = mapping.stream().map(CipherToCleartext::toString).collect(Collectors.joining("\n"));
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, content));
}
cleartextNamesCopied.setValue(true);
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
cleartextNamesCopied.set(false);
});
}
@Nullable
private CipherToCleartext getCleartextName(Path ciphertextNode) {
try {
return new CipherToCleartext(ciphertextNode.getFileName().toString(), vault.get().getCleartextName(ciphertextNode));
} catch (IOException e) {
LOG.warn("Failed to decrypt filename for {}", ciphertextNode, e);
return null;
} }
} }
@@ -192,17 +165,16 @@ public class VaultDetailUnlockedController implements FxController {
return path.startsWith(Path.of(mountPoint.getValue())); return path.startsWith(Path.of(mountPoint.getValue()));
} }
@Nullable private Optional<Path> getCiphertextPath(Path path) {
private Path getCiphertextPath(Path path) {
if (!startsWithVaultAccessPoint(path)) { if (!startsWithVaultAccessPoint(path)) {
LOG.debug("Path does not start with mount point of selected vault: {}", path); LOG.debug("Path does not start with access point of selected vault: {}", path);
return null; return Optional.empty();
} }
try { try {
return vault.get().getCiphertextPath(path); return Optional.of(vault.get().getCiphertextPath(path));
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Unable to get ciphertext path from path: {}", path, e); LOG.warn("Unable to get ciphertext path from path: {}", path, e);
return null; return Optional.empty();
} }
} }
@@ -234,40 +206,6 @@ public class VaultDetailUnlockedController implements FxController {
}); });
} }
private VaultStatisticsComponent buildVaultStats(Vault vault) {
return vaultStatsBuilder.vault(vault).build();
}
@FXML
public void revealAccessLocation() {
vaultService.reveal(vault.get());
}
@FXML
public void copyMountUri() {
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(mountPoint.getValue());
Clipboard.getSystemClipboard().setContent(clipboardContent);
}
@FXML
public void lock() {
appWindows.startLockWorkflow(vault.get(), mainWindow);
}
@FXML
public void showVaultStatistics() {
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
}
record CipherToCleartext(String ciphertext, String cleartext) {
@Override
public String toString() {
return ciphertext + " > " + cleartext;
}
}
/* Getter/Setter */ /* Getter/Setter */
public ReadOnlyObjectProperty<Vault> vaultProperty() { public ReadOnlyObjectProperty<Vault> vaultProperty() {
@@ -309,12 +247,4 @@ public class VaultDetailUnlockedController implements FxController {
public boolean isCiphertextPathsCopied() { public boolean isCiphertextPathsCopied() {
return ciphertextPathsCopied.get(); return ciphertextPathsCopied.get();
} }
public BooleanProperty cleartextNamesCopiedProperty() {
return cleartextNamesCopied;
}
public boolean isCleartextNamesCopied() {
return cleartextNamesCopied.get();
}
} }
@@ -13,16 +13,10 @@ import javax.inject.Inject;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.layout.HBox;
// unscoped because each cell needs its own controller // unscoped because each cell needs its own controller
public class VaultListCellController implements FxController { public class VaultListCellController implements FxController {
private static final Insets COMPACT_INSETS = new Insets(6, 12, 6, 12);
private static final Insets DEFAULT_INSETS = new Insets(12);
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>(); private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private final ObservableValue<FontAwesome5Icon> glyph; private final ObservableValue<FontAwesome5Icon> glyph;
private final ObservableValue<Boolean> compactMode; private final ObservableValue<Boolean> compactMode;
@@ -31,8 +25,6 @@ public class VaultListCellController implements FxController {
/* FXML */ /* FXML */
public FontAwesome5IconView vaultStateView; public FontAwesome5IconView vaultStateView;
@FXML
public HBox vaultListCell;
@Inject @Inject
VaultListCellController(Settings settings) { VaultListCellController(Settings settings) {
@@ -45,7 +37,6 @@ public class VaultListCellController implements FxController {
.onCondition(vault.flatMap(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals).orElse(false)) // .onCondition(vault.flatMap(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals).orElse(false)) //
.afterStop(() -> vaultStateView.setRotate(0)) // .afterStop(() -> vaultStateView.setRotate(0)) //
.build(); .build();
this.vaultListCell.paddingProperty().bind(compactMode.map(c -> c ? COMPACT_INSETS : DEFAULT_INSETS));
} }
// TODO deduplicate w/ VaultDetailController // TODO deduplicate w/ VaultDetailController
@@ -1,13 +1,11 @@
package org.cryptomator.ui.mainwindow; package org.cryptomator.ui.mainwindow;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.EventMap;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure; import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.common.VaultService;
@@ -25,11 +23,9 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Side; import javafx.geometry.Side;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu; import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.ContextMenuEvent;
@@ -38,6 +34,7 @@ import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.File; import java.io.File;
@@ -69,8 +66,6 @@ public class VaultListController implements FxController {
private final VaultListCellFactory cellFactory; private final VaultListCellFactory cellFactory;
private final AddVaultWizardComponent.Builder addVaultWizard; private final AddVaultWizardComponent.Builder addVaultWizard;
private final BooleanBinding emptyVaultList; private final BooleanBinding emptyVaultList;
private final EventMap eventMap;
private final BooleanProperty newEventsPresent;
private final VaultListManager vaultListManager; private final VaultListManager vaultListManager;
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty(); private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
@@ -81,7 +76,7 @@ public class VaultListController implements FxController {
public ListView<Vault> vaultList; public ListView<Vault> vaultList;
public StackPane root; public StackPane root;
@FXML @FXML
private Button addVaultButton; private HBox addVaultButton;
@FXML @FXML
private ContextMenu addVaultContextMenu; private ContextMenu addVaultContextMenu;
@@ -96,8 +91,7 @@ public class VaultListController implements FxController {
ResourceBundle resourceBundle, // ResourceBundle resourceBundle, //
FxApplicationWindows appWindows, // FxApplicationWindows appWindows, //
Settings settings, // Settings settings, //
Dialogs dialogs, // Dialogs dialogs) {
EventMap eventMap) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
this.vaults = vaults; this.vaults = vaults;
this.selectedVault = selectedVault; this.selectedVault = selectedVault;
@@ -110,13 +104,6 @@ public class VaultListController implements FxController {
this.dialogs = dialogs; this.dialogs = dialogs;
this.emptyVaultList = Bindings.isEmpty(vaults); this.emptyVaultList = Bindings.isEmpty(vaults);
this.eventMap = eventMap;
this.newEventsPresent = new SimpleBooleanProperty(false);
eventMap.addListener((MapChangeListener<? super EventMap.EventKey, ? super VaultEvent>) change -> {
if (change.wasAdded()) {
newEventsPresent.setValue(true);
}
});
selectedVault.addListener(this::selectedVaultDidChange); selectedVault.addListener(this::selectedVaultDidChange);
cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0); cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0);
@@ -276,11 +263,6 @@ public class VaultListController implements FxController {
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY); appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
} }
@FXML
public void showEventViewer() {
appWindows.showEventViewer();
newEventsPresent.setValue(false);
}
// Getter and Setter // Getter and Setter
public BooleanBinding emptyVaultListProperty() { public BooleanBinding emptyVaultListProperty() {
@@ -307,11 +289,4 @@ public class VaultListController implements FxController {
return cellSize.getValue(); return cellSize.getValue();
} }
public ObservableValue<Boolean> newEventsPresentProperty() {
return newEventsPresent;
}
public boolean getNewEventsPresent() {
return newEventsPresent.getValue();
}
} }
@@ -16,7 +16,7 @@ import javafx.fxml.FXML;
public class WelcomeController implements FxController { public class WelcomeController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/desktop/getting-started/"; private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/en/1.7/desktop/getting-started/";
private final Application application; private final Application application;
private final BooleanBinding noVaultPresent; private final BooleanBinding noVaultPresent;
@@ -10,7 +10,7 @@ import javafx.stage.Stage;
public class MigrationImpossibleController implements FxController { public class MigrationImpossibleController implements FxController {
private static final String HELP_URI = "https://docs.cryptomator.org/help/manual-migration/"; private static final String HELP_URI = "https://docs.cryptomator.org/en/1.7/help/manual-migration/";
private final Application application; private final Application application;
private final Stage window; private final Stage window;
@@ -2,6 +2,7 @@ package org.cryptomator.ui.preferences;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.autostart.AutoStartProvider; import org.cryptomator.integrations.autostart.AutoStartProvider;
@@ -27,10 +28,6 @@ import javafx.stage.Stage;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@PreferencesScoped @PreferencesScoped
public class GeneralPreferencesController implements FxController { public class GeneralPreferencesController implements FxController {
@@ -45,7 +42,6 @@ public class GeneralPreferencesController implements FxController {
private final Environment environment; private final Environment environment;
private final List<KeychainAccessProvider> keychainAccessProviders; private final List<KeychainAccessProvider> keychainAccessProviders;
private final KeychainManager keychain; private final KeychainManager keychain;
private final ExecutorService backgroundExecutor;
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
public CheckBox useKeychainCheckbox; public CheckBox useKeychainCheckbox;
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox; public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
@@ -57,18 +53,13 @@ public class GeneralPreferencesController implements FxController {
public CheckBox autoStartCheckbox; public CheckBox autoStartCheckbox;
public ToggleGroup nodeOrientation; public ToggleGroup nodeOrientation;
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
@Inject @Inject
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, // GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, Environment environment, FxApplicationWindows appWindows) {
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
this.window = window; this.window = window;
this.settings = settings; this.settings = settings;
this.autoStartProvider = autoStartProvider; this.autoStartProvider = autoStartProvider;
this.keychainAccessProviders = keychainAccessProviders; this.keychainAccessProviders = keychainAccessProviders;
this.keychain = keychain; this.keychain = keychain;
this.backgroundExecutor = backgroundExecutor;
this.quickAccessServices = QuickAccessService.get().toList(); this.quickAccessServices = QuickAccessService.get().toList();
this.application = application; this.application = application;
this.environment = environment; this.environment = environment;
@@ -89,7 +80,7 @@ public class GeneralPreferencesController implements FxController {
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter); Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain); useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not()); keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntries); keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntriesOnMac);
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess); useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices); var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
@@ -100,21 +91,31 @@ public class GeneralPreferencesController implements FxController {
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not()); quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
} }
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) { public void migrateKeychainEntriesOnMac(Observable observable) {
//currently, we only migrate on macOS (touchID vs regular keychain) if (!SystemUtils.IS_OS_MAC) {
if (SystemUtils.IS_OS_MAC) { return;
var idsAndNames = settings.directories.stream().collect(Collectors.toMap(vs -> vs.id, vs -> vs.displayName.getValue())); }
if (!idsAndNames.isEmpty()) {
if (LOG.isDebugEnabled()) { var provider = keychainBackendChoiceBox.getSelectionModel().getSelectedItem();
LOG.debug("Migrating keychain entries {} from {} to {}", idsAndNames.keySet(), oldProvider.displayName(), newProvider.displayName()); var providerId = "org.cryptomator.macos.keychain.MacSystemKeychainAccess";
var isSystemKeychain = provider.getClass().getName().equals(providerId);
List<String> vaults = settings.directories.stream()
.map(vault -> vault.id)
.toList();
if (!vaults.isEmpty()) {
LOG.info("Migrating keychain entries for vaults: {}", vaults);
}
for (String vaultId :vaults) {
try {
if (keychain.isPassphraseStored(vaultId)) {
var passphrase = keychain.loadPassphrase(vaultId);
keychain.deletePassphrase(vaultId);
keychain.storePassphrase(vaultId, vaultId, new Passphrase(passphrase), !isSystemKeychain);
} }
keychainMigrations = keychainMigrations.thenRunAsync(() -> { } catch (KeychainAccessException e) {
try { LOG.error("Failed to migrate keychain entries.", e);
KeychainManager.migrate(oldProvider, newProvider, idsAndNames);
} catch (KeychainAccessException e) {
LOG.warn("Failed to migrate all entries from {} to {}", oldProvider.displayName(), newProvider.displayName(), e);
}
}, backgroundExecutor);
} }
} }
} }
@@ -19,8 +19,6 @@ import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay; import javafx.scene.control.ContentDisplay;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -34,10 +32,7 @@ import java.util.ResourceBundle;
@PreferencesScoped @PreferencesScoped
public class UpdatesPreferencesController implements FxController { public class UpdatesPreferencesController implements FxController {
private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" // private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
+ "?utm_source=cryptomator-desktop" //
+ "&utm_medium=update-notification&" //
+ "utm_campaign=app-update-%s";
private final Application application; private final Application application;
private final Environment environment; private final Environment environment;
@@ -55,7 +50,6 @@ public class UpdatesPreferencesController implements FxController {
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false); private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
private final DateTimeFormatter formatter; private final DateTimeFormatter formatter;
private final BooleanBinding upToDate; private final BooleanBinding upToDate;
private final String downloadsUri;
/* FXML */ /* FXML */
public CheckBox checkForUpdatesCheckbox; public CheckBox checkForUpdatesCheckbox;
@@ -71,13 +65,12 @@ public class UpdatesPreferencesController implements FxController {
this.latestVersion = updateChecker.latestVersionProperty(); this.latestVersion = updateChecker.latestVersionProperty();
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty(); this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck); this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
this.currentVersion = environment.getAppVersion(); this.currentVersion = updateChecker.getCurrentVersion();
this.updateAvailable = updateChecker.updateAvailableProperty(); this.updateAvailable = updateChecker.updateAvailableProperty();
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion)); this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
this.checkFailed = updateChecker.checkFailedProperty(); this.checkFailed = updateChecker.checkFailedProperty();
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck); this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII));
} }
public void initialize() { public void initialize() {
@@ -100,7 +93,7 @@ public class UpdatesPreferencesController implements FxController {
@FXML @FXML
public void visitDownloadsPage() { public void visitDownloadsPage() {
application.getHostServices().showDocument(downloadsUri); application.getHostServices().showDocument(DOWNLOADS_URI);
} }
@FXML @FXML
@@ -22,7 +22,7 @@ import java.util.ResourceBundle;
@PreferencesScoped @PreferencesScoped
public class VolumePreferencesController implements FxController { public class VolumePreferencesController implements FxController {
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/desktop/volume-type/"; public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
public static final int MIN_PORT = 1024; public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535; public static final int MAX_PORT = 65535;
@@ -3,7 +3,7 @@ package org.cryptomator.ui.sharevault;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.application.Application; import javafx.application.Application;
@@ -33,7 +33,8 @@ public class ShareVaultController implements FxController {
this.window = window; this.window = window;
this.application = application; this.application = application;
this.vault = vault; this.vault = vault;
this.hubVault = KeyLoadingStrategy.isHubVault(vault.getVaultSettings().lastKnownKeyLoader.get()); var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
} }
@FXML @FXML
@@ -10,7 +10,6 @@ import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
@@ -26,8 +25,6 @@ import javafx.scene.Scene;
import javafx.stage.Screen; import javafx.stage.Screen;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.nio.file.ReadOnlyFileSystemException;
import java.util.concurrent.TimeUnit;
/** /**
* A multi-step task that consists of background activities as well as user interaction. * A multi-step task that consists of background activities as well as user interaction.
@@ -49,7 +46,6 @@ public class UnlockWorkflow extends Task<Void> {
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
private final KeyLoadingStrategy keyLoadingStrategy; private final KeyLoadingStrategy keyLoadingStrategy;
private final ObjectProperty<IllegalMountPointException> illegalMountPointException; private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
private final Dialogs dialogs;
@Inject @Inject
UnlockWorkflow(@PrimaryStage Stage mainWindow, // UnlockWorkflow(@PrimaryStage Stage mainWindow, //
@@ -61,8 +57,7 @@ public class UnlockWorkflow extends Task<Void> {
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, // @FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
FxApplicationWindows appWindows, // FxApplicationWindows appWindows, //
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, // @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException, // @UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
Dialogs dialogs) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
this.window = window; this.window = window;
this.vault = vault; this.vault = vault;
@@ -73,7 +68,6 @@ public class UnlockWorkflow extends Task<Void> {
this.appWindows = appWindows; this.appWindows = appWindows;
this.keyLoadingStrategy = keyLoadingStrategy; this.keyLoadingStrategy = keyLoadingStrategy;
this.illegalMountPointException = illegalMountPointException; this.illegalMountPointException = illegalMountPointException;
this.dialogs = dialogs;
} }
@Override @Override
@@ -150,36 +144,11 @@ public class UnlockWorkflow extends Task<Void> {
switch (throwable) { switch (throwable) {
case IllegalMountPointException e -> handleIllegalMountPointError(e); case IllegalMountPointException e -> handleIllegalMountPointError(e);
case ConflictingMountServiceException _ -> handleConflictingMountServiceException(); case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
case ReadOnlyFileSystemException _ -> handleReadOnlyFileSystem();
default -> handleGenericError(throwable); default -> handleGenericError(throwable);
} }
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED); vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
} }
private void handleReadOnlyFileSystem() {
var readOnlyDialog = dialogs.prepareRetryIfReadonlyDialog(mainWindow, stage -> {
stage.close();
this.retry();
}).build();
Platform.runLater(readOnlyDialog::showAndWait);
}
private void retry() {
try {
vault.getVaultSettings().usesReadOnlyMode.set(true);
var isLocked = vault.stateProperty().awaitState(VaultState.Value.LOCKED, 5, TimeUnit.SECONDS);
if (!isLocked) {
LOG.error("Vault did not changed to LOCKED state within 5 seconds. Aborting unlock retry.");
} else {
appWindows.startUnlockWorkflow(vault, mainWindow);
}
} catch (InterruptedException e) {
LOG.error("Waiting for LOCKED vault state was interrupted. Aborting unlock retry.", e);
Thread.currentThread().interrupt();
}
}
@Override @Override
protected void cancelled() { protected void cancelled() {
LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName()); LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName());
@@ -17,8 +17,6 @@ import org.cryptomator.ui.preferences.VolumePreferencesController;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.application.Application; import javafx.application.Application;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@@ -52,6 +50,7 @@ public class MountOptionsController implements FxController {
private final ObservableValue<String> defaultMountFlags; private final ObservableValue<String> defaultMountFlags;
private final ObservableValue<Boolean> mountpointDirSupported; private final ObservableValue<Boolean> mountpointDirSupported;
private final ObservableValue<Boolean> mountpointDriveLetterSupported; private final ObservableValue<Boolean> mountpointDriveLetterSupported;
private final ObservableValue<Boolean> readOnlySupported;
private final ObservableValue<Boolean> mountFlagsSupported; private final ObservableValue<Boolean> mountFlagsSupported;
private final ObservableValue<Boolean> defaultMountServiceSelected; private final ObservableValue<Boolean> defaultMountServiceSelected;
private final ObservableValue<String> directoryPath; private final ObservableValue<String> directoryPath;
@@ -61,7 +60,6 @@ public class MountOptionsController implements FxController {
private final ObservableValue<MountService> selectedMountService; private final ObservableValue<MountService> selectedMountService;
private final ObservableValue<Boolean> selectedMountServiceRequiresRestart; private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
private final ObservableValue<Boolean> loopbackPortChangeable; private final ObservableValue<Boolean> loopbackPortChangeable;
private final ObservableBooleanValue readOnlyOptionAllowed;
//-- FXML objects -- //-- FXML objects --
@@ -110,10 +108,10 @@ public class MountOptionsController implements FxController {
}); });
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS)); this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true); this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true);
this.readOnlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT)); this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER)); this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null); this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null);
this.readOnlyOptionAllowed = BooleanBinding.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY))).or(vaultSettings.usesReadOnlyMode);
} }
private MountService reselectMountService() { private MountService reselectMountService() {
@@ -347,12 +345,12 @@ public class MountOptionsController implements FxController {
return mountpointDriveLetterSupported.getValue(); return mountpointDriveLetterSupported.getValue();
} }
public ObservableValue<Boolean> readOnlyOptionAllowedProperty() { public ObservableValue<Boolean> readOnlySupportedProperty() {
return readOnlyOptionAllowed; return readOnlySupported;
} }
public boolean isReadOnlyOptionAllowed() { public boolean isReadOnlySupported() {
return readOnlyOptionAllowed.getValue(); return readOnlySupported.getValue();
} }
public ObservableValue<String> directoryPathProperty() { public ObservableValue<String> directoryPathProperty() {
@@ -3,7 +3,6 @@ package org.cryptomator.ui.vaultoptions;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy; import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -43,11 +42,11 @@ public class VaultOptionsController implements FxController {
window.setOnShowing(this::windowWillAppear); window.setOnShowing(this::windowWillAppear);
selectedTabProperty.addListener(observable -> this.selectChosenTab()); selectedTabProperty.addListener(observable -> this.selectChosenTab());
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged()); tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
var vaultKeyLoader = vault.getVaultSettings().lastKnownKeyLoader.get(); var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
if(!KeyLoadingStrategy.isMasterkeyFileVault(vaultKeyLoader)){ if(!vaultScheme.equals(MasterkeyFileLoadingStrategy.SCHEME)){
tabPane.getTabs().remove(keyTab); tabPane.getTabs().remove(keyTab);
} }
if(!KeyLoadingStrategy.isHubVault(vaultKeyLoader)){ if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){
tabPane.getTabs().remove(hubTab); tabPane.getTabs().remove(hubTab);
} }
@@ -15,7 +15,7 @@ import java.io.UncheckedIOException;
@WrongFileAlertScoped @WrongFileAlertScoped
public class WrongFileAlertController implements FxController { public class WrongFileAlertController implements FxController {
private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/desktop/accessing-vaults/"; private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/en/1.7/desktop/accessing-vaults/";
private final Application app; private final Application app;
private final Stage window; private final Stage window;
+2 -86
View File
@@ -16,10 +16,6 @@
src: url('opensans_bold.ttf'); src: url('opensans_bold.ttf');
} }
@font-face {
src: url('firacode_regular.ttf');
}
/******************************************************************************* /*******************************************************************************
* * * *
* Root Styling & Colors * * Root Styling & Colors *
@@ -129,13 +125,6 @@
-fx-fill: TEXT_FILL; -fx-fill: TEXT_FILL;
} }
.cryptic-text {
-fx-background-color: MAIN_BG;
-fx-text-fill: TEXT_FILL;
-fx-font-family: 'Fira Code';
-fx-font-size: 1.1em;
}
/******************************************************************************* /*******************************************************************************
* * * *
* Glyph Icons * * Glyph Icons *
@@ -194,37 +183,19 @@
} }
.main-window .button-bar { .main-window .button-bar {
-fx-min-height:42px;
-fx-max-height:42px;
-fx-background-color: MAIN_BG; -fx-background-color: MAIN_BG;
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
-fx-border-width: 1px 0 0 0; -fx-border-width: 1px 0 0 0;
} }
.main-window .button-bar .button-left { .main-window .button-left {
-fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-width: 0 1px 0 0; -fx-border-width: 0 1px 0 0;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
} }
.main-window .button-bar .button-right { .main-window .button-right {
-fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-width: 0 0 0 1px; -fx-border-width: 0 0 0 1px;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
}
.main-window .button-bar .button-left:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
}
.main-window .button-bar .button-right:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
} }
/******************************************************************************* /*******************************************************************************
@@ -341,42 +312,6 @@
-fx-fill: transparent; -fx-fill: transparent;
} }
/*******************************************************************************
* *
* Event List *
* *
******************************************************************************/
.event-window .button-bar {
-fx-min-height:42px;
-fx-max-height:42px;
-fx-background-color: MAIN_BG;
-fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent;
-fx-border-width: 0 0 1px 0;
}
.event-window .button-bar .button-right {
-fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL;
-fx-border-width: 0 0 0 1px;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
}
.event-window .button-bar .button-right:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
}
.event-window .list-view .list-cell:hover {
-fx-background-color: CONTROL_BG_SELECTED;
}
.event-window .list-view .list-cell:selected {
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
-fx-background-insets: 0, 0 0 0 3px;
}
/******************************************************************************* /*******************************************************************************
* * * *
* NotificationBar * * NotificationBar *
@@ -406,12 +341,6 @@
-fx-background-color: PRIMARY; -fx-background-color: PRIMARY;
} }
.notification-debug:hover .notification-label,
.notification-update:hover .notification-label,
.notification-support:hover .notification-label {
-fx-underline:true;
}
/******************************************************************************* /*******************************************************************************
* * * *
* ScrollBar * * ScrollBar *
@@ -646,19 +575,6 @@
-fx-graphic-text-gap: 9px; -fx-graphic-text-gap: 9px;
} }
/*******************************************************************************
* *
* Update indicator
* *
******************************************************************************/
.update-indicator {
-fx-background-color: white, RED_5;
-fx-background-insets: 1px, 2px;
-fx-background-radius: 6px, 5px;
-fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0);
}
/******************************************************************************* /*******************************************************************************
* * * *
* Hyperlinks * * Hyperlinks *
Binary file not shown.
+2 -90
View File
@@ -16,10 +16,6 @@
src: url('opensans_bold.ttf'); src: url('opensans_bold.ttf');
} }
@font-face {
src: url('firacode_regular.ttf');
}
/******************************************************************************* /*******************************************************************************
* * * *
* Root Styling & Colors * * Root Styling & Colors *
@@ -128,13 +124,6 @@
-fx-fill: TEXT_FILL; -fx-fill: TEXT_FILL;
} }
.cryptic-text {
-fx-background-color: MAIN_BG;
-fx-text-fill: TEXT_FILL;
-fx-font-family: 'Fira Code';
-fx-font-size: 1.1em;
}
/******************************************************************************* /*******************************************************************************
* * * *
* Glyph Icons * * Glyph Icons *
@@ -193,8 +182,6 @@
} }
.main-window .button-bar { .main-window .button-bar {
-fx-min-height:42px;
-fx-max-height:42px;
-fx-background-color: MAIN_BG; -fx-background-color: MAIN_BG;
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
-fx-border-width: 1px 0 0 0; -fx-border-width: 1px 0 0 0;
@@ -203,27 +190,11 @@
.main-window .button-bar .button-left { .main-window .button-bar .button-left {
-fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-width: 0 1px 0 0; -fx-border-width: 0 1px 0 0;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
} }
.main-window .button-bar .button-right { .main-window .button-bar .button-right {
-fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-color: CONTROL_BORDER_NORMAL;
-fx-border-width: 0 0 0 1px; -fx-border-width: 0 0 0 1px;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
}
.main-window .button-bar .button-left:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
}
.main-window .button-bar .button-right:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
} }
/******************************************************************************* /*******************************************************************************
@@ -312,10 +283,6 @@
-fx-font-size: 1.0em; -fx-font-size: 1.0em;
} }
.list-cell .header-misc {
-fx-font-size: 1.0em;
}
.list-cell .detail-label { .list-cell .detail-label {
-fx-text-fill: TEXT_FILL_MUTED; -fx-text-fill: TEXT_FILL_MUTED;
-fx-font-size: 0.8em; -fx-font-size: 0.8em;
@@ -344,42 +311,6 @@
-fx-fill: transparent; -fx-fill: transparent;
} }
/*******************************************************************************
* *
* Event List *
* *
******************************************************************************/
.event-window .button-bar {
-fx-min-height:42px;
-fx-max-height:42px;
-fx-background-color: MAIN_BG;
-fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent;
-fx-border-width: 0 0 1px 0;
}
.event-window .button-bar .button-right {
-fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL;
-fx-border-width: 0 0 0 1px;
-fx-background-color: MAIN_BG;
-fx-background-radius: 0px;
-fx-min-height: 42px;
-fx-max-height: 42px;
}
.event-window .button-bar .button-right:armed {
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
}
.event-window .list-view .list-cell:hover {
-fx-background-color: CONTROL_BG_SELECTED;
}
.event-window .list-view .list-cell:selected {
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
-fx-background-insets: 0, 0 0 0 3px;
}
/******************************************************************************* /*******************************************************************************
* * * *
* NotificationBar * * NotificationBar *
@@ -409,12 +340,6 @@
-fx-background-color: PRIMARY; -fx-background-color: PRIMARY;
} }
.notification-debug:hover .notification-label,
.notification-update:hover .notification-label,
.notification-support:hover .notification-label {
-fx-underline:true;
}
/******************************************************************************* /*******************************************************************************
* * * *
* ScrollBar * * ScrollBar *
@@ -649,19 +574,6 @@
-fx-graphic-text-gap: 9px; -fx-graphic-text-gap: 9px;
} }
/*******************************************************************************
* *
* Update indicator
* *
******************************************************************************/
.update-indicator {
-fx-background-color: white, RED_5;
-fx-background-insets: 1px, 2px;
-fx-background-radius: 6px, 5px;
-fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0);
}
/******************************************************************************* /*******************************************************************************
* * * *
* Hyperlinks * * Hyperlinks *
@@ -874,11 +786,11 @@
/******************************************************************************* /*******************************************************************************
* * * *
* Dropdown button context menu * Add Vault - MenuItem *
* * * *
******************************************************************************/ ******************************************************************************/
.dropdown-button-context-menu-item { .add-vault-menu-item {
-fx-padding: 4px 8px; -fx-padding: 4px 8px;
} }
-35
View File
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Tooltip?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.eventview.EventViewController"
minWidth="300"
prefWidth="300"
styleClass="event-window"
>
<HBox styleClass="button-bar" alignment="CENTER">
<padding>
<Insets left="6" />
</padding>
<ChoiceBox fx:id="vaultFilterChoiceBox"/>
<Region HBox.hgrow="ALWAYS"/>
<Button styleClass="button-right" onAction="#clearEvents" contentDisplay="GRAPHIC_ONLY">
<graphic>
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
</graphic>
<tooltip>
<Tooltip text="%eventView.clearListButton.tooltip"/>
</tooltip>
</Button>
</HBox>
<ListView fx:id="eventListView" fixedCellSize="60" VBox.vgrow="ALWAYS"/>
</VBox>
@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.eventview.EventListCellController"
prefHeight="60"
prefWidth="200"
spacing="12"
alignment="CENTER_LEFT"
fx:id="root">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
<VBox alignment="CENTER" minWidth="20">
<FontAwesome5IconView glyph="${controller.icon}" HBox.hgrow="NEVER" glyphSize="16"/>
</VBox>
<VBox spacing="4" HBox.hgrow="ALWAYS">
<HBox spacing="4">
<Label styleClass="header-label" text="${controller.message}"/>
<Label styleClass="header-misc" text="${controller.count}" visible="${controller.vaultUnlocked}"/>
</HBox>
<Label text="${controller.description}"/>
</VBox>
<Button fx:id="eventActionsButton" contentDisplay="GRAPHIC_ONLY" onAction="#toggleEventActionsMenu" managed="${controller.actionsButtonVisible}" visible="${controller.actionsButtonVisible}">
<graphic>
<FontAwesome5IconView glyph="ELLIPSIS_V" glyphSize="16"/>
</graphic>
</Button>
<VBox alignment="CENTER" maxWidth="64" minWidth="64" visible="${!controller.actionsButtonVisible}" managed="${!controller.actionsButtonVisible}">
<Label text="${controller.eventLocalTime}" />
<Label text="${controller.eventLocalDate}" />
</VBox>
<fx:define>
<ContextMenu fx:id="eventActionsMenu"/>
</fx:define>
</HBox>
@@ -22,7 +22,7 @@
</ImageView> </ImageView>
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT"> <VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/> <FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
<Label text="© 2016 2025 Skymatic GmbH"/> <Label text="© 2016 2024 Skymatic GmbH"/>
</VBox> </VBox>
</HBox> </HBox>
+8 -8
View File
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?> <?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.control.ButtonBar?>
<HBox xmlns:fx="http://javafx.com/fxml" <HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.dialogs.SimpleDialogController" fx:controller="org.cryptomator.ui.dialogs.SimpleDialogController"
@@ -41,7 +41,7 @@
<Region VBox.vgrow="ALWAYS" minHeight="18"/> <Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CI"> <ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons> <buttons>
<Button text="${controller.cancelButtonText}" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#handleCancel" visible="${controller.cancelButtonVisible}" managed="${controller.cancelButtonVisible}"/> <Button text="${controller.cancelButtonText}" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#handleCancel"/>
<Button text="${controller.okButtonText}" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#handleOk"/> <Button text="${controller.okButtonText}" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#handleOk"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
@@ -1,14 +1,12 @@
<?import org.cryptomator.ui.controls.FontAwesome5IconView?> <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.ThroughputLabel?> <?import org.cryptomator.ui.controls.ThroughputLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?> <?import javafx.scene.control.Tooltip?>
<?import javafx.geometry.Insets?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.mainwindow.VaultDetailUnlockedController" fx:controller="org.cryptomator.ui.mainwindow.VaultDetailUnlockedController"
@@ -46,47 +44,28 @@
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
<HBox alignment="BOTTOM_CENTER"> <HBox alignment="BOTTOM_CENTER">
<StackPane visible="${controller.accessibleViaPath}" managed="${controller.accessibleViaPath}"> <HBox visible="${controller.accessibleViaPath}" managed="${controller.accessibleViaPath}">
<padding> <padding>
<Insets topRightBottomLeft="0"/> <Insets topRightBottomLeft="0"/>
</padding> </padding>
<Button fx:id="revealEncryptedDropZone" styleClass="drag-n-drop" text="%main.vaultDetail.locateEncryptedFileBtn" minWidth="120" maxWidth="180" prefHeight="72" wrapText="true" textAlignment="CENTER" onAction="#chooseDecryptedFileAndReveal" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}"> <Button fx:id="dropZone" styleClass="drag-n-drop" text="%main.vaultDetail.locateEncryptedFileBtn" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseFileAndReveal" contentDisplay="TOP" visible="${!controller.ciphertextPathsCopied}" managed="${!controller.ciphertextPathsCopied}">
<graphic> <graphic>
<Text styleClass="cryptic-text" text="abc → 101010"/> <FontAwesome5IconView glyph="FILE_DOWNLOAD" glyphSize="15"/>
</graphic> </graphic>
<tooltip> <tooltip>
<Tooltip text="%main.vaultDetail.locateEncryptedFileBtn.tooltip"/> <Tooltip text="%main.vaultDetail.locateEncryptedFileBtn.tooltip"/>
</tooltip> </tooltip>
</Button> </Button>
<Button styleClass="drag-n-drop" text="%main.vaultDetail.encryptedPathsCopied" minWidth="120" maxWidth="180" prefHeight="72" wrapText="true" textAlignment="CENTER" onAction="#chooseDecryptedFileAndReveal" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}"> <Button styleClass="drag-n-drop" text="%main.vaultDetail.encryptedPathsCopied" minWidth="120" maxWidth="180" wrapText="true" textAlignment="CENTER" onAction="#chooseFileAndReveal" contentDisplay="TOP" visible="${controller.ciphertextPathsCopied}" managed="${controller.ciphertextPathsCopied}">
<graphic> <graphic>
<FontAwesome5IconView glyph="CHECK" glyphSize="15"/> <FontAwesome5IconView glyph="CHECK" glyphSize="15"/>
</graphic> </graphic>
</Button> </Button>
</StackPane> </HBox>
<!-- decrypt file name -->
<StackPane>
<padding>
<Insets topRightBottomLeft="0"/>
</padding>
<Button fx:id="decryptNameDropZone" styleClass="drag-n-drop" text="%main.vaultDetail.decryptName.buttonLabel" minWidth="120" maxWidth="180" prefHeight="72" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndCopyNames" contentDisplay="TOP" visible="${!controller.cleartextNamesCopied}" managed="${!controller.cleartextNamesCopied}">
<graphic>
<Text styleClass="cryptic-text" text="101010 → abc"/>
</graphic>
<tooltip>
<Tooltip text="%main.vaultDetail.decryptName.tooltip"/>
</tooltip>
</Button>
<Button styleClass="drag-n-drop" text="%main.vaultDetail.decryptName.copied" minWidth="120" maxWidth="180" prefHeight="72" wrapText="true" textAlignment="CENTER" onAction="#chooseEncryptedFileAndCopyNames" contentDisplay="TOP" visible="${controller.cleartextNamesCopied}" managed="${controller.cleartextNamesCopied}">
<graphic>
<FontAwesome5IconView glyph="CHECK" glyphSize="15"/>
</graphic>
</Button>
</StackPane>
<Region HBox.hgrow="ALWAYS"/> <Region HBox.hgrow="ALWAYS"/>
<Button text="%main.vaultDetail.stats" minWidth="120" onAction="#showVaultStatistics" contentDisplay="BOTTOM" prefHeight="72"> <Button text="%main.vaultDetail.stats" minWidth="120" onAction="#showVaultStatistics" contentDisplay="BOTTOM">
<graphic> <graphic>
<VBox spacing="6"> <VBox spacing="6">
<HBox alignment="CENTER_RIGHT" spacing="6"> <HBox alignment="CENTER_RIGHT" spacing="6">
+20 -30
View File
@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?> <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?> <?import javafx.scene.control.ListView?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.shape.Arc?> <?import javafx.scene.shape.Arc?>
<StackPane xmlns:fx="http://javafx.com/fxml" <StackPane xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
@@ -36,42 +35,33 @@
</VBox> </VBox>
</StackPane> </StackPane>
<HBox styleClass="button-bar"> <HBox styleClass="button-bar">
<Button fx:id="addVaultButton" onMouseClicked="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY"> <HBox fx:id="addVaultButton" onMouseClicked="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20">
<graphic> <padding>
<FontAwesome5IconView glyph="PLUS" glyphSize="16"/> <Insets topRightBottomLeft="12"/>
</graphic> </padding>
</Button> <FontAwesome5IconView glyph="PLUS" HBox.hgrow="NEVER" glyphSize="16"/>
</HBox>
<Region HBox.hgrow="ALWAYS"/> <Region HBox.hgrow="ALWAYS"/>
<StackPane> <HBox onMouseClicked="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20">
<Button onMouseClicked="#showEventViewer" styleClass="button-right" minWidth="20" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false"> <padding>
<graphic> <Insets topRightBottomLeft="12"/>
<FontAwesome5IconView glyph="BELL" glyphSize="16"/> </padding>
</graphic> <FontAwesome5IconView glyph="COG" HBox.hgrow="NEVER" glyphSize="16"/>
<tooltip> </HBox>
<Tooltip text="%main.vaultlist.showEventsButton.tooltip"/>
</tooltip>
</Button>
<Region styleClass="update-indicator" visible="${controller.newEventsPresent}" mouseTransparent="true" StackPane.alignment="TOP_RIGHT" prefWidth="12" prefHeight="12" maxWidth="-Infinity" maxHeight="-Infinity"/>
</StackPane>
<Button onMouseClicked="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY">
<graphic>
<FontAwesome5IconView glyph="COG" glyphSize="16"/>
</graphic>
</Button>
</HBox> </HBox>
</VBox> </VBox>
<Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/> <Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/>
<fx:define> <fx:define>
<ContextMenu fx:id="addVaultContextMenu"> <ContextMenu fx:id="addVaultContextMenu">
<items> <items>
<MenuItem styleClass="dropdown-button-context-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault"> <MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault" >
<graphic> <graphic>
<FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14"/> <FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14" />
</graphic> </graphic>
</MenuItem> </MenuItem>
<MenuItem styleClass="dropdown-button-context-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault"> <MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault" >
<graphic> <graphic>
<FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14"/> <FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14" />
</graphic> </graphic>
</MenuItem> </MenuItem>
</items> </items>
+17 -12
View File
@@ -1,28 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?> <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tooltip?> <?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<HBox xmlns:fx="http://javafx.com/fxml" <HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:id="vaultListCell"
fx:controller="org.cryptomator.ui.mainwindow.VaultListCellController" fx:controller="org.cryptomator.ui.mainwindow.VaultListCellController"
prefHeight="60" prefHeight="60"
prefWidth="200" prefWidth="200"
spacing="12" spacing="12"
alignment="CENTER_LEFT"> alignment="CENTER_LEFT">
<!-- Remark Check the containing list view for a fixed cell size before editing height properties --> <!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
<VBox alignment="CENTER" minWidth="20"> <padding>
<FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/> <Insets topRightBottomLeft="12"/>
</VBox> </padding>
<VBox spacing="4" HBox.hgrow="ALWAYS"> <children>
<Label styleClass="header-label" text="${controller.vault.displayName}"/> <VBox alignment="CENTER" minWidth="20">
<Label styleClass="detail-label" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" visible="${!controller.compactMode}" managed="${!controller.compactMode}"> <FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
<tooltip> </VBox>
<Tooltip text="${controller.vault.displayablePath}"/> <VBox spacing="4" HBox.hgrow="ALWAYS">
</tooltip> <Label styleClass="header-label" text="${controller.vault.displayName}"/>
</Label> <Label styleClass="detail-label" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" visible="${!controller.compactMode}" managed="${!controller.compactMode}">
</VBox> <tooltip>
<Tooltip text="${controller.vault.displayablePath}"/>
</tooltip>
</Label>
</VBox>
</children>
</HBox> </HBox>
@@ -54,7 +54,7 @@
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/> <Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
</HBox> </HBox>
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/> <CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlySupported}" managed="${controller.readOnlySupported}"/>
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}"> <VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/> <CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
+4 -42
View File
@@ -2,7 +2,6 @@
additionalStyleSheets= additionalStyleSheets=
# Generics # Generics
generic.action.dismiss=Dismiss
## Button ## Button
generic.button.apply=Apply generic.button.apply=Apply
generic.button.back=Back generic.button.back=Back
@@ -178,7 +177,7 @@ hub.registerFailed.description.generic=An error was thrown in the registration p
hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device. hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device.
### Unauthorized ### Unauthorized
hub.unauthorized.message=Access denied hub.unauthorized.message=Access denied
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access. hub.unauthorized.description=Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.
### Requires Account Initialization ### Requires Account Initialization
hub.requireAccountInit.message=Action required hub.requireAccountInit.message=Action required
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
@@ -284,7 +283,7 @@ preferences.title=Preferences
## General ## General
preferences.general=General preferences.general=General
preferences.general.startHidden=Hide window when starting Cryptomator preferences.general.startHidden=Hide window when starting Cryptomator
preferences.general.autoCloseVaults=Lock vaults without asking when quitting application preferences.general.autoCloseVaults=Lock open vaults automatically when quitting application
preferences.general.debugLogging=Enable debug logging preferences.general.debugLogging=Enable debug logging
preferences.general.debugDirectory=Reveal log files preferences.general.debugDirectory=Reveal log files
preferences.general.autoStart=Launch Cryptomator on system start preferences.general.autoStart=Launch Cryptomator on system start
@@ -396,7 +395,6 @@ main.vaultlist.contextMenu.vaultoptions=Show Vault Options
main.vaultlist.contextMenu.reveal=Reveal Drive main.vaultlist.contextMenu.reveal=Reveal Drive
main.vaultlist.addVaultBtn.menuItemNew=Create New Vault... main.vaultlist.addVaultBtn.menuItemNew=Create New Vault...
main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault... main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault...
main.vaultlist.showEventsButton.tooltip=Open event view
##Notificaition ##Notificaition
main.notification.updateAvailable=Update is available. main.notification.updateAvailable=Update is available.
main.notification.support=Support Cryptomator. main.notification.support=Support Cryptomator.
@@ -425,11 +423,7 @@ main.vaultDetail.stats=Vault Statistics
main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File
main.vaultDetail.locateEncryptedFileBtn.tooltip=Choose a file from your vault to locate its encrypted counterpart main.vaultDetail.locateEncryptedFileBtn.tooltip=Choose a file from your vault to locate its encrypted counterpart
main.vaultDetail.encryptedPathsCopied=Paths Copied to Clipboard! main.vaultDetail.encryptedPathsCopied=Paths Copied to Clipboard!
main.vaultDetail.locateEncrypted.filePickerTitle=Select File Inside Vault main.vaultDetail.filePickerTitle=Select File Inside Vault
main.vaultDetail.decryptName.buttonLabel=Decrypt File Name
main.vaultDetail.decryptName.filePickerTitle=Select encrypted file
main.vaultDetail.decryptName.tooltip=Choose an encrypted vault file to decrypt its name
main.vaultDetail.decryptName.copied=Names copied to clipboard!
### Missing ### Missing
main.vaultDetail.missing.info=Cryptomator could not find a vault at this path. main.vaultDetail.missing.info=Cryptomator could not find a vault at this path.
main.vaultDetail.missing.recheck=Recheck main.vaultDetail.missing.recheck=Recheck
@@ -559,12 +553,6 @@ dokanySupportEnd.message=Support end for Dokany
dokanySupportEnd.description=The volume type Dokany is no longer supported by Cryptomator. Your settings are adjusted to use the default volume type now. You can view the default type in the preferences. dokanySupportEnd.description=The volume type Dokany is no longer supported by Cryptomator. Your settings are adjusted to use the default volume type now. You can view the default type in the preferences.
dokanySupportEnd.preferencesBtn=Open Preferences dokanySupportEnd.preferencesBtn=Open Preferences
#Retry If Readonly
retryIfReadonly.title=Restricted Vault Access
retryIfReadonly.message=No write access to vault directory
retryIfReadonly.description=Cryptomator cannot write to the vault directory. You can change the vault to be read-only and try again. This option can be disabled in the vault options.
retryIfReadonly.retry=Change and Retry
# Share Vault # Share Vault
shareVault.title=Share Vault shareVault.title=Share Vault
shareVault.message=Would you like to share your vault with others? shareVault.message=Would you like to share your vault with others?
@@ -583,30 +571,4 @@ shareVault.hub.message=How to share a Hub vault
shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps: shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps:
shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage. shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub. shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub.
shareVault.hub.openHub=Open Cryptomator Hub shareVault.hub.openHub=Open Cryptomator Hub
# Event View
eventView.title=Events
eventView.filter.allVaults=All
eventView.clearListButton.tooltip=Dismiss all
## event list entries
eventView.entry.vaultLocked.message=***********
eventView.entry.vaultLocked.description=Unlock "%s" for details
eventView.entry.conflictResolved.message=Resolved conflict
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
eventView.entry.conflict.message=Conflict resolution failed
eventView.entry.conflict.showDecrypted=Show decrypted, original file
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
eventView.entry.decryptionFailed.message=Decryption failed
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
eventView.entry.brokenDirFile.message=Broken directory link
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
eventView.entry.brokenFileNode.message=Broken filesystem node
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
@@ -118,6 +118,4 @@
#Dokany Support End #Dokany Support End
#Retry If Readonly
# Share Vault # Share Vault

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