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
157 changed files with 851 additions and 2760 deletions
+8 -8
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:
+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:
+11 -12
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
+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 -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
+18 -30
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-SNAPSHOT</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.0</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.2.4</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.3.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.5.2</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version> <cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
<cryptomator.webdav.version>2.0.7</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.2</jackson.version> <jackson.version>2.18.1</jackson.version>
<javafx.version>23.0.1</javafx.version> <javafx.version>22.0.2</javafx.version>
<jwt.version>4.4.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.16</logback.version> <logback.version>1.5.12</logback.version>
<slf4j.version>2.0.16</slf4j.version> <slf4j.version>2.0.16</slf4j.version>
<tinyoauth2.version>0.8.0</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.1</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>
@@ -208,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>
@@ -311,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>
@@ -337,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>
@@ -351,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>
@@ -430,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>
-6
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,11 +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;
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, //
@@ -0,0 +1,20 @@
package org.cryptomator;
import javafx.application.Platform;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class JavaFXUtil {
public static boolean startPlatform() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
try {
Platform.startup(latch::countDown);
} catch (IllegalStateException e) {
//already initialized
latch.countDown();
}
return latch.await(5, TimeUnit.SECONDS);
}
}
@@ -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);
} }
@@ -24,7 +24,7 @@ public class KeychainManager implements KeychainAccessProvider {
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) { KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
this.keychain = selectedKeychain; this.keychain = selectedKeychain;
this.passphraseStoredProperties = Caffeine.newBuilder() // this.passphraseStoredProperties = Caffeine.newBuilder() //
.weakValues() // .softValues() //
.build(this::createStoredPassphraseProperty); .build(this::createStoredPassphraseProperty);
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll()); keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
} }
@@ -43,8 +43,13 @@ public class KeychainManager implements KeychainAccessProvider {
} }
@Override @Override
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean ignored) throws KeychainAccessException { public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, displayName, passphrase); storePassphrase(key, displayName, passphrase, true); //TODO: currently only TouchID is using this parameter, so this is okayish
}
@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, displayName, passphrase, requireOsAuthentication);
setPassphraseStored(key, true); setPassphraseStored(key, true);
} }
@@ -101,15 +106,13 @@ public class KeychainManager implements KeychainAccessProvider {
} }
private void setPassphraseStored(String key, boolean value) { private void setPassphraseStored(String key, boolean value) {
BooleanProperty property = passphraseStoredProperties.getIfPresent(key); BooleanProperty property = passphraseStoredProperties.get(key, _ -> new SimpleBooleanProperty(value));
if (property != null) {
if (Platform.isFxApplicationThread()) { if (Platform.isFxApplicationThread()) {
property.set(value); property.set(value);
} else { } else {
Platform.runLater(() -> property.set(value)); Platform.runLater(() -> property.set(value));
} }
} }
}
/** /**
* Returns an observable property for use in the UI that tells whether a passphrase is stored for the given key. * Returns an observable property for use in the UI that tells whether a passphrase is stored for the given key.
@@ -134,4 +137,8 @@ public class KeychainManager implements KeychainAccessProvider {
} }
} }
public ObjectExpression<KeychainAccessProvider> getKeychainImplementation() {
return this.keychain;
}
} }
@@ -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.event.FileSystemEventAggregator;
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,7 +18,6 @@ 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;
@@ -46,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;
@@ -76,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 FileSystemEventAggregator fileSystemEventAggregator;
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);
@@ -88,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) {
FileSystemEventAggregator fileSystemEventAggregator) {
this.vaultSettings = vaultSettings; this.vaultSettings = vaultSettings;
this.configCache = configCache; this.configCache = configCache;
this.cryptoFileSystem = cryptoFileSystem; this.cryptoFileSystem = cryptoFileSystem;
@@ -106,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.fileSystemEventAggregator = fileSystemEventAggregator;
this.showingStats = new SimpleBooleanProperty(false); this.showingStats = new SimpleBooleanProperty(false);
this.quickAccessEntry = new AtomicReference<>(null); this.quickAccessEntry = new AtomicReference<>(null);
} }
@@ -117,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);
@@ -148,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);
} }
@@ -257,11 +243,6 @@ public class Vault {
} }
} }
private void consumeVaultEvent(FilesystemEvent e) {
fileSystemEventAggregator.put(this, e);
}
// ****************************************************************************** // ******************************************************************************
// Observable Properties // Observable Properties
// ******************************************************************************* // *******************************************************************************
@@ -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,8 +0,0 @@
package org.cryptomator.event;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import java.nio.file.Path;
public record FSEventBucket(Vault vault, Path idPath, Class<? extends FilesystemEvent> c) {}
@@ -1,5 +0,0 @@
package org.cryptomator.event;
import org.cryptomator.cryptofs.event.FilesystemEvent;
public record FSEventBucketContent(FilesystemEvent mostRecentEvent, int count) {}
@@ -1,101 +0,0 @@
package org.cryptomator.event;
import org.cryptomator.common.vaults.Vault;
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 javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@Singleton
public class FileSystemEventAggregator {
private final ConcurrentHashMap<FSEventBucket, FSEventBucketContent> map;
private final AtomicBoolean hasUpdates;
@Inject
public FileSystemEventAggregator() {
this.map = new ConcurrentHashMap<>();
this.hasUpdates = new AtomicBoolean(false);
}
/**
* Adds the given event to the map. If a bucket for this event already exists, only the count is updated and the event set as the most recent one.
*
* @param v Vault where the event occurred
* @param e Actual {@link FilesystemEvent}
*/
public void put(Vault v, FilesystemEvent e) {
var key = computeKey(v, e);
hasUpdates.set(true);
map.compute(key, (k, val) -> {
if (val == null) {
return new FSEventBucketContent(e, 1);
} else {
return new FSEventBucketContent(e, val.count() + 1);
}
});
}
/**
* Removes an event bucket from the map.
*/
public FSEventBucketContent remove(FSEventBucket key) {
hasUpdates.set(true);
return map.remove(key);
}
/**
* Clears the event map.
*/
public void clear() {
hasUpdates.set(true);
map.clear();
}
public boolean hasUpdates() {
return hasUpdates.get();
}
/**
* Clones the map entries into a collection.
* <p>
* The collection is first cleared, then all map entries are added in one bulk operation. Cleans the hasUpdates status.
*
* @param target collection which is first cleared and then the EntrySet copied to.
*/
public void cloneTo(Collection<Map.Entry<FSEventBucket, FSEventBucketContent>> target) {
hasUpdates.set(false);
target.clear();
target.addAll(map.entrySet());
}
/**
* Method to compute the identifying key for a given filesystem event
*
* @param v Vault where the event occurred
* @param event Actual {@link FilesystemEvent}
* @return a {@link FSEventBucket} used in the map and lru cache
*/
private static FSEventBucket computeKey(Vault v, FilesystemEvent event) {
var p = switch (event) {
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 FSEventBucket(v, p, event.getClass());
}
}
@@ -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(tmpDir); Files.deleteIfExists(tmpFile);
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Unable to delete temporary directory {}. Needs to be deleted manually.", tmpDir); LOG.warn("Unable to delete temporary file {}. Needs to be deleted manually.", tmpFile);
}
} }
} }
} }
@@ -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) {
@@ -128,6 +126,7 @@ public class SimpleDialog {
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,329 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
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.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.Map;
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 FileSystemEventAggregator fileSystemEventAggregator;
@Nullable
private final RevealPathService revealService;
private final ResourceBundle resourceBundle;
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
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(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
this.fileSystemEventAggregator = fileSystemEventAggregator;
this.revealService = revealService.orElseGet(() -> null);
this.resourceBundle = resourceBundle;
this.eventEntry = new SimpleObjectProperty<>(null);
this.eventMessage = new SimpleStringProperty();
this.eventDescription = new SimpleStringProperty();
this.eventIcon = new SimpleObjectProperty<>();
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().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 setEventEntry(@NotNull Map.Entry<FSEventBucket, FSEventBucketContent> item) {
eventEntry.set(item);
eventActionsMenu.hide();
eventActionsMenu.getItems().clear();
eventTooltip.setText(item.getKey().vault().getDisplayName());
addAction("generic.action.dismiss", () -> {
fileSystemEventAggregator.remove(item.getKey());
});
switch (item.getValue().mostRecentEvent()) {
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 if (eventEntry.getValue() != null) {
var e = eventEntry.getValue().getKey();
return resourceBundle.getString("eventView.entry.vaultLocked.description").formatted(e != null ? e.vault().getDisplayName() : "");
} else {
return "";
}
}
@FXML
public void toggleEventActionsMenu() {
var e = eventEntry.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 = eventEntry.getValue().getKey().vault();
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,66 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
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;
import java.util.Map;
@EventViewScoped
public class EventListCellFactory implements Callback<ListView<Map.Entry<FSEventBucket, FSEventBucketContent>>, ListCell<Map.Entry<FSEventBucket, FSEventBucketContent>>> {
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<Map.Entry<FSEventBucket, FSEventBucketContent>> call(ListView<Map.Entry<FSEventBucket, FSEventBucketContent>> 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<Map.Entry<FSEventBucket, FSEventBucketContent>> {
private final Parent root;
private final EventListCellController controller;
public Cell(Parent root, EventListCellController controller) {
this.root = root;
this.controller = controller;
}
@Override
protected void updateItem(Map.Entry<FSEventBucket, FSEventBucketContent> 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.setEventEntry(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,132 +0,0 @@
package org.cryptomator.ui.eventview;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxFSEventList;
import javax.inject.Inject;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
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.Map;
import java.util.ResourceBundle;
@EventViewScoped
public class EventViewController implements FxController {
private final FilteredList<Map.Entry<FSEventBucket, FSEventBucketContent>> filteredEventList;
private final ObservableList<Vault> vaults;
private final FileSystemEventAggregator aggregator;
private final SortedList<Map.Entry<FSEventBucket, FSEventBucketContent>> sortedEventList;
private final ObservableList<Vault> choiceBoxEntries;
private final ResourceBundle resourceBundle;
private final EventListCellFactory cellFactory;
@FXML
ChoiceBox<Vault> vaultFilterChoiceBox;
@FXML
ListView<Map.Entry<FSEventBucket, FSEventBucketContent>> eventListView;
@Inject
public EventViewController(FxFSEventList fxFSEventList, ObservableList<Vault> vaults, ResourceBundle resourceBundle, EventListCellFactory cellFactory, FileSystemEventAggregator aggregator) {
this.filteredEventList = fxFSEventList.getObservableList().filtered(_ -> true);
this.vaults = vaults;
this.aggregator = aggregator;
this.sortedEventList = new SortedList<>(filteredEventList, this::compareBuckets);
this.choiceBoxEntries = FXCollections.observableArrayList();
this.resourceBundle = resourceBundle;
this.cellFactory = cellFactory;
}
/**
* Comparison method for the lru cache. During comparsion the map is accessed.
* First the entries are compared by the event timestamp, then vaultId, then identifying path and lastly by class name.
*
* @param left an entry of a {@link FSEventBucket} and its content
* @param right another entry of a {@link FSEventBucket} plus content, compared to {@code left}
* @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
*/
private int compareBuckets(Map.Entry<FSEventBucket, FSEventBucketContent> left, Map.Entry<FSEventBucket, FSEventBucketContent> right) {
var t1 = left.getValue().mostRecentEvent().getTimestamp();
var t2 = right.getValue().mostRecentEvent().getTimestamp();
var timeComparison = t1.compareTo(t2);
if (timeComparison != 0) {
return -timeComparison; //we need the reverse timesorting
}
var vaultIdComparison = left.getKey().vault().getId().compareTo(right.getKey().vault().getId());
if (vaultIdComparison != 0) {
return vaultIdComparison;
}
var pathComparison = left.getKey().idPath().compareTo(right.getKey().idPath());
if (pathComparison != 0) {
return pathComparison;
}
return left.getKey().c().getName().compareTo(right.getKey().c().getName());
}
@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());
}
});
eventListView.setCellFactory(cellFactory);
eventListView.setItems(sortedEventList);
vaultFilterChoiceBox.setItems(choiceBoxEntries);
vaultFilterChoiceBox.valueProperty().addListener(this::applyVaultFilter);
vaultFilterChoiceBox.setConverter(new VaultConverter(resourceBundle));
}
private void applyVaultFilter(ObservableValue<? extends Vault> v, Vault oldV, Vault newV) {
if (newV == null) {
filteredEventList.setPredicate(_ -> true);
} else {
filteredEventList.setPredicate(e -> e.getKey().vault().equals(newV));
}
}
@FXML
void clearEvents() {
aggregator.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,67 +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 org.cryptomator.ui.fxapp.FxFSEventList;
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, FxFSEventList fxFSEventList) {
Stage stage = factory.create();
stage.setHeight(498);
stage.setTitle(resourceBundle.getString("eventView.title"));
stage.setResizable(true);
stage.initModality(Modality.NONE);
stage.focusedProperty().addListener((_,_,isFocused) -> {
if(isFocused) {
fxFSEventList.unreadEventsProperty().setValue(false);
}
});
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 {
}
@@ -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
); );
} }
@@ -29,10 +29,9 @@ public class FxApplication {
private final FxApplicationStyle applicationStyle; private final FxApplicationStyle applicationStyle;
private final FxApplicationTerminator applicationTerminator; private final FxApplicationTerminator applicationTerminator;
private final AutoUnlocker autoUnlocker; private final AutoUnlocker autoUnlocker;
private final FxFSEventList fxFSEventList; //not unused! By injecting it here, the object gets initiated the service starts
@Inject @Inject
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) { FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
this.startupTime = startupTime; this.startupTime = startupTime;
this.environment = environment; this.environment = environment;
this.settings = settings; this.settings = settings;
@@ -42,7 +41,6 @@ public class FxApplication {
this.applicationStyle = applicationStyle; this.applicationStyle = applicationStyle;
this.applicationTerminator = applicationTerminator; this.applicationTerminator = applicationTerminator;
this.autoUnlocker = autoUnlocker; this.autoUnlocker = autoUnlocker;
this.fxFSEventList = fxFSEventList;
} }
public void start() { public void start() {
@@ -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;
@@ -20,9 +19,6 @@ import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent; import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import javax.inject.Named;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -37,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 {
@@ -71,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,8 +135,7 @@ 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) {
@@ -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() {
return dialogs.prepareDokanySupportEndDialog(mainWindow.get().window(), stage -> {
showPreferencesWindow(SelectedPreferencesTab.VOLUME); showPreferencesWindow(SelectedPreferencesTab.VOLUME);
stage.close(); stage.close();
}).build(); }
).build().showAndWait(), Platform::runLater);
} }
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);
} }
} }
} }
@@ -1,65 +0,0 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@FxApplicationScoped
public class FxFSEventList {
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
private final FileSystemEventAggregator eventAggregator;
private final ScheduledFuture<?> scheduledTask;
private final BooleanProperty unreadEvents;
@Inject
public FxFSEventList(FileSystemEventAggregator fsEventAggregator, ScheduledExecutorService scheduler) {
this.events = FXCollections.observableArrayList();
this.eventAggregator = fsEventAggregator;
this.unreadEvents = new SimpleBooleanProperty(false);
this.scheduledTask = scheduler.scheduleWithFixedDelay(() -> {
if (fsEventAggregator.hasUpdates()) {
flush();
}
}, 1000, 1000, TimeUnit.MILLISECONDS);
}
/**
* Starts the clone task on the FX thread and wait till it is completed
*/
private void flush() {
var latch = new CountDownLatch(1);
Platform.runLater(() -> {
eventAggregator.cloneTo(events);
unreadEvents.setValue(true);
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> getObservableList() {
return events;
}
public BooleanProperty unreadEventsProperty() {
return unreadEvents;
}
}
@@ -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();
@@ -112,14 +112,14 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
} }
private void savePasswordToSystemkeychain(Passphrase passphrase) { private void savePasswordToSystemkeychain(Passphrase passphrase) {
if (keychain.isSupported()) {
try { try {
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) {
LOG.error("Failed to store passphrase in system keychain.", e); LOG.error("Failed to store passphrase in system keychain.", e);
} }
} }
}
private Path askUserForMasterkeyFilePath() throws InterruptedException { private Path askUserForMasterkeyFilePath() throws InterruptedException {
var comp = masterkeyFileChoice.build(); var comp = masterkeyFileChoice.build();
@@ -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,76 +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.widthProperty().addListener((_, _, _) -> savePositionalSettings());
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); window.heightProperty().addListener((_, _, _) -> savePositionalSettings());
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); window.xProperty().addListener((_, _, _) -> savePositionalSettings());
window.yProperty().addListener((_, _, _) -> savePositionalSettings());
} }
window.setOnShowing(this::checkDisplayBounds); private boolean neverTouched() {
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
settings.windowXPosition.bind(window.xProperty());
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) { public void savePositionalSettings() {
return x != 0 || y != 0 || width != 0 || height != 0; settings.windowWidth.setValue(window.getWidth());
} settings.windowHeight.setValue(window.getHeight());
settings.windowXPosition.setValue(window.getX());
private void checkDisplayBounds(WindowEvent windowEvent) { settings.windowYPosition.setValue(window.getY());
int x = settings.windowXPosition.get();
int y = settings.windowYPosition.get();
int width = settings.windowWidth.get();
int height = settings.windowHeight.get();
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
// If the position is (-32000, -32000), restore to the last saved position
if (window.getX() == -32000 && window.getY() == -32000) {
window.setX(x);
window.setY(y);
window.setWidth(width);
window.setHeight(height);
}
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) {
@@ -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;
} }
@@ -8,9 +8,9 @@ import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -21,7 +21,6 @@ public class VaultDetailLockedController implements FxController {
private final ReadOnlyObjectProperty<Vault> vault; private final ReadOnlyObjectProperty<Vault> vault;
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
private final VaultOptionsComponent.Factory vaultOptionsWindow; private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final KeychainManager keychain;
private final Stage mainWindow; private final Stage mainWindow;
private final ObservableValue<Boolean> passwordSaved; private final ObservableValue<Boolean> passwordSaved;
@@ -30,13 +29,11 @@ public class VaultDetailLockedController implements FxController {
this.vault = vault; this.vault = vault;
this.appWindows = appWindows; this.appWindows = appWindows;
this.vaultOptionsWindow = vaultOptionsWindow; this.vaultOptionsWindow = vaultOptionsWindow;
this.keychain = keychain;
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
if (keychain.isSupported() && !keychain.isLocked()) { this.passwordSaved = Bindings.createBooleanBinding(() -> {
this.passwordSaved = vault.flatMap(v -> keychain.getPassphraseStoredProperty(v.getId())).orElse(false); var v = vault.get();
} else { return v != null && keychain.getPassphraseStoredProperty(v.getId()).getValue();
this.passwordSaved = new SimpleBooleanProperty(false); }, vault, keychain.getKeychainImplementation());
}
} }
@FXML @FXML
@@ -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
@@ -10,7 +10,6 @@ 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;
import org.cryptomator.ui.dialogs.Dialogs; import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxFSEventList;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -27,7 +26,6 @@ import javafx.collections.ListChangeListener;
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;
@@ -36,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;
@@ -67,7 +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 BooleanProperty unreadEvents;
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;
@@ -78,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;
@@ -93,8 +91,7 @@ public class VaultListController implements FxController {
ResourceBundle resourceBundle, // ResourceBundle resourceBundle, //
FxApplicationWindows appWindows, // FxApplicationWindows appWindows, //
Settings settings, // Settings settings, //
Dialogs dialogs, // Dialogs dialogs) {
FxFSEventList fxFSEventList) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
this.vaults = vaults; this.vaults = vaults;
this.selectedVault = selectedVault; this.selectedVault = selectedVault;
@@ -107,7 +104,6 @@ public class VaultListController implements FxController {
this.dialogs = dialogs; this.dialogs = dialogs;
this.emptyVaultList = Bindings.isEmpty(vaults); this.emptyVaultList = Bindings.isEmpty(vaults);
this.unreadEvents = fxFSEventList.unreadEventsProperty();
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);
@@ -267,11 +263,6 @@ public class VaultListController implements FxController {
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY); appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
} }
@FXML
public void showEventViewer() {
appWindows.showEventViewer();
unreadEvents.setValue(false);
}
// Getter and Setter // Getter and Setter
public BooleanBinding emptyVaultListProperty() { public BooleanBinding emptyVaultListProperty() {
@@ -298,11 +289,4 @@ public class VaultListController implements FxController {
return cellSize.getValue(); return cellSize.getValue();
} }
public ObservableValue<Boolean> unreadEventsPresentProperty() {
return unreadEvents;
}
public boolean getUnreadEventsPresent() {
return unreadEvents.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;
@@ -1,10 +1,14 @@
package org.cryptomator.ui.preferences; package org.cryptomator.ui.preferences;
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.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.autostart.AutoStartProvider; import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException; import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.cryptomator.integrations.common.NamedServiceProvider; import org.cryptomator.integrations.common.NamedServiceProvider;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.quickaccess.QuickAccessService; import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
@@ -14,6 +18,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.application.Application; import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
@@ -36,6 +41,7 @@ public class GeneralPreferencesController implements FxController {
private final Application application; private final Application application;
private final Environment environment; private final Environment environment;
private final List<KeychainAccessProvider> keychainAccessProviders; private final List<KeychainAccessProvider> keychainAccessProviders;
private final KeychainManager keychain;
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
public CheckBox useKeychainCheckbox; public CheckBox useKeychainCheckbox;
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox; public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
@@ -48,11 +54,12 @@ public class GeneralPreferencesController implements FxController {
public ToggleGroup nodeOrientation; public ToggleGroup nodeOrientation;
@Inject @Inject
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, Application application, Environment environment, FxApplicationWindows appWindows) { GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, Environment environment, FxApplicationWindows appWindows) {
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.quickAccessServices = QuickAccessService.get().toList(); this.quickAccessServices = QuickAccessService.get().toList();
this.application = application; this.application = application;
this.environment = environment; this.environment = environment;
@@ -73,6 +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::migrateKeychainEntriesOnMac);
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess); useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices); var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
@@ -83,6 +91,35 @@ public class GeneralPreferencesController implements FxController {
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not()); quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
} }
public void migrateKeychainEntriesOnMac(Observable observable) {
if (!SystemUtils.IS_OS_MAC) {
return;
}
var provider = keychainBackendChoiceBox.getSelectionModel().getSelectedItem();
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);
}
} catch (KeychainAccessException e) {
LOG.error("Failed to migrate keychain entries.", e);
}
}
}
public boolean isAutoStartSupported() { public boolean isAutoStartSupported() {
return autoStartProvider.isPresent(); return autoStartProvider.isPresent();
} }
@@ -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 -72
View File
@@ -183,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;
} }
/******************************************************************************* /*******************************************************************************
@@ -330,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 *
@@ -395,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 *
@@ -635,16 +575,6 @@
-fx-graphic-text-gap: 9px; -fx-graphic-text-gap: 9px;
} }
/*******************************************************************************
* *
* Update indicator
* *
******************************************************************************/
.update-indicator {
-fx-fill: RED_5;
}
/******************************************************************************* /*******************************************************************************
* * * *
* Hyperlinks * * Hyperlinks *
+2 -76
View File
@@ -182,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;
@@ -192,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;
} }
/******************************************************************************* /*******************************************************************************
@@ -301,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;
@@ -333,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 *
@@ -398,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 *
@@ -638,16 +574,6 @@
-fx-graphic-text-gap: 9px; -fx-graphic-text-gap: 9px;
} }
/*******************************************************************************
* *
* Update indicator
* *
******************************************************************************/
.icon-update-indicator {
-fx-fill: RED_5;
}
/******************************************************************************* /*******************************************************************************
* * * *
* Hyperlinks * * Hyperlinks *
@@ -860,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>
+18 -34
View File
@@ -1,21 +1,16 @@
<?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.shape.Arc?> <?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.shape.Circle?> <?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.shape.Rectangle?> <?import javafx.scene.shape.Arc?>
<?import javafx.scene.layout.AnchorPane?>
<StackPane xmlns:fx="http://javafx.com/fxml" <StackPane xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:id="root" fx:id="root"
@@ -40,42 +35,31 @@
</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>
<AnchorPane mouseTransparent="true" minWidth="12" maxWidth="12" minHeight="12" maxHeight="12" StackPane.alignment="CENTER">
<Circle radius="4" styleClass="icon-update-indicator" AnchorPane.topAnchor="-8" AnchorPane.rightAnchor="-6" visible="${controller.unreadEventsPresent}" />
</AnchorPane>
</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>
+6 -1
View File
@@ -1,19 +1,23 @@
<?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 -->
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<VBox alignment="CENTER" minWidth="20"> <VBox alignment="CENTER" minWidth="20">
<FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/> <FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
</VBox> </VBox>
@@ -25,4 +29,5 @@
</tooltip> </tooltip>
</Label> </Label>
</VBox> </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"/>
+2 -36
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.
@@ -555,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?
@@ -580,29 +572,3 @@ shareVault.hub.description=In order to share the vault content with another team
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
+3 -12
View File
@@ -13,7 +13,6 @@ generic.button.copied=تم النسخ!
generic.button.done=تم generic.button.done=تم
generic.button.next=التالي generic.button.next=التالي
generic.button.print=طباعة generic.button.print=طباعة
generic.button.remove=حذف
# Error # Error
error.message=حدث خطأ ما error.message=حدث خطأ ما
@@ -106,6 +105,7 @@ addvaultwizard.success.unlockNow=افتح الان
removeVault.title=احذف الحافظة removeVault.title=احذف الحافظة
removeVault.message=حذف المخزن؟ removeVault.message=حذف المخزن؟
removeVault.description=سيؤدي هذا إلى نسيان Cryptomator لهذا المخزن فقط. يمكنك إضافته مرة أخرى لاحقاً. لن يتم حذف أي من الملفات المشفرة من القرص الصلب الخاص بك. removeVault.description=سيؤدي هذا إلى نسيان Cryptomator لهذا المخزن فقط. يمكنك إضافته مرة أخرى لاحقاً. لن يتم حذف أي من الملفات المشفرة من القرص الصلب الخاص بك.
removeVault.confirmBtn=احذف الحافظة
# Change Password # Change Password
changepassword.title=تغيير كلمة المرور changepassword.title=تغيير كلمة المرور
@@ -176,6 +176,7 @@ hub.registerFailed.description.generic=حدث خطأ في عملية تسجيل
hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل لمستخدم مختلف بالفعل. حاول تغيير حساب المستخدم أو استخدام جهاز مختلف. hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل لمستخدم مختلف بالفعل. حاول تغيير حساب المستخدم أو استخدام جهاز مختلف.
### Unauthorized ### Unauthorized
hub.unauthorized.message=تم رفض الوصول hub.unauthorized.message=تم رفض الوصول
hub.unauthorized.description=لم يتم بعد منح الإذن لجهازك بالوصول إلى هذا المخزن. اطلب من مالك المخزن أن يأذن بذلك.
### Requires Account Initialization ### Requires Account Initialization
hub.requireAccountInit.message=مطلوب اتخاذ إجراء hub.requireAccountInit.message=مطلوب اتخاذ إجراء
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
@@ -286,7 +287,6 @@ preferences.general.debugLogging=تمكين سجلات التصحيح
preferences.general.debugDirectory=عرض ملفات السجل preferences.general.debugDirectory=عرض ملفات السجل
preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام
preferences.general.keychainBackend=تخزين كلمات المرور مع preferences.general.keychainBackend=تخزين كلمات المرور مع
preferences.general.quickAccessService=إضافة الخزانات المفتوحة إلى منطقة الوصول السريع
## Interface ## Interface
preferences.interface=الواجهة preferences.interface=الواجهة
preferences.interface.theme=الشكل والمظهر preferences.interface.theme=الشكل والمظهر
@@ -300,7 +300,6 @@ preferences.interface.interfaceOrientation=اتجاه الواجهة
preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين
preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل) preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
preferences.interface.compactMode=تمكين قائمة الخزنة المدمجة
## Volume ## Volume
preferences.volume=القرص الإفتراضي preferences.volume=القرص الإفتراضي
preferences.volume.type=نوع القرص الافتراضي preferences.volume.type=نوع القرص الافتراضي
@@ -334,14 +333,12 @@ preferences.contribute.registeredFor=شهادة الداعم مسجلة لـ %s
preferences.contribute.noCertificate=ادعم Cryptomator واحصل على شهادة الداعم. إنها مثل مفتاح الترخيص لكن للأشخاص الرائعين الذين يستخدمون البرامج المجانية ؛-) preferences.contribute.noCertificate=ادعم Cryptomator واحصل على شهادة الداعم. إنها مثل مفتاح الترخيص لكن للأشخاص الرائعين الذين يستخدمون البرامج المجانية ؛-)
preferences.contribute.getCertificate=ليس لديك واحدة بعد؟ تعلم كيف يمكنك الحصول عليها. preferences.contribute.getCertificate=ليس لديك واحدة بعد؟ تعلم كيف يمكنك الحصول عليها.
preferences.contribute.promptText=قم بلصق رمز شهادة الداعم هنا preferences.contribute.promptText=قم بلصق رمز شهادة الداعم هنا
preferences.contribute.thankYou=نشكرك على دعمك لتطوير Cryptomator مفتوح المصدر!
preferences.contribute.donate=تبرع preferences.contribute.donate=تبرع
preferences.contribute.sponsor=الراعي
### Remove License Key Dialog ### Remove License Key Dialog
removeCert.title=إزالة الشهادة removeCert.title=إزالة الشهادة
removeCert.message=إزالة شهادة الداعم؟ removeCert.message=إزالة شهادة الداعم؟
removeCert.description=الميزات الأساسية لـ Cryptomator غير متأثرة بهذا. لا يتم تقييد الوصول إلى خزاناتك ولا يتم تخفيض مستوى الأمان. removeCert.confirmBtn=حذف
#<-- Add entries for donations and code/translation/documentation contribution --> #<-- Add entries for donations and code/translation/documentation contribution -->
## About ## About
@@ -391,11 +388,7 @@ main.vaultlist.contextMenu.unlock=فتح…
main.vaultlist.contextMenu.unlockNow=افتح الان main.vaultlist.contextMenu.unlockNow=افتح الان
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
main.vaultlist.contextMenu.reveal=اظهار القرص main.vaultlist.contextMenu.reveal=اظهار القرص
main.vaultlist.addVaultBtn.menuItemNew=إنشاء مخزن جديد...
main.vaultlist.addVaultBtn.menuItemExisting=افتح مخزن موجود...
##Notificaition ##Notificaition
main.notification.updateAvailable=هناك تحديث متاح.
main.notification.support=دعم Cryptomator.
## Vault Detail ## Vault Detail
### Welcome ### Welcome
main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام: main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام:
@@ -551,8 +544,6 @@ dokanySupportEnd.message=انتهاء الدعم لـDokany
dokanySupportEnd.description=نوع وحدة التخزين Dokany لم يعد مدعوماً من قبل Cryptomator. تم تعديل إعداداتك لاستخدام نوع وحدة التخزين الافتراضي الآن. يمكنك عرض النوع الافتراضي في التفضيلات. dokanySupportEnd.description=نوع وحدة التخزين Dokany لم يعد مدعوماً من قبل Cryptomator. تم تعديل إعداداتك لاستخدام نوع وحدة التخزين الافتراضي الآن. يمكنك عرض النوع الافتراضي في التفضيلات.
dokanySupportEnd.preferencesBtn=فتح التفضيلات dokanySupportEnd.preferencesBtn=فتح التفضيلات
#Retry If Readonly
# Share Vault # Share Vault
shareVault.title=مشاركة الخزانة shareVault.title=مشاركة الخزانة
shareVault.message=هل ترغب في مشاركة خزانتك مع الآخرين؟ shareVault.message=هل ترغب في مشاركة خزانتك مع الآخرين؟
@@ -13,7 +13,6 @@ generic.button.copied=Күсермә алынды!
generic.button.done=Тамам generic.button.done=Тамам
generic.button.next=Киләһе generic.button.next=Киләһе
generic.button.print=Баҫтыр generic.button.print=Баҫтыр
generic.button.remove=Алып ташлау
# Error # Error
error.message=Хата килеп сыҡты error.message=Хата килеп сыҡты
@@ -105,6 +104,7 @@ addvaultwizard.success.unlockNow=Хәҙер бикте ас
removeVault.title="%s" һаҡлағысын алып ташла removeVault.title="%s" һаҡлағысын алып ташла
removeVault.message=Һаҡлағысты алып ташларғамы? removeVault.message=Һаҡлағысты алып ташларғамы?
removeVault.description=Cryptomator был һаҡлағысты ғына онотасаҡ. Һеҙ уны яңынан өҫтәй алаһығыҙ. Ҡаты дисктан шифрланған файлдар юйылмаясаҡ. removeVault.description=Cryptomator был һаҡлағысты ғына онотасаҡ. Һеҙ уны яңынан өҫтәй алаһығыҙ. Ҡаты дисктан шифрланған файлдар юйылмаясаҡ.
removeVault.confirmBtn=Һаҡлағысты алып ташла
# Change Password # Change Password
changepassword.title=Серһүҙҙе үҙгәртеү changepassword.title=Серһүҙҙе үҙгәртеү
@@ -164,6 +164,7 @@ hub.registerSuccess.unlockBtn=Биген ас
### Registration Failed ### Registration Failed
### Unauthorized ### Unauthorized
hub.unauthorized.message=Инеү кире ҡағылды hub.unauthorized.message=Инеү кире ҡағылды
hub.unauthorized.description=Һеҙҙең йыһаз әлегә был һаҡлағысҡа инеү хоҡуғына эйә түгел. Һаҡлағыс хужаһынан рөхсәт һорағыҙ.
### Requires Account Initialization ### Requires Account Initialization
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын: hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
@@ -315,6 +316,7 @@ preferences.contribute.getCertificate=Юҡ мы әллә? Уны нисек ал
preferences.contribute.promptText=Ярҙамсы сертификатын бында йәбештерегеҙ preferences.contribute.promptText=Ярҙамсы сертификатын бында йәбештерегеҙ
### Remove License Key Dialog ### Remove License Key Dialog
removeCert.confirmBtn=Алып ташлау
#<-- Add entries for donations and code/translation/documentation contribution --> #<-- Add entries for donations and code/translation/documentation contribution -->
## About ## About
@@ -515,6 +517,4 @@ updateReminder.yesAutomatically=Эйе, автоматик рәүештә
#Dokany Support End #Dokany Support End
dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
#Retry If Readonly
# Share Vault # Share Vault
@@ -13,7 +13,6 @@ generic.button.copied=Скапіявана!
generic.button.done=Файна generic.button.done=Файна
generic.button.next=Далей generic.button.next=Далей
generic.button.print=Друкаваць generic.button.print=Друкаваць
generic.button.remove=Выдаліць
# Error # Error
error.message=Адбылася памылка error.message=Адбылася памылка
@@ -105,6 +104,7 @@ addvaultwizard.success.unlockNow=Разамкнуць зараз
removeVault.title=Выдаліць "%s" removeVault.title=Выдаліць "%s"
removeVault.message=Ці выдаліць скарбніцу? removeVault.message=Ці выдаліць скарбніцу?
removeVault.description=Гэта прывядзе толькі да таго, што Cryptomator забудзецца на гэтую скрынку. Ты зможаш дадаць яе пазней ізноў. Аніякія зашыфраваныя файлы з тайго жорсткага дыску не выдаляцьмуцца. removeVault.description=Гэта прывядзе толькі да таго, што Cryptomator забудзецца на гэтую скрынку. Ты зможаш дадаць яе пазней ізноў. Аніякія зашыфраваныя файлы з тайго жорсткага дыску не выдаляцьмуцца.
removeVault.confirmBtn=Выдаліць скарбніцу
# Change Password # Change Password
changepassword.title=Змяніць пароль changepassword.title=Змяніць пароль
@@ -157,6 +157,7 @@ hub.registerSuccess.unlockBtn=Адамкнуць
### Registration Failed ### Registration Failed
### Unauthorized ### Unauthorized
hub.unauthorized.message=Адмова ў доступе hub.unauthorized.message=Адмова ў доступе
hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам.
### Requires Account Initialization ### Requires Account Initialization
### License Exceeded ### License Exceeded
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
@@ -302,6 +303,7 @@ preferences.contribute.getCertificate=Яшчэ ня маеш такога? Да
preferences.contribute.promptText=Устаў код сэртыфікату ахвяравальніка сюды preferences.contribute.promptText=Устаў код сэртыфікату ахвяравальніка сюды
### Remove License Key Dialog ### Remove License Key Dialog
removeCert.confirmBtn=Выдаліць
#<-- Add entries for donations and code/translation/documentation contribution --> #<-- Add entries for donations and code/translation/documentation contribution -->
## About ## About
@@ -495,6 +497,4 @@ updateReminder.yesAutomatically=Так, аўтаматычна
#Dokany Support End #Dokany Support End
dokanySupportEnd.preferencesBtn=Адчыніць налады dokanySupportEnd.preferencesBtn=Адчыніць налады
#Retry If Readonly
# Share Vault # Share Vault
@@ -13,7 +13,6 @@ generic.button.copied=Копирано!
generic.button.done=Готово generic.button.done=Готово
generic.button.next=Напред generic.button.next=Напред
generic.button.print=Отпечатване generic.button.print=Отпечатване
generic.button.remove=Премахване
# Error # Error
error.message=Възникна грешка error.message=Възникна грешка
@@ -105,6 +104,7 @@ addvaultwizard.success.unlockNow=Отключване сега
removeVault.title=Премахване на „%s“ removeVault.title=Премахване на „%s“
removeVault.message=Премахване на хранилище? removeVault.message=Премахване на хранилище?
removeVault.description=По този начин Криптоматор ще забрави за това хранилище. Можете да го добавите отново. Шифрованите файлове няма да бъдат премахнати от твърдия диск. removeVault.description=По този начин Криптоматор ще забрави за това хранилище. Можете да го добавите отново. Шифрованите файлове няма да бъдат премахнати от твърдия диск.
removeVault.confirmBtn=Премахване
# Change Password # Change Password
changepassword.title=Промяна на парола changepassword.title=Промяна на парола
@@ -164,6 +164,7 @@ hub.registerSuccess.unlockBtn=Отключване
### Registration Failed ### Registration Failed
### Unauthorized ### Unauthorized
hub.unauthorized.message=Отказан достъп hub.unauthorized.message=Отказан достъп
hub.unauthorized.description=Устройството не е упълномощено за достъп до това хранилище. Поискайте достъп от собственика.
### Requires Account Initialization ### Requires Account Initialization
hub.requireAccountInit.message=Необходимо е действие hub.requireAccountInit.message=Необходимо е действие
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
@@ -315,6 +316,7 @@ preferences.contribute.getCertificate=Все още нямате? Научете
preferences.contribute.promptText=Поставете тук кода на сертификата за дарение preferences.contribute.promptText=Поставете тук кода на сертификата за дарение
### Remove License Key Dialog ### Remove License Key Dialog
removeCert.confirmBtn=Премахване
#<-- Add entries for donations and code/translation/documentation contribution --> #<-- Add entries for donations and code/translation/documentation contribution -->
## About ## About
@@ -515,6 +517,4 @@ updateReminder.yesAutomatically=Да, автоматично
#Dokany Support End #Dokany Support End
dokanySupportEnd.preferencesBtn=Към настройките dokanySupportEnd.preferencesBtn=Към настройките
#Retry If Readonly
# Share Vault # Share Vault

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