Compare commits

...

363 Commits

Author SHA1 Message Date
Armin Schrenk
a1f58ffb0e Use the javaimage to extract wixhelper 2026-02-18 15:10:22 +01:00
Armin Schrenk
f21fae5b47 temurin 2026-02-18 14:46:46 +01:00
Armin Schrenk
5ab12c1b1a Update to latest JDK 25 2026-02-18 11:49:13 +01:00
Armin Schrenk
0ac63e7aba Closes #4136 2026-02-18 11:26:42 +01:00
Armin Schrenk
ac6f34fe17 reorder changelog 2026-02-18 11:15:46 +01:00
Armin Schrenk
dba3de230b Update dependencies
* org.cryptomator:integrations-win from 1.5.1 to 1.6.0
* org.cryptomator:webdav-nio-adapter from 3.0.0 to 3.0.1
2026-02-18 11:15:00 +01:00
Armin Schrenk
e11d42bb07 update IDE file 2026-02-18 11:07:18 +01:00
Jan-Peter Klein
171d5d5a4c Fixes #4141 2026-02-18 10:55:49 +01:00
Armin Schrenk
b86d8ca790 [skip ci] update changelog 2026-02-17 15:30:20 +01:00
Armin Schrenk
74fc77ab8d fix pom inconsistency 2026-02-17 15:21:16 +01:00
Armin Schrenk
044dbcb11f Update cryptofs to version 2.10.0-beta3 2026-02-17 15:13:46 +01:00
Armin Schrenk
1b243bb725 suppress logging of expected exception 2026-02-17 15:12:25 +01:00
Armin Schrenk
f21cb90e7d Merge pull request #4025 from purejava/new-secret-service
New keychain backend SecretService for Linux
2026-02-17 13:16:06 +01:00
Armin Schrenk
1e8f272f6a Merge pull request #4143 from cryptomator/dependabot/maven/java-production-dependencies-d98826387a
Bump the java-production-dependencies group across 1 directory with 11 updates
2026-02-17 13:15:07 +01:00
Armin Schrenk
5c1f8e7576 update IDE file 2026-02-17 12:46:19 +01:00
Armin Schrenk
bc0bb38e4c remove dagger formatting due to compile error 2026-02-17 12:45:46 +01:00
Armin Schrenk
b9b02acbe5 Merge pull request #4134 from cryptomator/feature/javafx-color-scheme-change
Feature: Use JavaFX built ins to adapt to OS color scheme
2026-02-17 10:36:23 +01:00
Armin Schrenk
e53598cfce update azure signing to stable version 2026-02-16 17:06:00 +01:00
Armin Schrenk
a79fd63634 [skip ci] update changelog 2026-02-16 16:15:05 +01:00
Armin Schrenk
f753ddc9be Apply suggestions from code review
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2026-02-16 16:05:26 +01:00
Armin Schrenk
158b454b0d fix NPE in EventualLogger 2026-02-16 12:45:57 +01:00
Armin Schrenk
9e6bd913cb refactor FxApplicationStyle
use pattern (removeOldListeners, addNewListerner, applyTheme)
2026-02-16 12:37:46 +01:00
Armin Schrenk
83ef9d06d9 simplify JfxUiAppearanceProvider
and move loading of appearance service into the fx app
2026-02-16 11:55:03 +01:00
Armin Schrenk
cf0052b4f5 revert lambda processing 2026-02-16 11:07:04 +01:00
Armin Schrenk
0b4d86768a Closes #4088 2026-02-16 10:13:21 +01:00
dependabot[bot]
a77e90738a Bump the java-production-dependencies group across 1 directory with 11 updates
Bumps the java-production-dependencies group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [org.cryptomator:cryptolib](https://github.com/cryptomator/cryptolib) | `2.2.1` | `2.2.2` |
| [org.cryptomator:fuse-nio-adapter](https://github.com/cryptomator/fuse-nio-adapter) | `5.1.0` | `6.0.0` |
| [ch.qos.logback:logback-core](https://github.com/qos-ch/logback) | `1.5.19` | `1.5.31` |
| [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) | `1.5.19` | `1.5.31` |
| org.apache.commons:commons-lang3 | `3.19.0` | `3.20.0` |
| [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) | `2.20.0` | `2.21.0` |
| com.fasterxml.jackson.datatype:jackson-datatype-jsr310 | `2.20.0` | `2.21.0` |
| [com.google.dagger:dagger](https://github.com/google/dagger) | `2.57.2` | `2.59.1` |
| [com.google.dagger:dagger-compiler](https://github.com/google/dagger) | `2.57.2` | `2.59.1` |
| [com.github.ben-manes.caffeine:caffeine](https://github.com/ben-manes/caffeine) | `3.2.2` | `3.2.3` |
| [org.cryptomator:integrations-linux](https://github.com/cryptomator/integrations-linux) | `1.7.0-beta3` | `1.7.0-beta4` |



Updates `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
- [Release notes](https://github.com/cryptomator/cryptolib/releases)
- [Changelog](https://github.com/cryptomator/cryptolib/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cryptomator/cryptolib/compare/2.2.1...2.2.2)

Updates `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
- [Release notes](https://github.com/cryptomator/fuse-nio-adapter/releases)
- [Changelog](https://github.com/cryptomator/fuse-nio-adapter/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cryptomator/fuse-nio-adapter/compare/5.1.0...6.0.0)

Updates `ch.qos.logback:logback-core` from 1.5.19 to 1.5.31
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.19...v_1.5.31)

Updates `ch.qos.logback:logback-classic` from 1.5.19 to 1.5.31
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.19...v_1.5.31)

Updates `ch.qos.logback:logback-classic` from 1.5.19 to 1.5.31
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.19...v_1.5.31)

Updates `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0

Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0

Updates `com.google.dagger:dagger` from 2.57.2 to 2.59.1
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.57.2...dagger-2.59.1)

Updates `com.google.dagger:dagger-compiler` from 2.57.2 to 2.59.1
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.57.2...dagger-2.59.1)

Updates `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
- [Release notes](https://github.com/ben-manes/caffeine/releases)
- [Commits](https://github.com/ben-manes/caffeine/compare/v3.2.2...v3.2.3)

Updates `com.google.dagger:dagger-compiler` from 2.57.2 to 2.59.1
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.57.2...dagger-2.59.1)

Updates `org.cryptomator:integrations-linux` from 1.7.0-beta3 to 1.7.0-beta4
- [Release notes](https://github.com/cryptomator/integrations-linux/releases)
- [Changelog](https://github.com/cryptomator/integrations-linux/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cryptomator/integrations-linux/compare/1.7.0-beta3...1.7.0-beta4)

---
updated-dependencies:
- dependency-name: org.cryptomator:cryptolib
  dependency-version: 2.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: org.cryptomator:fuse-nio-adapter
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: java-production-dependencies
- dependency-name: ch.qos.logback:logback-core
  dependency-version: 1.5.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: org.apache.commons:commons-lang3
  dependency-version: 3.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
  dependency-version: 2.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger
  dependency-version: 2.59.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger-compiler
  dependency-version: 2.59.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.github.ben-manes.caffeine:caffeine
  dependency-version: 3.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger-compiler
  dependency-version: 2.59.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: org.cryptomator:integrations-linux
  dependency-version: 1.7.0-beta4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 06:06:54 +00:00
mindmonk
0bec741c2b Merge pull request #4120 from cryptomator/feature/disable-password-tab-if-masterkey-missing
Disable recovery key actions in Vault Password options when masterkey is missing
2026-02-12 18:08:52 +01:00
Armin Schrenk
af43aeca15 Merge branch 'develop' into feature/javafx-color-scheme-change 2026-02-12 10:18:32 +01:00
Armin Schrenk
1629eae5d3 [skip ci] update changelog 2026-02-11 15:55:34 +01:00
Armin Schrenk
99898b74fb minor cleanup 2026-02-11 15:55:19 +01:00
Armin Schrenk
327d6065e9 Merge pull request #4105 from cryptomator/feature/admin-properties
Feature: Admin Config
2026-02-11 15:25:31 +01:00
Armin Schrenk
2a5ef5d999 log inconsistent state 2026-02-11 15:21:55 +01:00
Armin Schrenk
34e5d19a04 prevent resource leak 2026-02-11 15:16:11 +01:00
Armin Schrenk
33851a8559 Refactor admin props
* rename class to AdminPropertiesFactory
* rename factory method to "create"
* remove side effects from methods
* returned properties default to system properties
2026-02-11 15:12:52 +01:00
Armin Schrenk
fb54d96997 fix wrong method delegation 2026-02-11 14:49:56 +01:00
Armin Schrenk
cff47b1c73 wrap system properties instead of direct modification 2026-02-11 14:36:55 +01:00
Armin Schrenk
5db05d8bc7 Apply suggestions from codereview 2026-02-11 14:33:01 +01:00
Armin Schrenk
b85780eae9 Disable JavaFX based UiAppearanceProvider for macOS 2026-02-11 12:22:22 +01:00
Ralph Plawetzki
bdfd22c483 Partially reverts dd1af8cd78
Not needed anymore, see https://github.com/cryptomator/integrations-linux/pull/125#issuecomment-3876060158
2026-02-11 06:07:01 +01:00
Armin Schrenk
e3433cb312 Refactor FxApplicationStyle class 2026-02-10 18:17:09 +01:00
Armin Schrenk
46d1d605ad refactor JfxUiAppearanceProvider class
* use delegate pattern for initialization
* add logging
2026-02-10 18:01:36 +01:00
Armin Schrenk
a4eadd4817 remove theme change restrictions 2026-02-10 17:58:29 +01:00
Armin Schrenk
c6717bd4e1 Rely on javafx to change color theme 2026-02-09 17:48:23 +01:00
Armin Schrenk
53f368108a Rename jvm property and actual config file 2026-02-09 16:54:04 +01:00
Armin Schrenk
5e52811c74 Remove hard coded default locations for admin config 2026-02-09 16:49:12 +01:00
Armin Schrenk
cad7b45808 Refactor to first check system property cryptomator.adminConfig for config path 2026-02-09 16:16:26 +01:00
Armin Schrenk
a057bf6f70 Merge branch 'develop' into feature/admin-properties 2026-02-09 15:03:06 +01:00
Armin Schrenk
4cf11cf293 [skip ci] update changelog 2026-02-09 14:56:37 +01:00
Armin Schrenk
2523609996 disable loading of user defined config for windows msi (#4132) 2026-02-09 14:49:07 +01:00
dependabot[bot]
80276ee992 Bump the github-actions group across 1 directory with 8 updates (#4124)
Bumps the github-actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `6.0.1` | `6.0.2` |
| [actions/setup-java](https://github.com/actions/setup-java) | `5.1.0` | `5.2.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `5.0.0` | `6.0.0` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `6.0.0` | `7.0.0` |
| [actions/cache](https://github.com/actions/cache) | `4.3.0` | `5.0.3` |
| [skymatic/workflows/.github/workflows/run-dependency-check.yml](https://github.com/skymatic/workflows) | `3.0.1` | `3.0.2` |
| [fjogeleit/http-request-action](https://github.com/fjogeleit/http-request-action) | `1.16.6` | `2.0.0` |
| [skymatic/workflows](https://github.com/skymatic/workflows) | `3.0.1` | `3.0.2` |



Updates `actions/checkout` from 6.0.1 to 6.0.2
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](8e8c483db8...de0fac2e45)

Updates `actions/setup-java` from 5.1.0 to 5.2.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](f2beeb24e1...be666c2fcd)

Updates `actions/upload-artifact` from 5.0.0 to 6.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](330a01c490...b7c566a772)

Updates `actions/download-artifact` from 6.0.0 to 7.0.0
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](018cc2cf5b...37930b1c2a)

Updates `actions/cache` from 4.3.0 to 5.0.3
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0057852bfa...cdf6c1fa76)

Updates `skymatic/workflows/.github/workflows/run-dependency-check.yml` from 3.0.1 to 3.0.2
- [Release notes](https://github.com/skymatic/workflows/releases)
- [Commits](1074588008...2d0c27a7b0)

Updates `fjogeleit/http-request-action` from 1.16.6 to 2.0.0
- [Release notes](https://github.com/fjogeleit/http-request-action/releases)
- [Changelog](https://github.com/fjogeleit/http-request-action/blob/main/RELEASE.md)
- [Commits](c0b95d02a0...551353b829)

Updates `skymatic/workflows` from 3.0.1 to 3.0.2
- [Release notes](https://github.com/skymatic/workflows/releases)
- [Commits](1074588008...2d0c27a7b0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/setup-java
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: 5.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: skymatic/workflows/.github/workflows/run-dependency-check.yml
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: fjogeleit/http-request-action
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: skymatic/workflows
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-09 09:27:49 +01:00
Ralph Plawetzki
561432bc98 Revert "Make SecretServiceKeychainAccess the default selection"
This reverts commit 9a74194391.
Users decide when to migrate.
2026-02-05 08:15:10 +01:00
Ralph Plawetzki
cd0a640a36 Enable migrating KeychainEntries on Linux as well 2026-02-05 07:09:38 +01:00
Armin Schrenk
a7b8541912 Update Slack webhook URL secret in workflow 2026-01-30 15:27:31 +01:00
Jan-Peter Klein
d4382f3130 remove docTooltip 2026-01-28 18:40:56 +01:00
Jan-Peter Klein
2983745be5 remove docs link 2026-01-28 18:20:18 +01:00
Armin Schrenk
77983fe00a simplify EventualLogger init 2026-01-28 17:44:48 +01:00
Armin Schrenk
a4836f6528 cleanup 2026-01-28 17:36:52 +01:00
Armin Schrenk
e9b3b505a8 apply suggestions from code review 2026-01-28 17:35:34 +01:00
Armin Schrenk
19c9eada9d check first the filesize before opening a Reader to the config file 2026-01-28 17:32:10 +01:00
Jan-Peter Klein
2a5bce2c5c disable password tab when masterkey.file is missing 2026-01-27 17:42:04 +01:00
mindmonk
9ee81a0e35 Merge pull request #4106 from cryptomator/feature/move-recover-io-tasks-to-background
Move recovery I/O to background thread
2026-01-26 13:30:39 +01:00
Jan-Peter Klein
8b05ae0a54 dedup createTask 2026-01-23 16:09:06 +01:00
Jan-Peter Klein
aa898c634f refactor recovery restore to sync logic with async task wrapper for testability 2026-01-23 13:57:38 +01:00
Armin Schrenk
efbd107fb5 increase null safety 2026-01-21 18:09:26 +01:00
Armin Schrenk
300cac5441 don't forget log markers 2026-01-21 16:36:56 +01:00
Armin Schrenk
b651b9ac26 config file can be exactly the max size 2026-01-21 16:08:00 +01:00
Armin Schrenk
29e76e7f93 keep throwables in EventualLogger 2026-01-21 16:07:17 +01:00
Armin Schrenk
5cac6b8114 also adjust distribution 2026-01-21 15:55:04 +01:00
Armin Schrenk
35c2141fd6 cleanup 2026-01-21 15:34:34 +01:00
Armin Schrenk
b00c81c20a use "cryptomator.config" for config file 2026-01-21 15:34:24 +01:00
Armin Schrenk
7ee0606306 adjust unit tests 2026-01-21 15:34:08 +01:00
Armin Schrenk
046372f95b refactored BufferedLog
* rename to EventualLogger
* adhere to slf4j API
* ensure single instance
2026-01-21 15:33:26 +01:00
Armin Schrenk
c198adaf3f switch back to properties 2026-01-21 11:53:31 +01:00
Armin Schrenk
d53f0880ca add test for AdminPropertiesSetter 2026-01-15 18:06:24 +01:00
Armin Schrenk
088b177c0e Ensure that null map is also logged 2026-01-15 15:11:46 +01:00
Armin Schrenk
287ab4e792 [skip ci] Update changelog 2026-01-15 15:11:12 +01:00
Armin Schrenk
0e9cad5b09 Merge pull request #4103 from cryptomator/feature/basic-accessibility
Feature: Improve accessibility for core functionality
2026-01-15 10:44:16 +01:00
Armin Schrenk
a0c9caeb21 [skip ci] update changelog 2026-01-14 17:57:24 +01:00
Armin Schrenk
f620c6685a apply suggestions from AI review 2026-01-14 17:53:07 +01:00
Armin Schrenk
61be8c449c fixed flush does not clear BufferedLog 2026-01-14 17:39:49 +01:00
Armin Schrenk
43343b9954 fix wrong Linux path 2026-01-14 17:36:07 +01:00
Armin Schrenk
460b7e6225 [skip ci] update changelog 2026-01-14 17:33:45 +01:00
Armin Schrenk
e2430bfb22 add null check 2026-01-14 17:29:56 +01:00
Armin Schrenk
ac7c9c2165 use a bracket to "describe" the vault state 2026-01-14 17:28:20 +01:00
Jan-Peter Klein
efd73e0d3e move recovery IO operations to background 2026-01-14 17:14:23 +01:00
Armin Schrenk
b441803462 also adjust wix installer to new config format 2026-01-14 16:28:48 +01:00
Armin Schrenk
42b06aa556 Switch to JSON as config format
Java Properties require ISO 8859-1 character encoding, leading to manual edits of file.
2026-01-14 14:12:28 +01:00
Armin Schrenk
b1c21501a6 Add dummy admin properties file in Windows installer 2026-01-13 18:40:53 +01:00
Armin Schrenk
45633837e0 only allow overwriting a subset of JVM properties 2026-01-13 17:26:48 +01:00
Armin Schrenk
b23bd0b27a Implement logic to overwrite system properties set in app internal config file 2026-01-13 13:14:40 +01:00
Armin Schrenk
b3f3faf4ee add DIALOG role to more dialogs 2026-01-13 11:02:01 +01:00
Armin Schrenk
33f26bf804 Refactor notificationbar
* rename to InfoBar
* replace closeButton text with graphic and add accessibilityText
* use common base class for styling and use CSS lookup for variants
2026-01-12 23:31:37 +01:00
Armin Schrenk
a06accb80f add accessibletext to vault storage location 2026-01-12 23:15:40 +01:00
Armin Schrenk
a50e372f05 add correct accessible role to dialogs 2026-01-12 23:14:09 +01:00
Armin Schrenk
06726303fb add proper accessibility text for vault list 2026-01-12 23:13:15 +01:00
Armin Schrenk
11c86f7287 impl idea 2026-01-12 11:59:17 +01:00
mindmonk
8e68a62ab0 Merge pull request #4081 from cryptomator/feature/hub-vault-archived-dialog
Feature: Show dedicated “Archived Vault” screen on unlock when Hub returns 410
2026-01-06 11:20:29 +01:00
Jan-Peter Klein
787d0b8e12 code cleanup 2026-01-05 14:00:20 +01:00
Jan-Peter Klein
ba765f7a69 code cleanup 2026-01-05 13:31:45 +01:00
Jan-Peter Klein
e9d7e631c7 fix typo 2026-01-05 13:27:50 +01:00
Jan-Peter Klein
88acbc8ed7 fix typo 2026-01-05 13:22:01 +01:00
Jan-Peter Klein
e84a73b9c3 use SimpleDialog 2026-01-05 13:17:10 +01:00
Jan-Peter Klein
2324ddad8b update changelog 2026-01-05 11:53:51 +01:00
Jan-Peter Klein
04da2504de Merge branch 'develop' into feature/hub-vault-archived-dialog 2026-01-05 11:28:08 +01:00
Armin Schrenk
8105dbc236 Closes #4058 2025-12-30 12:31:45 +01:00
Armin Schrenk
ceed70c314 [skip ci] update changelog 2025-12-29 16:59:01 +01:00
Armin Schrenk
eddf586b81 Merge pull request #4078 from cryptomator/feature/files-in-use
Feature: Files in use
2025-12-29 16:00:36 +01:00
Armin Schrenk
0d7ddd3189 [skip ci] Update changelog 2025-12-29 15:55:49 +01:00
Armin Schrenk
491cd5e550 install/remove the encrypted mime type on install/uninstall 2025-12-29 15:22:26 +01:00
Armin Schrenk
93d95ecd93 make message more clear
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 12:42:11 +01:00
Armin Schrenk
05a41a625c reset selection index everytime window is shown
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 12:32:35 +01:00
Armin Schrenk
2aff02a186 cleanup
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 12:27:07 +01:00
Armin Schrenk
4b7de11750 dedup css
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 12:26:08 +01:00
Armin Schrenk
aaf921afcf Adjust notification dialog
* add scrollbar to description content
* move filename to own visual element
* move vaultName above message

Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 11:40:02 +01:00
Armin Schrenk
7718445f7c rename method
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-21 11:00:01 +01:00
Armin Schrenk
0fa27f66c9 Use proper icon for in-use event 2025-12-19 13:54:47 +01:00
Armin Schrenk
111c5ae2de correctly convert URI path to system path 2025-12-19 13:25:42 +01:00
Armin Schrenk
8841bb1c6a add c9u as file extension 2025-12-18 17:55:43 +01:00
Armin Schrenk
8ca7e34d8f minor fixes 2025-12-18 16:47:45 +01:00
Armin Schrenk
0fa6f0d915 only close notification window on index error 2025-12-18 16:20:58 +01:00
Armin Schrenk
4ffd8f4467 add localization for file-is-in-use-event in event view 2025-12-18 16:19:42 +01:00
Armin Schrenk
bb0c1b54c1 Add log message to get used mount service 2025-12-18 15:39:56 +01:00
Armin Schrenk
8eb91f6471 Change design of files-in-use-notification
* add timestamp of event occurance
* move filename into message section
* remove translations
2025-12-18 13:28:04 +01:00
Armin Schrenk
eb20fd7278 add device name to filesystemowner and display device name in notification 2025-12-17 16:55:42 +01:00
Armin Schrenk
d836798361 only one instance of notification window 2025-12-17 16:27:10 +01:00
Jan-Peter Klein
760e260eba show archived vault dialog on unlock when Hub returns 410 2025-12-17 09:56:56 +01:00
Armin Schrenk
0cd6eb8b5e use Path api to concat (string) paths for events
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-16 12:52:35 +01:00
Armin Schrenk
0b2107309b guard the notification window from theoretical thread races
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-16 12:33:25 +01:00
dependabot[bot]
309881b339 Bump the github-actions group across 1 directory with 8 updates (#4073)
Bumps the github-actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `5.0.0` | `6.0.1` |
| [actions/setup-java](https://github.com/actions/setup-java) | `5.0.0` | `5.1.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.6.2` | `5.0.0` |
| [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `2.4.1` | `2.5.0` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `5.0.0` | `6.0.0` |
| [fjogeleit/http-request-action](https://github.com/fjogeleit/http-request-action) | `1.16.5` | `1.16.6` |
| [actions/stale](https://github.com/actions/stale) | `10.1.0` | `10.1.1` |
| [skymatic/workflows](https://github.com/skymatic/workflows) | `450e322ff2214d0be0b079b63343c894f3ef735f` | `1074588008ae3326a2221ea451783280518f0366` |



Updates `actions/checkout` from 5.0.0 to 6.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](08c6903cd8...8e8c483db8)

Updates `actions/setup-java` from 5.0.0 to 5.1.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](dded088883...f2beeb24e1)

Updates `actions/upload-artifact` from 4.6.2 to 5.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](ea165f8d65...330a01c490)

Updates `softprops/action-gh-release` from 2.4.1 to 2.5.0
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](6da8fa9354...a06a81a03e)

Updates `actions/download-artifact` from 5.0.0 to 6.0.0
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](634f93cb29...018cc2cf5b)

Updates `fjogeleit/http-request-action` from 1.16.5 to 1.16.6
- [Release notes](https://github.com/fjogeleit/http-request-action/releases)
- [Commits](1297c6fc63...c0b95d02a0)

Updates `actions/stale` from 10.1.0 to 10.1.1
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](5f858e3efb...997185467f)

Updates `skymatic/workflows` from 450e322ff2214d0be0b079b63343c894f3ef735f to 1074588008ae3326a2221ea451783280518f0366
- [Release notes](https://github.com/skymatic/workflows/releases)
- [Commits](450e322ff2...1074588008)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-java
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: softprops/action-gh-release
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: fjogeleit/http-request-action
  dependency-version: 1.16.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: 10.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: skymatic/workflows
  dependency-version: 1074588008ae3326a2221ea451783280518f0366
  dependency-type: direct:production
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 09:54:39 +01:00
Armin Schrenk
532cdf30ac Fix typos 2025-12-12 17:41:29 +01:00
Armin Schrenk
d746e9e215 add translation to event 2025-12-12 16:55:15 +01:00
Armin Schrenk
408c80744a Show notification for fileIsInUseEvent 2025-12-12 13:29:46 +01:00
Armin Schrenk
bf890d2165 Merge pull request #4069 from cryptomator/feature/notify-fallback
Feature: Notification Window with JavaFX
2025-12-12 12:42:37 +01:00
Armin Schrenk
eb90a7fb33 Merge branch 'feature/files-in-use' into feature/notify-fallback
# Conflicts:
#	src/main/java/org/cryptomator/common/EventMap.java
#	src/main/java/org/cryptomator/common/vaults/Vault.java
2025-12-12 12:31:08 +01:00
Armin Schrenk
a47814f38c cleanup 2025-12-12 12:26:46 +01:00
Armin Schrenk
8b157fd935 add dark theme 2025-12-12 12:25:47 +01:00
Armin Schrenk
5e582f666c add accessibleText 2025-12-12 11:37:42 +01:00
Armin Schrenk
bebe3064ee hide paging if there is only one event 2025-12-12 11:21:39 +01:00
Armin Schrenk
bbf8e1d1e5 Ensure that keyloader is not reused between vaults 2025-12-11 14:35:23 +01:00
Armin Schrenk
98d2442deb For hub vaults set filesystem owner 2025-12-11 14:34:45 +01:00
Armin Schrenk
cac5c86dab Merge branch 'develop' into feature/files-in-use
# Conflicts:
#	pom.xml
2025-12-10 13:04:34 +01:00
Armin Schrenk
5a0145800d Redesign notifcation dialog
* add Cryptomator title
* include vault name in message
* remove bottom section and move button to current message area
2025-12-10 13:01:07 +01:00
Armin Schrenk
f4b56e5e51 rename variable 2025-12-10 12:34:33 +01:00
Armin Schrenk
7cfb63ed48 cleanup in NotificationController 2025-12-10 12:34:09 +01:00
Armin Schrenk
2521cd2bfb cleanup
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-09 18:13:04 +01:00
Armin Schrenk
1e861ac187 fix av-whitelist workflow 2025-12-09 09:34:25 +01:00
Tobias Hagemann
a4c85be61c Add accessible text to form controls and table columns (#4066) 2025-12-06 07:41:39 +01:00
Armin Schrenk
5b9e70da33 implement code suggestions
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 17:32:24 +01:00
Armin Schrenk
b83a3c27c6 [skip ci] remove unused import
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 16:52:04 +01:00
Armin Schrenk
5c2954de03 guard controller from NPE
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 16:43:39 +01:00
Armin Schrenk
8a834fe71c fix window placement
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 16:38:37 +01:00
Armin Schrenk
4ff2905723 regularly poll for notification updates
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 16:37:31 +01:00
Armin Schrenk
538e01b1e5 consider location of system bar when placing notification window
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 15:54:05 +01:00
Armin Schrenk
3135be9178 fix logic in notificationManager
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 12:14:07 +01:00
Armin Schrenk
2c456f5323 fix more issues
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 11:58:16 +01:00
Armin Schrenk
9d464e3a4f fix threading issue in notification manager
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 11:53:43 +01:00
Armin Schrenk
83199eb1de use VaultEvent for notification to also show affected vault
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 11:35:54 +01:00
Armin Schrenk
5772ffec17 activate notifications by consuming vault events
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 11:27:01 +01:00
Armin Schrenk
f3415277d7 cleanup
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 11:23:52 +01:00
Armin Schrenk
8e9a001fde clean up
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 10:50:48 +01:00
Armin Schrenk
5e229395f1 remove test code
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-05 10:50:32 +01:00
Armin Schrenk
18dd9770f2 add second fake event
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-04 17:03:47 +01:00
Armin Schrenk
185c5a4f5d implement more ui logic
and reduce css

Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-04 17:03:36 +01:00
Armin Schrenk
285ddac219 ai thoughts
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-04 16:20:49 +01:00
Armin Schrenk
9e631a78f2 Pimp notification dialog
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-12-04 15:35:05 +01:00
Tobias Hagemann
7fe9049266 Improve accessibility for icon-only controls and input labels (#4064) 2025-12-01 22:06:57 +01:00
Tobias Hagemann
39ab9ad63f Merge pull request #4055 from tamaracha/fix_a11y_buttons
Fix a11y for no-graphics buttons in main window
2025-12-01 16:13:10 +01:00
Armin Schrenk
b4528f825e [skip ci] drop interface approach and just add fx window 2025-11-29 18:03:55 +01:00
Armin Schrenk
3d679c73e7 [skip ci] impl draft
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-11-28 14:31:04 +01:00
Sebastian Stenzel
f4f093bb42 update GitHub Releases Template
[ci skip]
2025-11-24 19:57:24 +01:00
Sebastian Stenzel
617f1bf2c9 CHANGELOG.md: added
[ci skip]
2025-11-24 14:49:12 +01:00
Sebastian Stenzel
02ad38f871 Merge pull request #3948 from purejava/feature/app-update
Implement new update API
2025-11-24 14:41:55 +01:00
Sebastian Stenzel
7a2944cbea bump integrations version 2025-11-24 14:39:17 +01:00
Sebastian Stenzel
4c47204f92 Merge branch 'develop' into feature/app-update 2025-11-24 14:34:44 +01:00
Tamara Cook
b6d0823c24 Add accessible text and tooltips to no-graphics buttons in main window 2025-11-23 22:24:31 +01:00
Tamara Cook
7f4776a995 Make buttons clickable by any input device 2025-11-23 19:26:55 +01:00
Armin Schrenk
9d2b31cd56 Merge branch 'develop' into feature/files-in-use 2025-11-21 10:15:24 +01:00
Tobias Hagemann
f052395a7f Add section on use of Generative AI 2025-11-17 15:03:08 +01:00
Sebastian Stenzel
57be83b38d update license-merges 2025-11-12 11:56:09 +01:00
Sebastian Stenzel
c2cd4f5bbf update compiler.xml
[ci skip]
2025-11-12 11:55:59 +01:00
Sebastian Stenzel
d3d57312ba Merge branch 'develop' into feature/app-update
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/check-jdk-updates.yml
#	.github/workflows/debian.yml
#	.github/workflows/mac-dmg-x64.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/win-exe.yml
2025-11-12 11:50:21 +01:00
Sebastian Stenzel
593a64c9bd Bump JDK to version 25 (#4031)
* first draft for JDK 25 migration (including comments)

* fix unresolvable version in setup-java action

* switch to official `openjdk-25-jdk` ubuntu package

see https://packages.ubuntu.com/search?suite=jammy&arch=any&searchon=names&keywords=openjdk-25

* update jacoco to support JDK 25

* use jdk 25.0.1 for building the app

# Conflicts:
#	.github/workflows/win-exe.yml

* Use correct SHA256 value for openjfx linux arm64

* zulu version is different to temurin :rolling_eyes:

* fix check-jdk-updates

---------

Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-11-12 10:50:37 +01:00
sunnyraindy
19dc4fb6ff chore: fix typo in comment (#4036)
Signed-off-by: sunnyraindy <sunnyraindy@outlook.com>
2025-11-12 10:49:14 +01:00
Armin Schrenk
81f45012f3 [skip ci] Merge branch 'main' into develop 2025-11-12 10:45:38 +01:00
Armin Schrenk
c59554f7bb Merge branch 'release/1.18.0' 2025-11-12 10:43:22 +01:00
Armin Schrenk
11c66e8df7 Use correct SHA256 value for openjfx linux arm64 2025-11-11 16:23:33 +01:00
Armin Schrenk
c49bf0f146 finalize 1.18.0 2025-11-11 16:08:00 +01:00
Armin Schrenk
e74bd91879 Merge branch 'develop' into release/1.18.0 2025-11-11 15:58:03 +01:00
Cryptobot
89d9249a08 New Crowdin updates (#4016)
New translations strings.properties

Afrikaans; Arabic; Bashkir; Belarusian; Bengali; Bosnian; Bulgarian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Croatian; Czech; Danish; Dutch; Filipino; Finnish; French; Galician; German; Greek; Hebrew; Hindi; Hungarian; Indonesian; Italian; Japanese; Korean; Latvian; Macedonian; Marathi; Norwegian; Norwegian Bokmal; Norwegian Nynorsk; Persian; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Romanian; Russian; Serbian (Cyrillic); Serbian (Latin); Sinhala; Slovak; Slovenian; Spanish; Swahili, Tanzania; Swedish; Tamil; Telugu; Thai; Turkish; Ukrainian; Urdu (Pakistan); Uyghur; Vietnamese; 

[ci skip]
2025-11-11 15:57:18 +01:00
Armin Schrenk
013fff1223 Feature: Sign files with Azure trusted signing (#4038) 2025-11-11 14:24:20 +01:00
Armin Schrenk
fd9d27215e adjust wording 2025-11-11 13:20:02 +01:00
Sebastian Stenzel
f91cc2374c fix syntax error (really!) 2025-11-08 10:48:29 +01:00
Sebastian Stenzel
48298bb161 fix syntax error 2025-11-08 10:37:10 +01:00
Sebastian Stenzel
7a1cd9026c use dmg update mechanism 2025-11-08 10:24:11 +01:00
Sebastian Stenzel
754e53d8db cleanup 2025-11-08 10:18:40 +01:00
Sebastian Stenzel
c36f1bc8d0 moved update mechanism classes to integrations-* 2025-11-07 13:59:10 +01:00
Sebastian Stenzel
7315b59a6d simplify + allow IDE to find used i18n strings 2025-11-06 15:36:08 +01:00
Sebastian Stenzel
8a243a01aa cleanup + error handling 2025-11-06 15:35:12 +01:00
Sebastian Stenzel
9e4006cc89 improved error handling 2025-11-06 10:47:15 +01:00
Sebastian Stenzel
26b69beb87 prevent updates while having unlocked vaults 2025-11-06 10:08:49 +01:00
Sebastian Stenzel
f95bf87a4b reordered getters
[ci skip]
2025-11-06 07:41:30 +01:00
Sebastian Stenzel
e854c7d189 detect failed update attempt via new setting 2025-11-05 22:26:45 +01:00
Sebastian Stenzel
8a434dcd96 more robust settings, exposed saveNow() 2025-11-05 22:23:34 +01:00
Sebastian Stenzel
6b7324723e apply more suggestions from code review 2025-11-04 22:07:05 +01:00
Sebastian Stenzel
0bdcb2b3be adjust to type-safe api 2025-11-04 21:42:24 +01:00
Sebastian Stenzel
c3931d9d29 apply review suggestions 2025-11-04 21:42:14 +01:00
Sebastian Stenzel
307825a339 fix param order 2025-11-04 11:43:08 +01:00
Sebastian Stenzel
093f0e8c94 cleanup updater 2025-11-04 11:12:43 +01:00
Sebastian Stenzel
c938c42c00 unified "check for update" and "do update" button 2025-11-02 15:40:32 +01:00
Armin Schrenk
cee22e34db adjust display text 2025-10-31 14:18:07 +01:00
Jerry Pan
884c6f6bdd Fix : Shortcut doesn’t close SimpleDialogs (#4026) 2025-10-31 13:40:19 +01:00
Tobias Hagemann
c5367db971 Fix Hub vault URL to preserve path component 2025-10-29 15:22:24 +01:00
Ralph Plawetzki
9a74194391 Make SecretServiceKeychainAccess the default selection 2025-10-27 05:33:35 +01:00
Armin Schrenk
18300ee67f [skip ci] adjust to changed API 2025-10-21 15:36:51 +02:00
Sebastian Stenzel
59560193ee adjust to new multi-step update API 2025-10-21 11:32:45 +02:00
Sebastian Stenzel
a6b31e19b9 Merge branch 'feature/jdk25' into feature/app-update
# Conflicts:
#	.idea/compiler.xml
#	pom.xml
2025-10-17 17:03:43 +02:00
Sebastian Stenzel
8a44115234 update jacoco to support JDK 25 2025-10-17 15:25:18 +02:00
Sebastian Stenzel
43a1f00bea switch to official openjdk-25-jdk ubuntu package
see https://packages.ubuntu.com/search?suite=jammy&arch=any&searchon=names&keywords=openjdk-25
2025-10-17 12:47:13 +02:00
Sebastian Stenzel
3e458060bc Merge branch 'develop' into feature/jdk25
# Conflicts:
#	.github/workflows/debian.yml
#	.github/workflows/dependency-check.yml
#	.github/workflows/win-exe.yml
#	.idea/misc.xml
#	dist/linux/debian/control
2025-10-17 12:27:28 +02:00
Sebastian Stenzel
4bb5a3f10d fix unresolvable version in setup-java action 2025-10-17 12:12:44 +02:00
Armin Schrenk
8bb451261e Merge branch 'develop' into release/1.18.0 2025-10-16 16:43:01 +02:00
Armin Schrenk
de4c7d47e5 Merge pull request #3821 from cryptomator/feature/restore-vaultconfig
Feature: Vault Recovery Enhancements
2025-10-16 16:36:58 +02:00
Jan-Peter Klein
b4fe63c966 Merge branch 'feature/restore-vaultconfig' of https://github.com/cryptomator/cryptomator into feature/restore-vaultconfig 2025-10-16 15:38:16 +02:00
Jan-Peter Klein
ed45182b6c add IllegalStateException to the catch clause 2025-10-16 15:37:47 +02:00
Armin Schrenk
afd2d74c90 Merge branch 'develop' into feature/restore-vaultconfig 2025-10-16 15:25:27 +02:00
Jan-Peter Klein
4bcf01270a fix owner 2025-10-16 15:12:22 +02:00
Armin Schrenk
5797108dfe Merge branch 'develop' into feature/files-in-use 2025-10-16 12:33:14 +02:00
Armin Schrenk
64466df6b8 [skip ci] revert powershell build early stop (was intended for testing purpose only) 2025-10-16 10:40:35 +02:00
Armin Schrenk
db9bd49dd9 add jdk modules jdk.crypto.ec and jdk.crypto.cryptoki requrired by nimbus-jose-jwt dependency
references e50c949b62
2025-10-16 10:38:01 +02:00
Cryptobot
4ed4a3e3e5 New Crowdin updates (#4009)
New translations strings.properties

Arabic; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Czech; Danish; Dutch; Filipino; Finnish; French; German; Greek; Hungarian; Indonesian; Italian; Japanese; Korean; Latvian; Norwegian Bokmal; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Romanian; Russian; Slovak; Slovenian; Spanish; Swedish; Thai; Turkish; Ukrainian; Uyghur; Vietnamese; 

[ci skip]
2025-10-16 10:19:44 +02:00
Armin Schrenk
e1ff552f08 [skip ci] pick a distinguishable name for fs owner 2025-10-15 17:27:12 +02:00
Armin Schrenk
f3d4494d91 [skip ci] Adjust event view to new API 2025-10-15 17:21:20 +02:00
Armin Schrenk
bdc1cc3f05 Merge branch 'develop' into feature/files-in-use
# Conflicts:
#	pom.xml
2025-10-15 17:20:37 +02:00
Armin Schrenk
ad73b2f31d Merge pull request #4015 from cryptomator/feature/pin-gh-actions-version
Feature: Pin CI actions version
2025-10-15 16:07:09 +02:00
Jan-Peter Klein
227b322b8a revert cleanup that introduced a bug 2025-10-15 15:55:29 +02:00
Armin Schrenk
ccf2e60390 add version info in a comment 2025-10-15 14:55:39 +02:00
Jan-Peter Klein
1104c1067a wording changes 2025-10-15 14:06:00 +02:00
Armin Schrenk
83d2c39080 also pin official github actions 2025-10-15 12:48:49 +02:00
Armin Schrenk
446caff901 Pin exact version of external ci actions 2025-10-15 12:31:38 +02:00
Jan-Peter Klein
90537caee7 change wording 2025-10-15 07:34:17 +02:00
Jan-Peter Klein
25dea8ebe6 infeo suggested wordings 2025-10-14 14:51:12 +02:00
Jan-Peter Klein
4a7d2907c8 use ellipsis 2025-10-14 14:28:53 +02:00
Jan-Peter Klein
7f3af0c7f6 add defaults to switches to solve sonar cloud issues 2025-10-14 13:41:15 +02:00
dependabot[bot]
e6e04078fa Bump the java-production-dependencies group across 1 directory with 5 updates (#4007) 2025-10-14 11:16:41 +00:00
Armin Schrenk
e3f6b5f812 Feature: JFX 25 (#4013)
Closes #3902. References https://github.com/cryptomator/cryptomator/issues/3453 and references https://github.com/cryptomator/cryptomator/pull/3825
2025-10-14 13:15:49 +02:00
Armin Schrenk
f3461f7da4 fix failing CI 2025-10-14 13:06:52 +02:00
Jan-Peter Klein
94518ede99 set the messageLabel directly 2025-10-14 10:04:14 +02:00
Jan-Peter Klein
0fa041da85 remove the unconditional constructor title 2025-10-14 08:15:37 +02:00
Jan-Peter Klein
daa4f3a568 recheck directory structure after restoring backup 2025-10-14 08:02:17 +02:00
Jan-Peter Klein
fc6f7d184c skip unsafe states and check config file before reload 2025-10-14 07:42:24 +02:00
Armin Schrenk
e50c949b62 Bump com.nimbusds:nimbus-jose-jwt from version 9.37.3 to version 10.5
Addresses shaded vulnerability in CVE-2025-53864 (OSSINDEX)
2025-10-13 11:50:54 +02:00
Armin Schrenk
349349e057 Add share option in context menu (#4008) 2025-10-10 11:04:37 +02:00
Armin Schrenk
4b36a1219f [skip ci] bump dependency-check-workflow to version 3 2025-10-08 14:11:57 +02:00
Armin Schrenk
6533ba7367 use more env context in CI 2025-10-08 10:36:29 +02:00
Tobias Hagemann
7f9c9a7df6 Reinstate original macOS icon shape in macOS 26 Tahoe (#3990)
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2025-10-08 09:57:10 +02:00
Armin Schrenk
afda8a4981 only create AUR PR if it is a stable release 2025-09-26 15:39:05 +02:00
Armin Schrenk
cdcd43a805 first draft for JDK 25 migration (including comments) 2025-09-25 12:28:12 +02:00
Jan-Peter Klein
2f4c12a89e remove no more used injections 2025-09-04 08:45:04 +02:00
Jan-Peter Klein
2b79c58577 renaming and reordering 2025-09-04 08:44:18 +02:00
Jan-Peter Klein
1d3c62e2c7 revert cleanup that introduced a bug 2025-09-03 14:58:25 +02:00
Jan-Peter Klein
17166c45ac show correct message 2025-09-03 14:42:20 +02:00
Jan-Peter Klein
f758388063 fix closeOrReturn button 2025-09-03 14:28:16 +02:00
Jan-Peter Klein
8767564977 revert cleanup that introduced a bug 2025-09-03 13:39:08 +02:00
Jan-Peter Klein
59f47116f0 change wording 2025-09-03 12:42:24 +02:00
Armin Schrenk
f5bd23de51 test impl 2025-09-02 09:58:52 +02:00
Jan-Peter Klein
8fac541024 use defaults.vault.vaultName as fallback displayName 2025-09-02 07:40:02 +02:00
Jan-Peter Klein
1ba21b531a declare NoSuchElementException in method head 2025-09-02 07:27:02 +02:00
Jan-Peter Klein
daad48d5af clarify NoSuchElementException log message 2025-09-01 09:33:03 +02:00
Jan-Peter Klein
8eedc62fbe Check vault path before preparing new vault in recovery flow and refactor chooseValidVaultDirectory() to return Path instead of File 2025-09-01 09:14:21 +02:00
Jan-Peter Klein
d65442f042 add explicit VAULT_CONFIG_MISSING case and move legacy comment from LOCKED to NEEDS_MIGRATION 2025-09-01 08:21:28 +02:00
Jan-Peter Klein
216513405a removed unused strings 2025-08-27 10:35:11 +02:00
Jan-Peter Klein
f4c0bc29ed handle NoSuchElementException during recovery key validation 2025-08-27 09:43:28 +02:00
Jan-Peter Klein
d1ce0362b4 extract vault directory selection into new method chooseValidVaultDirectory using Optional 2025-08-27 09:34:04 +02:00
Jan-Peter Klein
4497fb160e new class VaultPreparator in recovery package 2025-08-27 09:22:26 +02:00
Jan-Peter Klein
5bffa28cdb remove unused method 2025-08-27 08:52:51 +02:00
Jan-Peter Klein
14c5693594 check dir structure first in determineVaultState 2025-08-27 08:37:04 +02:00
Jan-Peter Klein
53c6045cca extend initializeLastKnownKeyLoaderIfPossible and deduplicate in create 2025-08-27 07:26:37 +02:00
Jan-Peter Klein
16d441ec44 cleanup 2025-08-21 12:26:16 +02:00
Jan-Peter Klein
fa053ac3df undo no longer needed changes 2025-08-20 10:00:08 +02:00
Jan-Peter Klein
e78a064af6 dialogs with icons and some other ui improvements 2025-08-13 11:40:10 +02:00
Sebastian Stenzel
d5245009f4 Merge branch 'feature/self-update-poc' into feature/app-update
# Conflicts:
#	src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
#	src/main/resources/fxml/preferences_updates.fxml
2025-08-09 09:26:38 +02:00
Sebastian Stenzel
8f4392711e moved update API to integrations-api 2025-08-08 18:21:42 +02:00
Jan-Peter Klein
644e38f49e Merge branch 'develop' into feature/restore-vaultconfig 2025-08-06 13:53:04 +02:00
Jan-Peter Klein
3b38049d71 remove Objects.requireNonNull 2025-08-06 08:18:32 +02:00
Ralph Plawetzki
79bb4a5215 Correlate with API as suggested in a separate PoC: UpdateMechanism and UpdateProcess 2025-08-02 11:32:07 +02:00
Jan-Peter Klein
c199d1cb3b remove optional 2025-08-01 12:51:27 +02:00
Jan-Peter Klein
164a4de6de fix filter 2025-08-01 09:52:03 +02:00
Ralph Plawetzki
2ce7fee06d Log error messages 2025-07-30 19:43:39 +02:00
Ralph Plawetzki
99e9e92f10 Fix progress bar due to JavaFX limitations 2025-07-30 19:21:34 +02:00
Ralph Plawetzki
8e6500d93f Implement update button that updates and respawns the app 2025-07-30 19:21:34 +02:00
Ralph Plawetzki
89ce99deaf Wire UpdateService 2025-07-30 19:21:34 +02:00
Ralph Plawetzki
510e134605 Have multiple services 2025-07-30 19:21:34 +02:00
Ralph Plawetzki
62b434f549 Invent UpdatesModule 2025-07-30 19:21:33 +02:00
Ralph Plawetzki
1f1e336d57 Start FlatpakUpdater 2025-07-30 19:21:33 +02:00
Armin Schrenk
22a37699ea Fixes commit 44f92cfb26. 2025-07-22 13:04:33 +02:00
Armin Schrenk
44f92cfb26 replace null parameter 2025-07-17 12:37:57 +02:00
Jan-Peter Klein
2067c5a33b optimize exception handling
exclude only dir.c9r
2025-07-10 18:45:58 +02:00
Jan-Peter Klein
fc5ed9ecc0 remove unused property 2025-07-10 17:26:45 +02:00
Jan-Peter Klein
5e956c50c2 handle unexpected recovery action type 2025-07-10 11:54:00 +02:00
Jan-Peter Klein
5110b35690 hard coded UI strings replaced
showThirdText property binding
2025-07-10 10:15:11 +02:00
Jan-Peter Klein
499960b3ec replace while with do-while and use Constants.DATA_DIR_NAME 2025-07-10 09:42:00 +02:00
Jan-Peter Klein
f6479ddf24 optimize dialogs 2025-07-10 08:30:46 +02:00
Jan-Peter Klein
5d8b016e5f refactoring 2025-07-10 08:29:51 +02:00
Jan-Peter Klein
2bf2f2b616 exclude dirid.c9r 2025-07-09 19:27:51 +02:00
Jan-Peter Klein
1089e90ebf remove unused imports 2025-07-09 18:39:28 +02:00
Jan-Peter Klein
9fee4afd2a remove duplicate unicode value 2025-07-09 18:37:38 +02:00
Jan-Peter Klein
b00ee4740a legacy vaults supported now 2025-07-07 14:58:19 +02:00
Sebastian Stenzel
02186ca17a hooked up in UI 2025-07-06 20:02:41 +02:00
Sebastian Stenzel
b0ed133e05 PoC 2025-07-06 14:28:57 +02:00
Jan-Peter Klein
f5ecf846f2 resolve SonarCloud issue by safely accessing Optional value 2025-07-05 14:15:59 +02:00
Jan-Peter Klein
a9444182bb clean up 2025-07-04 22:17:27 +02:00
Jan-Peter Klein
191d9473c0 small enhancements 2025-07-04 19:17:24 +02:00
Jan-Peter Klein
8089bcd0d0 cleanup 2025-07-04 17:16:32 +02:00
Jan-Peter Klein
f623b1ee7e better naming, void instead of unused boolean and LOGs 2025-07-03 15:48:21 +02:00
Armin Schrenk
c546f3363b reduce object creation and conversion 2025-07-03 12:08:41 +02:00
Jan-Peter Klein
8a1ec1d073 read file header instead of allBytes to determine Scheme 2025-07-02 11:29:01 +02:00
Jan-Peter Klein
81307b6d75 remove PosixFilePermissions 2025-07-02 11:26:05 +02:00
Jan-Peter Klein
50fa8e0608 tmpDir renamed to cryptomator 2025-07-01 10:44:11 +02:00
Jan-Peter Klein
626a692d63 optimized LOGs 2025-07-01 10:43:10 +02:00
Jan-Peter Klein
c9b9eee564 remove unused methods 2025-07-01 10:42:36 +02:00
Jan-Peter Klein
edc308ccd1 recover button now in VaultListController 2025-06-30 17:36:30 +02:00
Jan-Peter Klein
ad214691f9 fix sonar cloud quality gate fail 2025-06-30 14:04:13 +02:00
Jan-Peter Klein
acf11da202 cleanup
removed MASTERKEY_MISSING
new recoveryActionType RESTORE_ALL
2025-06-30 09:26:30 +02:00
Jan-Peter Klein
4280112eab remove Optional and improve prepareVault() 2025-06-23 13:19:35 +02:00
Jan-Peter Klein
638e1e562d Merge branch 'develop' into feature/restore-vaultconfig 2025-06-23 10:17:43 +02:00
Jan-Peter Klein
321bd2ff8c deduplicate generic.button.close by define a constant 2025-06-23 10:00:38 +02:00
Jan-Peter Klein
323a884718 fix sonar cloud security hotspot 2025-06-23 09:47:07 +02:00
Jan-Peter Klein
5bdfffd151 directoryChooser cancelable 2025-06-23 09:20:25 +02:00
Jan-Peter Klein
6fcec26eea removed additional redetermineVaultState 2025-06-17 14:39:10 +02:00
Jan-Peter Klein
4ddb6b73bd optimised structure and new function initializeLastKnownKeyLoaderIfPossible 2025-06-16 15:22:27 +02:00
Jan-Peter Klein
da845c6941 combined into a single if 2025-06-16 14:38:37 +02:00
Jan-Peter Klein
c30a28aae0 Files.createTempDirectory instead of Files.createDirectory 2025-06-16 14:37:17 +02:00
Jan-Peter Klein
5305c5a631 optimised imports 2025-06-16 14:28:18 +02:00
Jan-Peter Klein
b0d2dcbff3 use constants 2025-06-16 14:18:54 +02:00
Jan-Peter Klein
5c1364792e unwrap ObjectProperty<RecoveryAction> 2025-06-16 14:03:59 +02:00
Jan-Peter Klein
abf8392baa use !Files.exists instead of Files.notExists 2025-06-16 13:49:59 +02:00
Jan-Peter Klein
034a70d473 throw illegalArgumentException directly 2025-06-16 13:40:57 +02:00
Jan-Peter Klein
8c9d75fe57 filter excluded filenames 2025-06-12 14:46:15 +02:00
Jan-Peter Klein
d00d77b747 optimised imports and some pr mentioned changes 2025-06-11 13:49:12 +02:00
Jan-Peter Klein
6d47f28634 changed shorteningThreshold to int
optimize imports
2025-06-05 13:15:54 +02:00
Jan-Peter Klein
225332bd4f pr mentioned changes 2025-06-05 13:01:47 +02:00
Jan-Peter Klein
8c84720e24 solve some todos 2025-06-04 10:47:56 +02:00
Jan-Peter Klein
968e8f51e4 new onboarding dialog
remove is hub vault dialog
2025-05-30 15:30:38 +02:00
Jan-Peter Klein
4de6b83e2f remove RecoverUtil 2025-05-19 14:37:39 +02:00
Jan-Peter Klein
90cbb0c5f6 new recovery package in common 2025-05-19 14:27:05 +02:00
Jan-Peter Klein
e1cc61037e cleanup 2025-03-23 11:15:36 +01:00
Jan-Peter Klein
1ae951126a refactored key validation 2025-03-21 11:03:24 +01:00
Jan-Peter Klein
6bb8c0d4e7 removed unused fxml and controller
dialog fix
2025-03-19 10:09:55 +01:00
Jan-Peter Klein
087b6162ed recoverType fix and more use of dialogs 2025-03-18 14:25:44 +01:00
Jan-Peter Klein
f9e8031acb Merge branch 'develop' into feature/restore-vaultconfig 2025-03-18 11:53:06 +01:00
Jan-Peter Klein
ebd767595b get cipherCombo while key validation 2025-03-17 18:39:17 +01:00
Jan-Peter Klein
6231c5b8b3 restore checkbox and other small ui rearrangements 2025-03-17 16:10:16 +01:00
Jan-Peter Klein
a3b8297e23 recoverykey validate 2025-03-14 13:51:25 +01:00
Jan-Peter Klein
6715ff9f5f introduce RecoverUtil.Types 2025-03-06 09:17:16 +01:00
Jan-Peter Klein
8c5325511c created more static methods in RecoverUtil, added first flow for choose dir and restore 2025-02-28 13:06:35 +01:00
Jan-Peter Klein
895614353e improve RecoverUtil and determineVaultState 2025-02-21 23:24:34 +01:00
Jan-Peter Klein
676c507a50 add RecoverUtil and some ui changes 2025-02-21 19:43:09 +01:00
Jan-Peter Klein
5f4fd92b1d Merge branch 'develop' into feature/restore-vaultconfig 2025-02-19 19:49:35 +01:00
Jan-Peter Klein
8191a7dae6 add isHubVault dialog 2025-01-15 21:05:27 +01:00
Jan-Peter Klein
756672258d add Hub Vault check for MASTERKEY_MISSING state 2025-01-14 15:29:49 +01:00
Jan-Peter Klein
fb6f7072d4 new vault states implemented in selectVaultRemovable 2025-01-14 15:12:21 +01:00
Jan-Peter Klein
aa95ad40c0 added vault config success scene 2025-01-13 15:31:55 +01:00
Jan-Peter Klein
ba3667ab51 restore config by recoverykey
auto restore when bkup exists
added new VaultStates
2024-12-11 16:30:56 +01:00
279 changed files with 7170 additions and 1664 deletions

View File

@@ -20,6 +20,10 @@
Translations are not managed directly in this repository. Instead, we use [Crowdin](https://translate.cryptomator.org/), which automatically synchronizes translations with this repository. If you want to help us with translations, please visit our translation project on Crowdin.
## Use of Generative AI
AI tools may assist your work, but every contribution must be fully understood, reviewed, and tested by you. Only submit changes you can clearly explain and justify. Unverified or low-quality AI output that wastes our time and resources will be closed without further review.
## Code of Conduct
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md).

View File

@@ -0,0 +1,76 @@
name: 'Windows Code Signing'
description: 'Sign files on Windows with the Azure Trusted Signing'
inputs:
base-dir:
description: 'Absolute path to the base directory to search for files'
required: true
recursive:
description: 'Whether to search recursively in subdirectories'
required: false
default: 'false'
file-extensions:
description: 'List of file extensions to sign, separated by comma'
required: true
default: 'exe,dll,ps1'
description:
description: 'Signature description'
required: true
default: 'Cryptomator'
url:
description: 'Signature URL'
required: false
default: 'https://cryptomator.org'
append-signature:
description: 'Whether to append the signature to existing signatures'
required: false
default: 'false'
tenant-id:
description: 'Azure Tenant ID'
required: true
client-id:
description: 'Azure Client ID'
required: true
client-secret:
description: 'Azure Client Secret'
required: true
runs:
using: 'composite'
steps:
- name: Generate, mask, and output the input secrets
id: set-secrets
run: |
echo "::add-mask::${{ inputs.tenant-id }}"
echo "::add-mask::${{ inputs.client-id }}"
echo "::add-mask::${{ inputs.client-secret }}"
echo "tenant-id=${{ inputs.tenant-id }}" >> "$GITHUB_OUTPUT"
echo "client-id=${{ inputs.client-id }}" >> "$GITHUB_OUTPUT"
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
shell: bash
- name: Sign DLLs with Azure Trusted Signing
uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0
with:
files-folder: ${{ inputs.base-dir }}
files-folder-filter: ${{ inputs.file-extensions }}
files-folder-recurse: ${{ inputs.recursive }}
append-signature: ${{ inputs.append-signature }}
description: ${{ inputs.description }}
description-url: ${{ inputs.url }}
azure-tenant-id: ${{ steps.set-secrets.outputs.tenant-id }}
azure-client-id: ${{ steps.set-secrets.outputs.client-id }}
azure-client-secret: ${{ steps.set-secrets.outputs.client-secret }}
signing-account-name: cryptomatorSigning
certificate-profile-name: production
endpoint: https://weu.codesigning.azure.net/
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
exclude-environment-credential: false
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true

View File

@@ -19,7 +19,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.2+10.0.LTS'
jobs:
get-version:
@@ -37,16 +37,16 @@ jobs:
include:
- os: ubuntu-latest
appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
openjfx-sha: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
openjfx-sha: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
- os: ubuntu-24.04-arm
appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
openjfx-sha: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
openjfx-sha: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -75,7 +75,7 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Plinux -DskipTests -Djavafx.platform=linux
run: mvn -B clean package -Plinux -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -95,7 +95,7 @@ jobs:
--verbose
--output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
--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.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
--strip-native-commands
--no-header-files
--no-man-pages
@@ -117,14 +117,13 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
@@ -176,7 +175,7 @@ jobs:
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.zsync
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: appimage-${{ matrix.appimage-suffix }}
path: |
@@ -186,7 +185,7 @@ jobs:
if-no-files-found: error
- name: Publish AppImage on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
@@ -199,10 +198,10 @@ jobs:
name: Create PR for aur-bin repo
needs: [build, get-version]
runs-on: ubuntu-latest
if: github.event_name == 'release'
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
steps:
- name: Download AppImages
uses: actions/download-artifact@v5
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: downloads/
merge-multiple: true
@@ -213,7 +212,7 @@ jobs:
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'cryptomator/aur-bin'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -248,7 +247,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'

View File

@@ -19,6 +19,8 @@ jobs:
runs-on: ubuntu-latest
needs: [get-version]
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
env:
INPUT_TAG: ${{ inputs.tag }}
outputs:
url: ${{ steps.url.outputs.url}}
sha256: ${{ steps.sha256.outputs.sha256}}
@@ -27,8 +29,8 @@ jobs:
id: url
run: |
URL="";
if [[ -n "${{ inputs.tag }}" ]]; then
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
if [[ -n "${INPUT_TAG}" ]]; then
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${INPUT_TAG}.tar.gz"
else
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
fi
@@ -46,7 +48,7 @@ jobs:
env:
AUR_PR_URL: tbd
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'cryptomator/aur'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -79,8 +81,8 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
if: github.event_name == 'release'
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'

View File

@@ -7,6 +7,16 @@ on:
description: "Url to the file to upload"
required: true
type: string
avast:
description: "Upload to Avast"
required: false
type: boolean
default: true
kaspersky:
description: "Upload to Kaspersky"
required: false
type: boolean
default: true
workflow_dispatch:
inputs:
url:
@@ -30,16 +40,18 @@ jobs:
runs-on: ubuntu-latest
outputs:
fileName: ${{ steps.extractName.outputs.fileName}}
env:
INPUT_URL: ${{ inputs.url }}
steps:
- name: Extract file name
id: extractName
run: |
url="${{ inputs.url }}"
url="${INPUT_URL}"
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
- name: Download file
run: curl --remote-name ${{ inputs.url }} -L -o ${{steps.extractName.outputs.fileName}}
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ steps.extractName.outputs.fileName }}
path: ${{ steps.extractName.outputs.fileName }}
@@ -48,15 +60,15 @@ jobs:
name: Anti Virus Allowlisting Kaspersky
runs-on: ubuntu-latest
needs: download-file
if: github.event_name == 'workflow_call' || inputs.kaspersky
if: inputs.kaspersky
steps:
- name: Download artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # v4.6.3
with:
protocol: ftps
server: allowlist.kaspersky-labs.com
@@ -68,15 +80,15 @@ jobs:
name: Anti Virus Allowlisting Avast
runs-on: ubuntu-latest
needs: download-file
if: github.event_name == 'workflow_call' || inputs.avast
if: inputs.avast
steps:
- name: Download artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
- name: Upload to Avast
uses: wlixcc/SFTP-Deploy-Action@v1.2.6
- name: Upload to Avast
uses: wlixcc/SFTP-Deploy-Action@a5ccb9c6211a94cc59404f0fdb2a9936a6dfee64 # v1.2.6
with:
server: whitelisting.avast.com
port: 22

View File

@@ -11,7 +11,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
defaults:
run:
@@ -22,14 +22,14 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@v4
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
@@ -37,7 +37,7 @@ jobs:
- name: Build and Test
run: >
xvfb-run
mvn -B verify -Djavafx.platform=linux
mvn -B verify
jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Pcoverage
@@ -49,28 +49,28 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Draft a release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
draft: true
discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true
body: |-
:construction: Work in Progress
> [!NOTE]
> 🚧 Work in Progress 🚧
>
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
<!--REPLACE with auto-generated release notes (see below)
### What's New 🎉
### Bugfixes 🐛
### Other Changes 📎
END REPLACE-->
Feel free to also read our [CHANGELOG.md](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
---
TODO FULL CHANGELOG
📜 List of closed issues is available [here](TODO)
---
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
<!-- Don't forget to include the
💾 SHA-256 checksums of release artifacts:
@@ -78,4 +78,9 @@ jobs:
```
-->
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
> [!TIP]
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
<!-- Auto-Generated Release Notes: -->

View File

@@ -6,7 +6,7 @@ on:
workflow_dispatch:
env:
JDK_VERSION: '24.0.1+9'
JDK_VERSION: '25.0.1+8.0.LTS'
JDK_VENDOR: temurin
RUNTIME_VERSION_HELPER: >
public class Test {
@@ -23,10 +23,10 @@ jobs:
JDK_MAJOR_VERSION: 'toBeFilled'
steps:
- name: Determine current major version
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$env:GITHUB_ENV"
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
shell: pwsh
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: ${{ env.JDK_MAJOR_VERSION}}
distribution: ${{ env.JDK_VENDOR }}
@@ -70,7 +70,7 @@ jobs:
}
- name: Notify
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
uses: rtCamp/action-slack-notify@v2
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'

View File

@@ -23,13 +23,12 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
COFFEELIBS_JDK: 24
COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
JAVA_VERSION: '25.0.2+10.0.LTS'
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
jobs:
get-version:
@@ -41,30 +40,34 @@ jobs:
name: Build Debian Package
runs-on: ubuntu-22.04
needs: [get-version]
env:
INPUT_PPAVER: ${{ inputs.ppaver }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: deb-version
name: Determine deb-version
run: |
if [ -n "${{inputs.ppaver}}" ]; then
echo "debVersion=${{inputs.ppaver }}" >> "$GITHUB_OUTPUT"
if [ -n "${INPUT_PPAVER}" ]; then
echo "debVersion=${INPUT_PPAVER}" >> "$GITHUB_OUTPUT"
else
echo "debVersion=${{needs.get-version.outputs.semVerStr}}" >> "$GITHUB_OUTPUT"
fi
- name: Install build tools
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
sudo apt-get install devscripts dput
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
env:
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
- name: Run maven
run: mvn -B clean package -Plinux -Djavafx.platform=linux -DskipTests
run: mvn -B clean package -Plinux -DskipTests
- name: Download OpenJFX jmods
id: download-jmods
run: |
@@ -140,7 +143,7 @@ jobs:
run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: linux-deb-package
path: |

View File

@@ -7,12 +7,13 @@ on:
jobs:
check-dependencies:
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@v1
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 24
check-command: 'mvn -B validate -Pdependency-check -Djavafx.platform=linux'
java-version: 25
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
ossindex-token: ${{ secrets.OSSINDEX_API_TOKEN }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- name: Get download count of latest releases
id: get-stats
uses: actions/github-script@v8
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const query = `query($owner:String!, $name:String!) {
@@ -53,7 +53,7 @@ jobs:
INTERVAL: 900
JSON_DATA: ${{ steps.get-stats.outputs.result }}
- name: Upload Results
uses: fjogeleit/http-request-action@v1
uses: fjogeleit/http-request-action@551353b829c3646756b2ec2b3694f819d7957495 # v2.0.0
with:
url: 'https://graphite-us-central1.grafana.net/metrics'
method: 'POST'

View File

@@ -14,7 +14,7 @@ jobs:
- name: Query Discussion Data
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
id: query-data
uses: actions/github-script@v8
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
@@ -42,7 +42,7 @@ jobs:
return await github.graphql(query, variables)
- name: Get Gist
id: get-gist
uses: andymckay/get-gist-action@master
uses: andymckay/get-gist-action@cf3bc8164af24126f7e5979eb6d3dc0c12309bd1 # not_tagged
with:
gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230
- name: Merge Error Code Data
@@ -58,7 +58,7 @@ jobs:
env:
DISCUSSION: ${{ steps.query-data.outputs.result }}
- name: Patch Gist
uses: exuanbo/actions-deploy-gist@v1
uses: exuanbo/actions-deploy-gist@47697fceaeea2006a90594ee24eb9cd0a1121ef8 # v1.1.4
with:
token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }}
gist_id: accba9fb9555e7192271b85606f97230

View File

@@ -26,13 +26,10 @@ jobs:
- name: Determine tarball url
id: url
run: |
URL="";
if [[ -n "${{ inputs.tag }}" ]]; then
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
else
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
fi
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz"
echo "url=${URL}" >> "$GITHUB_OUTPUT"
env:
TAG: ${{ inputs.tag || github.event.release.tag_name}}
- name: Download source tarball and compute checksum
id: sha512
run: |
@@ -46,7 +43,7 @@ jobs:
env:
FLATHUB_PR_URL: tbd
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'flathub/org.cryptomator.Cryptomator'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -74,7 +71,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
if: github.event_name == 'release'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -23,7 +23,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
jobs:
determine-version:
@@ -35,11 +35,11 @@ jobs:
revNum: ${{ steps.versions.outputs.revNum }}
type: ${{ steps.versions.outputs.type}}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -49,8 +49,8 @@ jobs:
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ inputs.version }}"
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${VERSION_STRING}"
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -70,7 +70,9 @@ jobs:
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
echo "type=${TYPE}" >> $GITHUB_OUTPUT
env:
VERSION_STRING: ${{ inputs.version }}
- name: Validate Version
uses: skymatic/semver-validation-action@v3
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
with:
version: ${{ steps.versions.outputs.semVerStr }}

View File

@@ -24,7 +24,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.2+10.0.LTS'
jobs:
get-version:
@@ -44,12 +44,12 @@ jobs:
architecture: x64
output-suffix: x64
fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-x64_bin-jmods.zip'
openjfx-sha: '6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-x64_bin-jmods.zip'
openjfx-sha: '0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -79,7 +79,7 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -99,7 +99,7 @@ jobs:
--verbose
--output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
--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
--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.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands
--no-header-files
--no-man-pages
@@ -121,7 +121,6 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\""
@@ -129,14 +128,15 @@ jobs:
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
@@ -151,9 +151,23 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Build and install DockTilePlugin
env:
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
run: |
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-destination "platform=macOS,arch=x86_64" \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p Cryptomator.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
- name: Generate license for dmg
run: >
mvn -B -Djavafx.platform=mac license:add-third-party
mvn -B license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -248,7 +262,7 @@ jobs:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@v1
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
@@ -269,7 +283,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -278,7 +292,7 @@ jobs:
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}

View File

@@ -22,7 +22,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.2+10.0.LTS'
jobs:
get-version:
@@ -42,12 +42,12 @@ jobs:
architecture: aarch64
output-suffix: arm64
fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-aarch64_bin-jmods.zip'
openjfx-sha: '13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -77,7 +77,7 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -97,7 +97,7 @@ jobs:
--verbose
--output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
--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
--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.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands
--no-header-files
--no-man-pages
@@ -119,7 +119,6 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\""
@@ -127,14 +126,15 @@ jobs:
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--mac-package-identifier org.cryptomator
@@ -150,9 +150,23 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Build and install DockTilePlugin
env:
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
run: |
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-destination "platform=macOS,arch=arm64" \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p Cryptomator.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
- name: Generate license for dmg
run: >
mvn -B -Djavafx.platform=mac license:add-third-party
mvn -B license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -247,7 +261,7 @@ jobs:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@v1
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
@@ -268,7 +282,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -277,7 +291,7 @@ jobs:
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
days-before-stale: 14
days-before-close: 0

View File

@@ -19,14 +19,14 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Publish asc on GitHub Releases
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
cryptomator-*.tar.gz.asc
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'

View File

@@ -5,7 +5,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
defaults:
run:
@@ -16,11 +16,11 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage -Djavafx.platform=linux
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage

View File

@@ -12,16 +12,16 @@ defaults:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 23
JAVA_VERSION: 25
jobs:
check-preconditions:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -49,7 +49,7 @@ jobs:
exit 1
fi
- name: Cache NVD DB
uses: actions/cache@v4
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.m2/repository/org/owasp/dependency-check-data/
key: dependency-check-${{ github.run_id }}
@@ -60,6 +60,6 @@ jobs:
- name: Run org.owasp:dependency-check plugin
id: dependency-check
continue-on-error: true
run: mvn -B verify -Pdependency-check -DskipTests -Djavafx.platform=linux
run: mvn -B verify -Pdependency-check -DskipTests
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
days-before-stale: 365
days-before-close: 90

View File

@@ -8,10 +8,6 @@ on:
version:
description: 'Version'
required: false
isDebug:
description: 'Build debug version with console output'
type: boolean
default: false
sign:
description: 'Sign binaries'
required: false
@@ -26,8 +22,8 @@ on:
env:
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
@@ -51,18 +47,13 @@ jobs:
include:
- arch: x64
os: windows-latest
java-dist: 'zulu'
java-version: '24.0.1+9'
java-dist: 'temurin' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
java-version: '25.0.2+10.0.LTS'
java-package: 'jdk'
- arch: arm64
os: windows-11-arm
java-dist: 'liberica'
java-version: '24.0.1+11'
java-package: 'jdk+fx' #This is needed, as liberica contains JFX 24 Jmods for Windows ARM64
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }}
@@ -102,7 +93,7 @@ jobs:
- name: Set version
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
run: mvn -B clean package -Pwin -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -122,7 +113,7 @@ jobs:
--verbose
--output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
--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.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands
--no-header-files
--no-man-pages
@@ -144,14 +135,13 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
@@ -195,12 +185,31 @@ jobs:
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
shell: pwsh
run: |
New-Item -Path appdir/jpackage-jmod -ItemType Directory
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
$extractDir = "appdir/jpackage-jimage"
New-Item -Path $extractDir -ItemType Directory -Force | Out-Null
& "$env:JAVA_HOME\bin\jimage.exe" extract --dir $extractDir "$env:JAVA_HOME\lib\modules"
$wixhelper = Get-ChildItem -Path $extractDir -Recurse -File -Filter "wixhelper.dll" | Select-Object -First 1
if (-not $wixhelper) {
throw "wixhelper.dll not found in $env:JAVA_HOME\lib\modules"
}
Copy-Item -Path $wixhelper.FullName -Destination "appdir/wixhelper.dll" -Force
Remove-Item -Path $extractDir -Recurse -Force
- name: Sign DLLs with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\appdir
recursive: true
append-signature: true
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign DLLs with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'appdir'
file-extensions: 'dll,exe,ps1'
@@ -225,7 +234,7 @@ jobs:
}
- name: Generate license for MSI
run: >
mvn -B license:add-third-party "-Djavafx.platform=win"
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
@@ -257,16 +266,16 @@ jobs:
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Sign msi with Actalis CodeSigner
- name: Sign MSI with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
uses: ./.github/actions/win-sign-action
with:
base-dir: 'installer'
file-extensions: 'msi'
sign-description: 'Cryptomator Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
base-dir: ${{ github.workspace }}\installer
file-extensions: msi
description: 'Cryptomator Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Add possible alpha/beta tags and architecture to installer name
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
- name: Create detached GPG signature with key 615D449FE6E6A235
@@ -277,7 +286,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: msi-${{ matrix.arch }}
path: |
@@ -298,28 +307,22 @@ jobs:
java-dist: 'zulu'
java-version: '24.0.1+9'
java-package: 'jdk'
- arch: arm64
os: windows-11-arm
executable-suffix: arm64
java-dist: 'liberica'
java-version: '24.0.1+11'
java-package: 'jdk+fx' #This is needed, as liberica contains JFX 24 Jmods for Windows ARM64
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install wix and extensions
run: |
dotnet tool install --global wix --version 6.0.0
wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.0 --global
wix.exe extension add WixToolset.Util.wixext/6.0.0 --global
- name: Download .msi
uses: actions/download-artifact@v5
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: msi-${{ matrix.arch }}
path: dist/win/bundle/resources
- name: Strip version info from msi file name
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }}
@@ -328,7 +331,7 @@ jobs:
cache: 'maven'
- name: Generate license for exe
run: >
mvn -B license:add-third-party "-Djavafx.platform=win"
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources"
@@ -369,9 +372,20 @@ jobs:
- name: Detach burn engine in preparation to sign
run: >
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\tmp
file-extensions: exe
append-signature: true
description: 'Cryptomator Bundle Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign burn engine with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'tmp'
file-extensions: 'exe'
@@ -382,9 +396,20 @@ jobs:
- name: Reattach signed burn engine to installer
run: >
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
- name: Sign EXE installer with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
file-extensions: exe
append-signature: true
description: 'Cryptomator Bundle Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign installer with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'installer'
file-extensions: 'exe'
@@ -402,7 +427,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: exe-${{ matrix.executable-suffix }}
path: |
@@ -417,26 +442,22 @@ jobs:
needs: [ build-msi, build-exe ]
outputs:
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
download-url-msi-arm64: ${{ fromJSON(steps.publish.outputs.assets)[1].browser_download_url }}
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
download-url-exe-arm64: ${{ fromJSON(steps.publish.outputs.assets)[3].browser_download_url }}
steps:
- name: Download installers
uses: actions/download-artifact@v5
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
merge-multiple: true
- name: Publish installers on GitHub Releases
id: publish
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
# do not change ordering of filelist, required for correct job output
files: |
*x64.msi
*arm64.msi
*x64.exe
*arm64.exe
*.asc
allowlist-msi-x64:
@@ -446,13 +467,6 @@ jobs:
url: ${{ needs.publish.outputs.download-url-msi-x64 }}
secrets: inherit
allowlist-msi-arm64:
uses: ./.github/workflows/av-whitelist.yml
needs: [ publish ]
with:
url: ${{ needs.publish.outputs.download-url-msi-arm64 }}
secrets: inherit
allowlist-exe-x64:
uses: ./.github/workflows/av-whitelist.yml
needs: [ publish, allowlist-msi-x64 ]
@@ -460,13 +474,6 @@ jobs:
url: ${{ needs.publish.outputs.download-url-exe-x64 }}
secrets: inherit
allowlist-exe-arm64:
uses: ./.github/workflows/av-whitelist.yml
needs: [ publish, allowlist-msi-arm64 ]
with:
url: ${{ needs.publish.outputs.download-url-exe-arm64 }}
secrets: inherit
notify-winget:
name: Notify for winget-release
if: needs.get-version.outputs.versionType == 'stable'
@@ -474,7 +481,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'

View File

@@ -18,7 +18,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Submit package
uses: vedantmgoyal2009/winget-releaser@main
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
with:
identifier: Cryptomator.Cryptomator
version: ${{ inputs.tag }}

48
.idea/compiler.xml generated
View File

@@ -12,18 +12,15 @@
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<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/2.55/dagger-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.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$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.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$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.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/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" />
@@ -31,14 +28,41 @@
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.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/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$/javax/inject/javax.inject/1/javax.inject-1.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.3/checker-compat-qual-2.5.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.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$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.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.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.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/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$/org/checkerframework/checker-qual/3.41.0/checker-qual-3.41.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.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$/javax/inject/javax.inject/1/javax.inject-1.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.3/checker-compat-qual-2.5.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
@@ -46,7 +70,7 @@
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" />
<module name="cryptomator" options="-Adagger.fastInit=enabled" />
</option>
</component>
</project>

View File

@@ -1,10 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="Deprecation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="DEPRECATED_ATTRIBUTES" />
<inspection_tool class="MarkedForRemoval" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
<inspection_tool class="RedundantScheduledForRemovalAnnotation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
</profile>
</component>

2
.idea/misc.xml generated
View File

@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" project-jdk-name="24" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

43
CHANGELOG.md Normal file
View File

@@ -0,0 +1,43 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The changelog starts with version 1.19.0.
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
### Added
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
* Implemented `.dmg` update mechanism
* Implemented Flatpak update mechanism
* App notifications ([#4069](https://github.com/cryptomator/cryptomator/pull/4069))
* Mark files in-use for Hub vaults ([#4078](https://github.com/cryptomator/cryptomator/pull/4078))
* Accessibility: Adjust app to be used with a screen reader ([#547](https://github.com/cryptomator/cryptomator/issues/547))
* Show Archived Vault Dialog on unlock when Hub returns 410 ([#4081](https://github.com/cryptomator/cryptomator/pull/4081))
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
### Changed
* Built using JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/issues/4031))
* Modernized Template for GitHub Releases
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
* Updated dependencies
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
* `com.google.dagger:*` from 2.57.2 to 2.59.1
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1

8
dist/common/cryptomator.config vendored Normal file
View File

@@ -0,0 +1,8 @@
# This is the Cryptomator administrative configuration file.
# It is a simple key-value pair file.
# Lines starting with '#' are comments and will be ignored.
# For more info, read the docs at https://docs.cryptomator.org/desktop/advanced-settings/
#
# Example:
# Sets the plugin directory and enables plugin loading
# cryptomator.pluginDir=@{userhome}/Cryptomator/Plugins

View File

@@ -19,16 +19,16 @@ if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=linux
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
cp ../../../LICENSE.txt ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods
JAVAFX_VERSION=24.0.1
JAVAFX_VERSION=25
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256='425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
JAVAFX_JMODS_SHA256='96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
if [ "${CPU_ARCH}" = "aarch64" ]; then
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256='7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
JAVAFX_JMODS_SHA256='951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1'
fi
# download javaFX jmods
@@ -62,7 +62,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \
--output runtime \
--module-path "${JMOD_PATHS}" \
--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.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -88,8 +88,8 @@ ${JAVA_HOME}/bin/jpackage \
--app-version "${VERSION}.${REVISION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
@@ -115,6 +115,7 @@ cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/us
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
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.encrypted.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.encrypted.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/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/vnd.cryptomator.encrypted">
<comment>Cryptomator Encrypted Data</comment>
<glob pattern="*.c9r"/>
<glob pattern="*.c9s"/>
<glob pattern="*.c9u"/>
</mime-type>
</mime-info>

View File

@@ -83,6 +83,9 @@
</content_rating>
<releases>
<release date="2025-11-12" version="1.18.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
</release>
<release date="2025-07-08" version="1.17.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.17.1</url>
</release>

View File

@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-24 (>= 24.0.1+9-0ppa3), libgtk-3-0, libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git
@@ -12,7 +12,7 @@ Package: cryptomator
Architecture: any
Section: utils
Priority: optional
Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3
Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3, libgtk-3-0 (>= 3.20.0)
Recommends: gvfs-backends, gvfs-fuse, gnome-keyring
XB-AppName: Cryptomator
XB-Category: Utility;Security;FileTools;

View File

@@ -6,4 +6,5 @@ common/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/sca
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
common/application-vnd.cryptomator.encrypted.xml usr/share/mime/packages

View File

@@ -25,6 +25,7 @@ case "$1" in
fi
xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
;;
abort-upgrade|abort-remove|abort-deconfigure)

View File

@@ -23,6 +23,7 @@ case "$1" in
xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
;;
failed-upgrade)

View File

@@ -4,11 +4,12 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-24-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
else ifeq ($(DEB_BUILD_ARCH),arm64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
endif
@@ -28,7 +29,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \
--output runtime \
--module-path "${JMODS_PATH}" \
--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.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -45,14 +46,13 @@ override_dh_auto_build:
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--java-options "--sun-misc-unsafe-memory-access=allow" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \

1
dist/mac/.gitignore vendored
View File

@@ -1 +1,2 @@
embedded.provisionprofile
xcuserdata/

View File

@@ -0,0 +1,19 @@
//
// CryptomatorDockTilePlugin.swift
// Integrations
//
// Created by Tobias Hagemann on 22.09.25.
// Copyright © 2025 Cryptomator. All rights reserved.
//
import AppKit
class CryptomatorDockTilePlugin: NSObject, NSDockTilePlugIn {
func setDockTile(_ dockTile: NSDockTile?) {
guard let dockTile = dockTile, let image = Bundle(for: Self.self).image(forResource: "Cryptomator") else {
return
}
dockTile.contentView = NSImageView(image: image)
dockTile.display()
}
}

View File

@@ -0,0 +1,314 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */; };
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */ = {isa = PBXBuildFile; fileRef = 74E08DEC2E858532007E665C /* Cryptomator.icns */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cryptomator.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptomatorDockTilePlugin.swift; sourceTree = "<group>"; };
74E08DEC2E858532007E665C /* Cryptomator.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Cryptomator.icns; path = ../resources/Cryptomator.icns; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
74E08DD62E858467007E665C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
74E08DD02E858467007E665C = {
isa = PBXGroup;
children = (
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */,
74E08DEC2E858532007E665C /* Cryptomator.icns */,
74E08DDA2E858467007E665C /* Products */,
);
sourceTree = "<group>";
};
74E08DDA2E858467007E665C /* Products */ = {
isa = PBXGroup;
children = (
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
74E08DD82E858467007E665C /* DockTilePlugin */ = {
isa = PBXNativeTarget;
buildConfigurationList = 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */;
buildPhases = (
74E08DD52E858467007E665C /* Sources */,
74E08DD62E858467007E665C /* Frameworks */,
74E08DD72E858467007E665C /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = DockTilePlugin;
packageProductDependencies = (
);
productName = DockTilePlugin;
productReference = 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
74E08DD12E858467007E665C /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 2600;
ORGANIZATIONNAME = Cryptomator;
TargetAttributes = {
74E08DD82E858467007E665C = {
CreatedOnToolsVersion = 26.0.1;
};
};
};
buildConfigurationList = 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 74E08DD02E858467007E665C;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 74E08DDA2E858467007E665C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
74E08DD82E858467007E665C /* DockTilePlugin */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
74E08DD72E858467007E665C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
74E08DD52E858467007E665C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
74E08DDB2E858467007E665C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = YZQJQUHA3L;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 11.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
74E08DDC2E858467007E665C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = YZQJQUHA3L;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 11.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
74E08DDE2E858467007E665C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
PRODUCT_NAME = Cryptomator;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = Debug;
};
74E08DDF2E858467007E665C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
PRODUCT_NAME = Cryptomator;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
74E08DDB2E858467007E665C /* Debug */,
74E08DDC2E858467007E665C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
74E08DDE2E858467007E665C /* Debug */,
74E08DDF2E858467007E665C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 74E08DD12E858467007E665C /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "74E08DD82E858467007E665C"
BuildableName = "Cryptomator.docktileplugin"
BlueprintName = "DockTilePlugin"
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "74E08DD82E858467007E665C"
BuildableName = "Cryptomator.docktileplugin"
BlueprintName = "DockTilePlugin"
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

28
dist/mac/dmg/build.sh vendored
View File

@@ -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'`
FUSE_LIB="FUSE-T"
JAVAFX_VERSION=24.0.1
JAVAFX_VERSION=25
JAVAFX_ARCH="undefined"
JAVAFX_JMODS_SHA256="undefined"
if [ "$(machine)" = "arm64e" ]; then
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256="b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4"
JAVAFX_JMODS_SHA256="13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba"
else
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256="6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105"
JAVAFX_JMODS_SHA256="0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52"
fi
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
@@ -71,7 +71,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
fi
# compile
mvn -B -Djavafx.platform=mac -f../../../pom.xml clean package -DskipTests -Pmac
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
@@ -85,7 +85,7 @@ fi
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JMOD_PATHS}" \
--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.security.auth,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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,java.compiler \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -114,15 +114,16 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
@@ -133,8 +134,21 @@ sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
# build and install dock tile plugin
echo "Building and installing Cryptomator.docktileplugin..."
DERIVED_DATA_PATH=../DockTilePlugin/build
xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p ${APP_NAME}.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
# generate license
mvn -B -Djavafx.platform=mac -f../../../pom.xml license:add-third-party \
mvn -B -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \

View File

@@ -105,6 +105,7 @@
<array>
<string>c9r</string>
<string>c9s</string>
<string>c9u</string>
</array>
<key>public.mime-type</key>
<array>
@@ -116,5 +117,8 @@
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<!-- register dock tile plugin -->
<key>NSDockTilePlugIn</key>
<string>Cryptomator.docktileplugin</string>
</dict>
</plist>

16
dist/win/build.ps1 vendored
View File

@@ -65,7 +65,7 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin "-Djavafx.platform=win"
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime
@@ -86,16 +86,16 @@ switch ($archName) {
'ARM64' {
$javafxBaseJmod = Join-Path $Env:JAVA_HOME "jmods\javafx.base.jmod"
if (!(Test-Path $javafxBaseJmod)) {
Write-Error "JavaFX module not found in JDK. Please ensure full JDK (including jmods) is installed."
Write-Error "JavaFX module not found in JDK. Please ensure a JDK with JavaFX (including jmods) is installed."
exit 1
}
$jmodPaths = "$Env:JAVA_HOME/jmods"
}
'x64' {
$javaFxVersion='24.0.1'
$javaFxVersion='25'
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
$javaFxJmodsSHA256 = 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
$javaFxJmodsSHA256 = 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
$javaFxJmods = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $javaFxJmods) ) {
@@ -133,7 +133,7 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
--verbose `
--output runtime `
--module-path $jmodPaths `
--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,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--strip-native-commands `
--no-header-files `
--no-man-pages `
@@ -155,7 +155,7 @@ $javaOptions = @(
"--java-options", "-Djava.net.useSystemProxies=true"
"--java-options", "-Dcryptomator.logDir=`"@{localappdata}/$AppName`""
"--java-options", "-XX:ErrorFile=`"C:/cryptomator/cryptomator_crash.log`""
"--java-options", "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`""
"--java-options", "-Dcryptomator.adminConfigPath=`"C:/ProgramData/$AppName/config.properties`""
"--java-options", "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
"--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
"--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
@@ -194,7 +194,7 @@ if ($LASTEXITCODE -ne 0) {
}
#Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\resources\" `
@@ -237,7 +237,7 @@ if ($LASTEXITCODE -ne 0) {
}
#Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `

12
dist/win/contrib/disableUserConfig.bat vendored Normal file
View File

@@ -0,0 +1,12 @@
@echo off
:: Batch wrapper for PowerShell script to disable user configuration in Cryptomator
:: This is executed as a Custom Action during MSI installation
:: This file must be located in the INSTALLDIR
:: Change to INSTALLDIR
cd %~dp0
:: Execute the PowerShell script
powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File ".\disableUserConfig.ps1"
:: Return the exit code from PowerShell
exit /b %ERRORLEVEL%

24
dist/win/contrib/disableUserConfig.ps1 vendored Normal file
View File

@@ -0,0 +1,24 @@
# PowerShell script to disable user configuration
# This script is executed as a Custom Action during MSI installation
# It deletes the file .package, effectively disabling user specific jpackage configuration.
# NOTE: This file must be located in the same directory as set in the MSI property INSTALLDIR
try {
# Determine file path
$packageFile = Join-Path $PSScriptRoot 'app\.package'
#check if file exists
if (Test-Path -Path $packageFile) {
Write-Host "Deleting file: $packageFile"
Remove-Item -Path $packageFile -Force -ErrorAction Stop
} else {
Write-Host "File not found: $packageFile. Skipping deletion."
}
exit 0
}
catch {
Write-Error "Error deleting package file: $_"
exit 1
}

View File

@@ -87,17 +87,37 @@
<!-- Non-Opening ProgID -->
<ns0:DirectoryRef Id="INSTALLDIR">
<ns0:Component Bitness="always64" Id="nonStartingProgID" >
<ns0:Component Bitness="always64" Id="nonStartingProgID">
<ns0:File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileEncryptedData)" Name="$(var.IconFileEncryptedData)"/>
<ns0:ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<ns0:Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
<ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/>
</ns0:Extension>
<ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
</ns0:ProgId>
</ns0:Component>
</ns0:DirectoryRef>
<ns0:StandardDirectory Id="CommonAppDataFolder">
<ns0:Directory Id="CryptomatorDesktopProgramData" Name="Cryptomator">
<ns0:Component Id="AdminConfigDir" Guid="c078b7da-ba6e-4069-a5ab-5c0f0f9856a0">
<ns0:CreateFolder>
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Administrators" GenericAll="yes"/>
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
</ns0:CreateFolder>
</ns0:Component>
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Administrators" GenericAll="yes"/>
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
</ns0:File>
</ns0:Component>
</ns0:Directory>
</ns0:StandardDirectory>
<!-- Standard required root -->
<ns0:Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
@@ -105,7 +125,9 @@
<ns0:ComponentGroupRef Id="Files"/>
<ns0:ComponentGroupRef Id="FileAssociations"/>
<!-- Ref to additional ProgIDs -->
<ns0:ComponentRef Id="nonStartingProgID" />
<ns0:ComponentRef Id="nonStartingProgID"/>
<ns0:ComponentRef Id="AdminConfigDir"/>
<ns0:ComponentRef Id="AdminConfigFile"/>
</ns0:Feature>
<ns0:CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
@@ -131,6 +153,10 @@
<!-- Property for controlling update check behavior (can be set via command line) -->
<ns0:Property Id="DISABLEUPDATECHECK" Secure="yes" />
<!-- Disable user config -->
<ns0:SetProperty Id="DisableUserConfig" Value="&quot;[INSTALLDIR]disableUserConfig.bat&quot;" Sequence="execute" Before="DisableUserConfig" />
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- WebDAV patches -->
<ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot;" Sequence="execute" Before="PatchWebDAV" />
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
@@ -187,9 +213,10 @@
<ns0:Custom Action="FailOnRunningApp" After="Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Condition="FOUNDRUNNINGAPP" />
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
<!-- Skip action on uninstall -->
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
<ns0:Custom Action="PatchWebDAV" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
<!-- Configure update check setting if property is provided -->
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
</ns0:InstallExecuteSequence>

View File

@@ -1,6 +1,6 @@
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
Apache License v2.0|Apache License, Version 2.0|The Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|MIT|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1|GNU Lesser General Public License
GPLv2|GNU General Public License Version 2
GPLv2+CE|CDDL + GPLv2 with classpath exception
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0

53
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.18.0</version>
<version>1.19.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,30 +26,32 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>24</project.jdk.version>
<project.jdk.version>25</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.7.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.4.1</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.6.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version>
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<commons-lang3.version>3.18.0</commons-lang3.version>
<dagger.version>2.57.1</dagger.version>
<caffeine.version>3.2.3</caffeine.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<dagger.version>2.59.1</dagger.version>
<easybind.version>2.2</easybind.version>
<jackson.version>2.20.0</jackson.version>
<javafx.version>24.0.1</javafx.version>
<jackson.version>2.21.0</jackson.version>
<javafx.version>25</javafx.version>
<jwt.version>4.5.0</jwt.version>
<nimbus-jose.version>9.37.3</nimbus-jose.version>
<logback.version>1.5.18</logback.version>
<nimbus-jose.version>10.5</nimbus-jose.version>
<logback.version>1.5.31</logback.version>
<slf4j.version>2.0.17</slf4j.version>
<tinyoauth2.version>0.8.1</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
@@ -62,7 +64,7 @@
<!-- build-time dependencies -->
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
<dependency-check.version>12.1.5</dependency-check.version>
<jacoco.version>0.8.13</jacoco.version>
<jacoco.version>0.8.14</jacoco.version>
<license-generator.version>2.7.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
<mvn-compiler.version>3.14.1</mvn-compiler.version>
@@ -75,12 +77,26 @@
<surefire.jacoco.args></surefire.jacoco.args>
</properties>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>2.2.1</version>
<version>${cryptomator.cryptolib.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
@@ -214,7 +230,7 @@
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.2.2</version>
<version>${caffeine.version}</version>
</dependency>
<!-- JUnit / Mockito / Hamcrest -->
<dependency>
@@ -316,7 +332,6 @@
</annotationProcessorPaths>
<compilerArgs>
<arg>-Adagger.fastInit=enabled</arg>
<arg>-Adagger.formatGeneratedSource=enabled</arg>
</compilerArgs>
</configuration>
</plugin>

View File

@@ -1,5 +1,4 @@
import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
@@ -14,11 +13,14 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
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.uiappearance.UiAppearanceProvider;
import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
open module org.cryptomator.desktop {
@@ -50,17 +52,18 @@ open module org.cryptomator.desktop {
requires io.github.coffeelibs.tinyoauth2client;
requires org.slf4j;
requires org.apache.commons.lang3;
requires com.github.benmanes.caffeine;
/* dagger bs */
requires jakarta.inject;
requires static javax.inject;
requires java.compiler;
requires com.github.benmanes.caffeine;
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
uses SSLContextProvider;
uses org.cryptomator.event.NotificationHandler;
provides UiAppearanceProvider with JfxUiAppearanceProvider;
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;

View File

@@ -74,13 +74,6 @@ public abstract class CommonsModule {
return new MasterkeyFileAccess(Constants.PEPPER, csprng);
}
@Provides
@Singleton
@Named("SemVer")
static Comparator<String> providesSemVerComparator() {
return new SemVerComparator();
}
@Provides
@Singleton
static Optional<RevealPathService> provideRevealPathService() {

View File

@@ -13,5 +13,7 @@ public interface Constants {
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
byte[] PEPPER = new byte[0];
// Separator used to concatenate Hub username and device name in the filesystem owner identifier.
String HUB_USER_DEVICE_SEPARATOR = "&";
}

View File

@@ -124,6 +124,15 @@ public class Environment {
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
}
/**
* Returns the app version concatenated with the build number (if defined).
*
* @return version string formatted like {@code 1.2.3-4567} or {@code 1.2.3} if no build number is defined.
*/
public String getAppVersionWithBuildNumber() {
return getAppVersion() + getBuildNumber().map("-"::concat).orElse("");
}
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME);
}

View File

@@ -1,160 +0,0 @@
package org.cryptomator.common;
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.event.VaultEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
/**
* Map containing {@link VaultEvent}s.
* The map is keyed by the ciphertext path of the affected resource _and_ the {@link FilesystemEvent}s class in order to group same events
* <p>
* Use {@link EventMap#put(VaultEvent)} to add an element and {@link EventMap#remove(VaultEvent)} to remove it.
* <p>
* The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed.
*/
@Singleton
public class EventMap implements ObservableMap<EventMap.EventKey, VaultEvent> {
private static final int MAX_SIZE = 300;
public record EventKey(Path ciphertextPath, Class<? extends FilesystemEvent> c) {}
private final ObservableMap<EventMap.EventKey, VaultEvent> delegate;
@Inject
public EventMap() {
delegate = FXCollections.observableHashMap();
}
@Override
public void addListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
delegate.addListener(mapChangeListener);
}
@Override
public void removeListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
delegate.removeListener(mapChangeListener);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public VaultEvent get(Object key) {
return delegate.get(key);
}
@Override
public @Nullable VaultEvent put(EventKey key, VaultEvent value) {
return delegate.put(key, value);
}
@Override
public VaultEvent remove(Object key) {
return delegate.remove(key);
}
@Override
public void putAll(@NotNull Map<? extends EventKey, ? extends VaultEvent> m) {
delegate.putAll(m);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public @NotNull Set<EventKey> keySet() {
return delegate.keySet();
}
@Override
public @NotNull Collection<VaultEvent> values() {
return delegate.values();
}
@Override
public @NotNull Set<Entry<EventKey, VaultEvent>> entrySet() {
return delegate.entrySet();
}
@Override
public void addListener(InvalidationListener invalidationListener) {
delegate.addListener(invalidationListener);
}
@Override
public void removeListener(InvalidationListener invalidationListener) {
delegate.removeListener(invalidationListener);
}
public synchronized void put(VaultEvent e) {
//compute key
var key = computeKey(e.actualEvent());
//if-else
var nullOrEntry = delegate.get(key);
if (nullOrEntry == null) {
if (size() == MAX_SIZE) {
delegate.entrySet().stream() //
.min(Comparator.comparing(entry -> entry.getValue().actualEvent().getTimestamp())) //
.ifPresent(oldestEntry -> delegate.remove(oldestEntry.getKey()));
}
delegate.put(key, e);
} else {
delegate.put(key, nullOrEntry.incrementCount(e.actualEvent()));
}
}
public synchronized VaultEvent remove(VaultEvent similar) {
//compute key
var key = computeKey(similar.actualEvent());
return this.remove(key);
}
private EventKey computeKey(FilesystemEvent e) {
var p = switch (e) {
case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath;
case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext;
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
};
return new EventKey(p, e.getClass());
}
}

View File

@@ -0,0 +1,18 @@
package org.cryptomator.common;
import java.util.function.Supplier;
/**
* Interface marking a class to be used in {@link org.cryptomator.cryptofs.CryptoFileSystemProperties.Builder#withOwnerGetter(Supplier)}.
*/
@FunctionalInterface
public interface FilesystemOwnerSupplier {
/**
* Get the filesystem owner.
*
* @return the filesystem owner
*/
String getOwner();
}

View File

@@ -1,81 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2017 Sebastian Stenzel and others.
* All rights reserved.
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.common;
import org.apache.commons.lang3.StringUtils;
import java.util.Comparator;
/**
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
*/
public class SemVerComparator implements Comparator<String> {
private static final char VERSION_SEP = '.'; // http://semver.org/spec/v2.0.0.html#spec-item-2
private static final String PRE_RELEASE_SEP = "-"; // http://semver.org/spec/v2.0.0.html#spec-item-9
private static final String BUILD_SEP = "+"; // http://semver.org/spec/v2.0.0.html#spec-item-10
@Override
public int compare(String version1, String version2) {
// "Build metadata SHOULD be ignored when determining version precedence.
// Thus two versions that differ only in the build metadata, have the same precedence."
String v1WithoutBuildMetadata = StringUtils.substringBefore(version1, BUILD_SEP);
String v2WithoutBuildMetadata = StringUtils.substringBefore(version2, BUILD_SEP);
if (v1WithoutBuildMetadata.equals(v2WithoutBuildMetadata)) {
return 0;
}
String v1MajorMinorPatch = StringUtils.substringBefore(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2MajorMinorPatch = StringUtils.substringBefore(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
String v1PreReleaseVersion = StringUtils.substringAfter(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2PreReleaseVersion = StringUtils.substringAfter(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
return compare(v1MajorMinorPatch, v1PreReleaseVersion, v2MajorMinorPatch, v2PreReleaseVersion);
}
private int compare(String v1MajorMinorPatch, String v1PreReleaseVersion, String v2MajorMinorPatch, String v2PreReleaseVersion) {
int comparisonResult = compareNumericallyThenLexicographically(v1MajorMinorPatch, v2MajorMinorPatch);
if (comparisonResult == 0) {
if (v1PreReleaseVersion.isEmpty()) {
return 1; // 1.0.0 > 1.0.0-BETA
} else if (v2PreReleaseVersion.isEmpty()) {
return -1; // 1.0.0-BETA < 1.0.0
} else {
return compareNumericallyThenLexicographically(v1PreReleaseVersion, v2PreReleaseVersion);
}
} else {
return comparisonResult;
}
}
private int compareNumericallyThenLexicographically(String version1, String version2) {
final String[] vComps1 = StringUtils.split(version1, VERSION_SEP);
final String[] vComps2 = StringUtils.split(version2, VERSION_SEP);
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
int subversionComparisonResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
subversionComparisonResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
}
if (subversionComparisonResult != 0) {
return subversionComparisonResult;
}
}
// all in common so far? longest version string is considered the higher version:
return vComps1.length - vComps2.length;
}
}

View File

@@ -167,6 +167,7 @@ public class Mounter {
usedMountServices.add(mountService);
var builder = mountService.forFileSystem(cryptoFsRoot);
LOG.debug("Using mount service {} for mounting vault {}", mountService.getClass().getName(), vaultSettings.displayName);
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
var cleanup = internal.prepare();
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);

View File

@@ -0,0 +1,53 @@
package org.cryptomator.common.recovery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.util.stream.Stream;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
public final class BackupRestorer {
private static final Logger LOG = LoggerFactory.getLogger(BackupRestorer.class);
private BackupRestorer() {}
public static void restoreIfBackupPresent(Path vaultPath, String filePrefix) {
Path targetFile = vaultPath.resolve(filePrefix);
try (Stream<Path> files = Files.list(vaultPath)) {
files.filter(file -> isFileMatchingPattern(file.getFileName().toString(), filePrefix))
.max((f1, f2) -> {
try {
FileTime time1 = Files.getLastModifiedTime(f1);
FileTime time2 = Files.getLastModifiedTime(f2);
return time1.compareTo(time2);
} catch (IOException e) {
return 0;
}
})
.ifPresent(backupFile -> copyBackupFile(backupFile, targetFile));
} catch (IOException e) {
LOG.info("Unable to restore backup files in '{}'", vaultPath, e);
}
}
private static boolean isFileMatchingPattern(String fileName, String filePrefix) {
return fileName.startsWith(filePrefix) && fileName.endsWith(MASTERKEY_BACKUP_SUFFIX);
}
private static void copyBackupFile(Path backupFile, Path configPath) {
try {
Files.copy(backupFile, configPath, StandardCopyOption.REPLACE_EXISTING);
LOG.debug("Backup restored - file: '{}' path: '{}'", backupFile, configPath);
} catch (IOException e) {
LOG.warn("Unable to copy backup file from '{}' to '{}'", backupFile, configPath, e);
}
}
}

View File

@@ -0,0 +1,33 @@
package org.cryptomator.common.recovery;
import java.io.IOException;
import java.nio.file.Path;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
public final class CryptoFsInitializer {
private CryptoFsInitializer() {}
public static void init(Path recoveryPath,
Masterkey masterkey,
int shorteningThreshold,
CryptorProvider.Scheme scheme) throws IOException, CryptoException {
MasterkeyLoader loader = ignored -> masterkey.copy();
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties //
.cryptoFileSystemProperties() //
.withCipherCombo(scheme) //
.withKeyLoader(loader) //
.withShorteningThreshold(shorteningThreshold) //
.build();
CryptoFileSystemProvider.initialize(recoveryPath, fsProps, DEFAULT_KEY_ID);
}
}

View File

@@ -0,0 +1,102 @@
package org.cryptomator.common.recovery;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME;
public final class MasterkeyService {
private static final Logger LOG = LoggerFactory.getLogger(MasterkeyService.class);
private MasterkeyService() {}
public static void recoverFromRecoveryKey(String recoveryKey, RecoveryKeyFactory recoveryKeyFactory, Path recoveryPath, CharSequence newPassword) throws IOException {
recoveryKeyFactory.newMasterkeyFileWithPassphrase(recoveryPath, recoveryKey, newPassword);
}
public static Masterkey load(MasterkeyFileAccess masterkeyFileAccess, Path masterkeyFilePath, CharSequence password) throws IOException {
return masterkeyFileAccess.load(masterkeyFilePath, password);
}
public static CryptorProvider.Scheme validateRecoveryKeyAndDetectCombo(RecoveryKeyFactory recoveryKeyFactory, //
Vault vault, String recoveryKey, //
MasterkeyFileAccess masterkeyFileAccess) throws IOException, CryptoException, NoSuchElementException {
String tmpPass = UUID.randomUUID().toString();
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
Path tempRecoveryPath = recoveryDirectory.getRecoveryPath();
recoverFromRecoveryKey(recoveryKey, recoveryKeyFactory, tempRecoveryPath, tmpPass);
Path masterkeyFilePath = tempRecoveryPath.resolve(MASTERKEY_FILENAME);
try (Masterkey mk = load(masterkeyFileAccess, masterkeyFilePath, tmpPass)) {
return detect(mk, vault.getPath()).orElseThrow();
}
}
}
public static Optional<CryptorProvider.Scheme> detect(Masterkey masterkey, Path vaultPath) {
try (Stream<Path> paths = Files.walk(vaultPath.resolve(DATA_DIR_NAME))) {
Optional<Path> c9rFile = paths //
.filter(p -> p.toString().endsWith(".c9r")) //
.filter(p -> !p.endsWith("dir.c9r")) //
.filter(Files::isRegularFile) //
.findFirst();
if (c9rFile.isEmpty()) {
LOG.info("Unable to detect Crypto scheme: No *.c9r file found in {}", vaultPath);
return Optional.empty();
}
return determineScheme(c9rFile.get(), masterkey);
} catch (IOException e) {
LOG.info("Unable to detect Crypto scheme: Failed to inspect vault", e);
return Optional.empty();
}
}
private static Optional<CryptorProvider.Scheme> determineScheme(Path c9rFile, Masterkey masterkey) {
return Arrays.stream(CryptorProvider.Scheme.values()).filter(scheme -> {
try (Cryptor cryptor = CryptorProvider.forScheme(scheme).provide(masterkey.copy(), SecureRandom.getInstanceStrong())) {
int headerSize = cryptor.fileHeaderCryptor().headerSize();
ByteBuffer headerBuf = ByteBuffer.allocate(headerSize);
try (FileChannel channel = FileChannel.open(c9rFile, StandardOpenOption.READ)) {
channel.read(headerBuf, 0);
}
headerBuf.flip();
cryptor.fileHeaderCryptor().decryptHeader(headerBuf.duplicate());
LOG.debug("Detected Crypto scheme: {}", scheme);
return true;
} catch (IllegalArgumentException | CryptoException e) {
LOG.debug("Could not decrypt with scheme: {}", scheme);
return false;
} catch (IOException | NoSuchAlgorithmException e) {
LOG.warn("Unable to detect Crypto scheme: Failed to decrypt .c9r file", e);
return false;
}
}).findFirst();
}
}

View File

@@ -0,0 +1,10 @@
package org.cryptomator.common.recovery;
public enum RecoveryActionType {
RESTORE_ALL,
RESTORE_MASTERKEY,
RESTORE_VAULT_CONFIG,
RESET_PASSWORD,
SHOW_KEY,
CONVERT_VAULT
}

View File

@@ -0,0 +1,56 @@
package org.cryptomator.common.recovery;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Comparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class RecoveryDirectory implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(RecoveryDirectory.class);
private final Path recoveryPath;
private final Path vaultPath;
private RecoveryDirectory(Path vaultPath, Path recoveryPath) {
this.vaultPath = vaultPath;
this.recoveryPath = recoveryPath;
}
public static RecoveryDirectory create(Path vaultPath) throws IOException {
Path tempDir = Files.createTempDirectory("cryptomator");
return new RecoveryDirectory(vaultPath, tempDir);
}
public void moveRecoveredFile(String file) throws IOException {
Files.move(recoveryPath.resolve(file), vaultPath.resolve(file), StandardCopyOption.REPLACE_EXISTING);
}
private void deleteRecoveryDirectory() {
try (var paths = Files.walk(recoveryPath)) {
paths.sorted(Comparator.reverseOrder()).forEach(p -> {
try {
Files.delete(p);
} catch (IOException e) {
LOG.info("Unable to delete {}. Please delete it manually.", p);
}
});
} catch (IOException e) {
LOG.error("Failed to clean up recovery directory", e);
}
}
@Override
public void close() {
deleteRecoveryDirectory();
}
public Path getRecoveryPath() {
return recoveryPath;
}
}

View File

@@ -0,0 +1,54 @@
package org.cryptomator.common.recovery;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultConfigCache;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.integrations.mount.MountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.ResourceBundle;
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
public final class VaultPreparator {
private static final Logger LOG = LoggerFactory.getLogger(VaultPreparator.class);
private VaultPreparator() {}
public static Vault prepareVault(Path selectedDirectory, //
VaultComponent.Factory vaultComponentFactory, //
List<MountService> mountServices, //
ResourceBundle resourceBundle) {
VaultSettings vaultSettings = VaultSettings.withRandomId();
vaultSettings.path.set(selectedDirectory);
if (selectedDirectory.getFileName() != null) {
vaultSettings.displayName.set(selectedDirectory.getFileName().toString());
} else {
vaultSettings.displayName.set(resourceBundle.getString("defaults.vault.vaultName"));
}
var wrapper = new VaultConfigCache(vaultSettings);
Vault vault = vaultComponentFactory.create(vaultSettings, wrapper, LOCKED, null).vault();
try {
VaultListManager.determineVaultState(vault.getPath());
} catch (IOException e) {
LOG.warn("Failed to determine vault state for {}", vaultSettings.path.get(), e);
}
//due to https://github.com/cryptomator/cryptomator/issues/2880#issuecomment-1680313498
var nameOfWinfspLocalMounter = "org.cryptomator.frontend.fuse.mount.WinFspMountProvider";
if (SystemUtils.IS_OS_WINDOWS && vaultSettings.path.get().toString().contains("Dropbox") && mountServices.stream().anyMatch(s -> s.getClass().getName().equals(nameOfWinfspLocalMounter))) {
vaultSettings.mountService.setValue(nameOfWinfspLocalMounter);
}
return vault;
}
}

View File

@@ -25,10 +25,8 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
import java.nio.file.Path;
import java.time.Instant;
import java.util.function.Consumer;
public class Settings {
@@ -53,6 +51,7 @@ public class Settings {
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
private final SettingsProvider provider;
public final ObservableList<VaultSettings> directories;
public final BooleanProperty startHidden;
public final BooleanProperty autoCloseVaults;
@@ -78,13 +77,12 @@ public class Settings {
public final ObjectProperty<Instant> lastUpdateCheckReminder;
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
public final StringProperty lastUpdateAttemptedByVersion;
private Consumer<Settings> saveCmd;
public static Settings create(Environment env) {
public static Settings create(SettingsProvider provider, Environment env) {
var defaults = new SettingsJson();
defaults.showTrayIcon = env.showTrayIcon();
return new Settings(defaults);
return new Settings(provider, defaults);
}
/**
@@ -92,7 +90,8 @@ public class Settings {
*
* @param json The parsed settings.json
*/
Settings(SettingsJson json) {
Settings(SettingsProvider provider, SettingsJson json) {
this.provider = provider;
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
@@ -118,6 +117,7 @@ public class Settings {
this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck);
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -148,15 +148,11 @@ public class Settings {
lastUpdateCheckReminder.addListener(this::somethingChanged);
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
private void migrateLegacySettings(SettingsJson json) {
// migrate renamed keychainAccess
if(this.keychainProvider.getValueSafe().equals("org.cryptomator.linux.keychain.SecretServiceKeychainAccess")) {
this.keychainProvider.setValue("org.cryptomator.linux.keychain.GnomeKeyringKeychainAccess");
}
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
this.mountService.set(switch (json.preferredVolumeImpl) {
@@ -210,6 +206,7 @@ public class Settings {
json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get();
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
return json;
}
@@ -222,20 +219,12 @@ public class Settings {
}
}
// TODO rename to setChangeListener
void setSaveCmd(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
this.save();
provider.scheduleSave(this);
}
void save() {
if (saveCmd != null) {
saveCmd.accept(this);
}
public void saveNow() {
provider.saveNow(this);
}
}

View File

@@ -96,4 +96,7 @@ class SettingsJson {
@JsonProperty("previouslyUsedVaultDirectory")
Path previouslyUsedVaultDirectory;
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
}

View File

@@ -26,7 +26,9 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -61,8 +63,7 @@ public class SettingsProvider implements Supplier<Settings> {
Settings settings = env.getSettingsPath() //
.flatMap(this::tryLoad) //
.findFirst() //
.orElseGet(() -> Settings.create(env));
settings.setSaveCmd(this::scheduleSave);
.orElseGet(() -> Settings.create(this, env));
return settings;
}
@@ -71,7 +72,7 @@ public class SettingsProvider implements Supplier<Settings> {
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
var json = JSON.reader().readValue(in, SettingsJson.class);
LOG.info("Settings loaded from {}", path);
var settings = new Settings(json);
var settings = new Settings(this, json);
return Stream.of(settings);
} catch (JacksonException e) {
LOG.warn("Failed to parse json file {}", path, e);
@@ -84,19 +85,33 @@ public class SettingsProvider implements Supplier<Settings> {
}
}
private void scheduleSave(Settings settings) {
if (settings == null) {
return;
void saveNow(Settings settings) {
try {
scheduleSave(settings, 0L).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Saving settings was interrupted.", e);
} catch (ExecutionException e) {
LOG.error("Unexpected exception while saving.", e);
}
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
settingsPath.ifPresent(path -> {
Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
if (previouslyScheduledTask != null) {
previouslyScheduledTask.cancel(false);
}
});
}
void scheduleSave(Settings settings) {
scheduleSave(settings, SAVE_DELAY_MS);
}
private Future<?> scheduleSave(Settings settings, long delayMillis) {
if (settings == null) {
return CompletableFuture.completedFuture(null);
}
final Path settingsPath = env.getSettingsPath().findFirst().orElseThrow(); // always save to preferred (first) path
Runnable saveCommand = () -> this.save(settings, settingsPath);
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, delayMillis, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
if (previouslyScheduledTask != null) {
previouslyScheduledTask.cancel(false);
}
return scheduledTask;
}
private void save(Settings settings, Path settingsPath) {
@@ -107,7 +122,7 @@ public class SettingsProvider implements Supplier<Settings> {
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
var jsonObj = settings.serialized();
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
jsonObj.writtenByVersion = env.getAppVersionWithBuildNumber();
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
}
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);

View File

@@ -10,14 +10,6 @@ public enum UiTheme {
DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic");
public static UiTheme[] applicableValues() {
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
return values();
} else {
return new UiTheme[]{LIGHT, DARK};
}
}
private final String displayName;
UiTheme(String displayName) {

View File

@@ -10,7 +10,7 @@ package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.common.FilesystemOwnerSupplier;
import org.cryptomator.common.mount.Mounter;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
@@ -23,7 +23,8 @@ import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.event.VaultEvent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.event.NotificationManager;
import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException;
@@ -35,7 +36,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
@@ -75,10 +75,12 @@ public class Vault {
private final BooleanBinding missing;
private final BooleanBinding needsMigration;
private final BooleanBinding unknownError;
private final BooleanBinding missingVaultConfig;
private final ObjectBinding<Mountpoint> mountPoint;
private final Mounter mounter;
private final Settings settings;
private final FileSystemEventAggregator fileSystemEventAggregator;
private final NotificationManager notificationManager;
private final BooleanProperty showingStats;
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@@ -91,7 +93,8 @@ public class Vault {
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
VaultStats stats, //
Mounter mounter, Settings settings, //
FileSystemEventAggregator fileSystemEventAggregator) {
FileSystemEventAggregator fileSystemEventAggregator, //
NotificationManager notificationManager) {
this.vaultSettings = vaultSettings;
this.configCache = configCache;
this.cryptoFileSystem = cryptoFileSystem;
@@ -103,12 +106,14 @@ public class Vault {
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
this.missingVaultConfig = Bindings.createBooleanBinding(this::isMissingVaultConfig, state);
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
this.mounter = mounter;
this.settings = settings;
this.fileSystemEventAggregator = fileSystemEventAggregator;
this.notificationManager = notificationManager;
this.showingStats = new SimpleBooleanProperty(false);
this.quickAccessEntry = new AtomicReference<>(null);
}
@@ -145,14 +150,17 @@ public class Vault {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
}
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.withFilesystemEventConsumer(this::consumeVaultEvent) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
.withFilesystemEventConsumer(this::consumeVaultEvent);
if (keyLoader instanceof FilesystemOwnerSupplier oo) {
fsPropsBuilder.withOwnerGetter(oo::getOwner);
}
return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build());
}
private void destroyCryptoFileSystem() {
@@ -262,6 +270,7 @@ public class Vault {
private void consumeVaultEvent(FilesystemEvent e) {
fileSystemEventAggregator.put(this, e);
notificationManager.offer(this, e);
}
// ******************************************************************************
@@ -336,6 +345,14 @@ public class Vault {
return state.get() == VaultState.Value.ERROR;
}
public BooleanBinding missingVaultConfigProperty() {
return missingVaultConfig;
}
public boolean isMissingVaultConfig() {
return state.get() == VaultState.Value.VAULT_CONFIG_MISSING || state.get() == VaultState.Value.ALL_MISSING;
}
public ReadOnlyStringProperty displayNameProperty() {
return vaultSettings.displayName;
}

View File

@@ -20,7 +20,7 @@ public class VaultConfigCache {
private final VaultSettings settings;
private final AtomicReference<VaultConfig.UnverifiedVaultConfig> config;
VaultConfigCache(VaultSettings settings) {
public VaultConfigCache(VaultSettings settings) {
this.settings = settings;
this.config = new AtomicReference<>(null);
}

View File

@@ -9,6 +9,7 @@
package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.recovery.BackupRestorer;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
@@ -34,9 +35,7 @@ import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@Singleton
public class VaultListManager {
@@ -67,6 +66,12 @@ public class VaultListManager {
autoLocker.init();
}
public boolean isAlreadyAdded(Path vaultPath) {
assert vaultPath.isAbsolute();
assert vaultPath.normalize().equals(vaultPath);
return vaultList.stream().anyMatch(v -> vaultPath.equals(v.getPath()));
}
public Vault add(Path pathToVault) throws IOException {
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
@@ -114,59 +119,122 @@ public class VaultListManager {
.findAny();
}
public void addVault(Vault vault) {
Path path = vault.getPath().normalize().toAbsolutePath();
if (!isAlreadyAdded(path)) {
vaultList.add(vault);
}
}
private Vault create(VaultSettings vaultSettings) {
var wrapper = new VaultConfigCache(vaultSettings);
try {
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
wrapper.reloadConfig();
if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) {
var keyIdScheme = wrapper.get().getKeyId().getScheme();
vaultSettings.lastKnownKeyLoader.set(keyIdScheme);
}
} else if (vaultState == NEEDS_MIGRATION) {
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
}
initializeLastKnownKeyLoaderIfPossible(vaultSettings, vaultState, wrapper);
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e);
LOG.warn("Failed to determine vault state for {}", vaultSettings.path.get(), e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
}
}
private void initializeLastKnownKeyLoaderIfPossible(VaultSettings vaultSettings, VaultState.Value vaultState, VaultConfigCache wrapper) throws IOException {
if (vaultSettings.lastKnownKeyLoader.get() != null) {
return;
}
switch (vaultState) {
case LOCKED -> {
wrapper.reloadConfig();
vaultSettings.lastKnownKeyLoader.set(wrapper.get().getKeyId().getScheme());
}
case NEEDS_MIGRATION -> {
//for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
}
case VAULT_CONFIG_MISSING -> {
//Nothing to do here, since there is no config to read
}
case MISSING, ALL_MISSING, ERROR, PROCESSING -> {
// no config available or not safe to load
}
default -> {
if (Files.exists(vaultSettings.path.get().resolve(VAULTCONFIG_FILENAME))) {
try {
wrapper.reloadConfig();
vaultSettings.lastKnownKeyLoader.set(wrapper.get().getKeyId().getScheme());
} catch (IOException e) {
LOG.debug("Unable to load config for {}", vaultSettings.path.get(), e);
}
}
}
}
}
public static VaultState.Value redetermineVaultState(Vault vault) {
VaultState state = vault.stateProperty();
VaultState.Value previousState = state.getValue();
return switch (previousState) {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
try {
var determinedState = determineVaultState(vault.getPath());
if (determinedState == LOCKED) {
vault.getVaultConfigCache().reloadConfig();
}
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
state.set(ERROR);
vault.setLastKnownException(e);
yield ERROR;
}
VaultState.Value previous = state.getValue();
if (previous.equals(UNLOCKED) || previous.equals(PROCESSING)) {
return previous;
}
try {
VaultState.Value determined = determineVaultState(vault.getPath());
if (determined == LOCKED) {
vault.getVaultConfigCache().reloadConfig();
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
};
state.set(determined);
return determined;
} catch (IOException e) {
LOG.warn("Failed to (re)determine vault state for {}", vault.getPath(), e);
vault.setLastKnownException(e);
state.set(ERROR);
return ERROR;
}
}
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
public static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
if (!Files.exists(pathToVault)) {
return VaultState.Value.MISSING;
return MISSING;
}
VaultState.Value structureResult = checkDirStructure(pathToVault);
if (structureResult == LOCKED || structureResult == NEEDS_MIGRATION) {
return structureResult;
}
Path pathToVaultConfig = pathToVault.resolve(VAULTCONFIG_FILENAME);
Path pathToMasterkey = pathToVault.resolve(MASTERKEY_FILENAME);
if (!Files.exists(pathToVaultConfig)) {
BackupRestorer.restoreIfBackupPresent(pathToVault, VAULTCONFIG_FILENAME);
}
if (!Files.exists(pathToMasterkey)) {
BackupRestorer.restoreIfBackupPresent(pathToVault, MASTERKEY_FILENAME);
}
boolean hasConfig = Files.exists(pathToVaultConfig);
if (!hasConfig && !Files.exists(pathToMasterkey)) {
return ALL_MISSING;
}
if (!hasConfig) {
return VAULT_CONFIG_MISSING;
}
return checkDirStructure(pathToVault);
}
private static VaultState.Value checkDirStructure(Path pathToVault) throws IOException {
return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
case VAULT -> VaultState.Value.LOCKED;
case UNRELATED -> VaultState.Value.MISSING;
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? //
VaultState.Value.NEEDS_MIGRATION //
: VaultState.Value.MISSING;
case VAULT -> LOCKED;
case UNRELATED -> MISSING;
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? NEEDS_MIGRATION : MISSING;
};
}

View File

@@ -25,6 +25,16 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
*/
MISSING,
/**
* No vault config found at the provided path
*/
VAULT_CONFIG_MISSING,
/**
* No vault config and masterkey found at the provided path
*/
ALL_MISSING,
/**
* Vault requires migration to a newer vault format
*/

View File

@@ -6,6 +6,7 @@ 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.FileIsInUseEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import javax.inject.Inject;
@@ -101,6 +102,7 @@ public class FileSystemEventAggregator {
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext;
};
return new FSEventBucket(v, p, event.getClass());
}

View File

@@ -0,0 +1,85 @@
package org.cryptomator.event;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Manager for notifications.
* <p>
* To add (filesystem) events, use method {@link #offer(Vault, FilesystemEvent)}. If the input event is eligible, it is added to an internal queue.
* An event is eligible, if
* <ul>
* <li>the event should trigger a notification and</li>
* <li>it is not added within the last {@value DEBOUNCE_THRESHOLD_SECONDS} seconds</li>
* </ul>
*
* @see org.cryptomator.ui.fxapp.FxNotificationManager
*/
@Singleton
public class NotificationManager {
private static final int DEBOUNCE_THRESHOLD_SECONDS = 5;
private final Cache<FSEventBucket, FilesystemEvent> debounceCache;
private final ConcurrentLinkedQueue<VaultEvent> pendingEvents;
@Inject
public NotificationManager() {
debounceCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(DEBOUNCE_THRESHOLD_SECONDS)).build();
pendingEvents = new ConcurrentLinkedQueue<>();
}
/**
* Offers the given filesystem event to the notification manager.
*
* @param v The vault where the filesystem event happened
* @param e the actual filesystem event
* @return {@code true} if the filesystem event is accepted, otherwise {@code false}.
*/
public boolean offer(Vault v, FilesystemEvent e) {
return switch (e) {
case FileIsInUseEvent fiiue -> addEvent(v, fiiue.ciphertextPath(), fiiue);
default -> false;
};
}
boolean addEvent(Vault v, Path keyPath, FilesystemEvent e) {
var key = new FSEventBucket(v, keyPath, e.getClass());
var isAdded = new AtomicBoolean(false);
debounceCache.asMap().computeIfAbsent(key, _ -> {
synchronized (this) {
pendingEvents.add(new VaultEvent(v, e));
isAdded.set(true);
}
return e;
});
return isAdded.get();
}
/**
* Adds all events to the target list and clears afterward the pending-event-queue
*
* @param target list where the filesystem events are copied to
* @return {@code true}, if elements were copied
*/
public boolean appendToAndClear(List<VaultEvent> target) {
//it is not clear, if addAll iterates thread-safe over the pendingEvents
//hence we synchronize moving (copy then clear) and adding-single-element operations
synchronized (this) {
var result = target.addAll(pendingEvents);
pendingEvents.clear();
return result;
}
}
}

View File

@@ -3,25 +3,6 @@ package org.cryptomator.event;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.event.FilesystemEvent;
import java.time.Instant;
public record VaultEvent(Vault v, FilesystemEvent actualEvent) {
public record VaultEvent(Vault v, FilesystemEvent actualEvent, int count) implements Comparable<VaultEvent> {
public VaultEvent(Vault v, FilesystemEvent actualEvent) {
this(v, actualEvent, 1);
}
@Override
public int compareTo(VaultEvent other) {
var timeResult = actualEvent.getTimestamp().compareTo(other.actualEvent().getTimestamp());
if(timeResult != 0) {
return timeResult;
} else {
return this.equals(other) ? 0 : this.actualEvent.getClass().getName().compareTo(other.actualEvent.getClass().getName());
}
}
public VaultEvent incrementCount(FilesystemEvent update) {
return new VaultEvent(v, update, count+1);
}
}

View File

@@ -0,0 +1,97 @@
package org.cryptomator.launcher;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.Reader;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Properties;
import java.util.Set;
/**
* Factory to generate admin properties.
*
* <p>
* Admin properties are {@link Properties} using system properties as defaults, but allow overwriting a specific set of properties with an external config file.
* Those properties are created by calling {@link #create()}. The method first reads system property {@value #ADMIN_PROP_FILE_KEY}. If it contains a path to a valid properties file, all overridable properties from the file are loaded into the returned admin properties.
* <p>
* The overridable properties are:
* <ul>
* <li>cryptomator.logDir</li>
* <li>cryptomator.pluginDir</li>
* <li>cryptomator.p12Path</li>
* <li>cryptomator.mountPointsDir</li>
* <li>cryptomator.disableUpdateCheck</li>
* </ul>
*
* @see Properties
* @see System#getProperties()
*/
class AdminPropertiesFactory {
private static final Logger LOG = EventualLogger.INSTANCE;
private static final long MAX_CONFIG_SIZE_BYTES = 8192;
private static final String ADMIN_PROP_FILE_KEY = "cryptomator.adminConfigPath";
private static final Set<String> ALLOWED_OVERRIDES = Set.of( //
"cryptomator.logDir", //
"cryptomator.pluginDir", //
"cryptomator.p12Path", //
"cryptomator.mountPointsDir", //
"cryptomator.disableUpdateCheck");
/**
* Creates new {@link Properties} containing overridable properties from the admin config.
* <p>
* The returned properties object uses as default the {@link System} properties.
* For a list of overridable properties, see {@link AdminPropertiesFactory}
*
* @return {@link Properties} containing overridable properties from the admin config and defaulting to system properties.
*/
static Properties create() {
var systemProps = System.getProperties();
var adminProps = new Properties(systemProps);
final String adminCfgPath = System.getProperty(ADMIN_PROP_FILE_KEY);
if (adminCfgPath == null) {
LOG.debug("Admin config property is not defined. Skipping.");
return adminProps;
}
var propsFromFile = loadPropertiesFromFile(Path.of(adminCfgPath));
for (var key : propsFromFile.stringPropertyNames()) {
if (ALLOWED_OVERRIDES.contains(key)) {
var value = propsFromFile.getProperty(key);
LOG.info("Overwriting {} with value {} from admin config.", key, value);
adminProps.setProperty(key, value);
} else {
LOG.debug("Property {} in admin config is not supported for override.", key);
}
}
return adminProps;
}
//visible for testing
static Properties loadPropertiesFromFile(Path adminPropertiesPath) {
var adminProps = new Properties();
try (FileChannel ch = FileChannel.open(adminPropertiesPath, StandardOpenOption.READ); //
Reader reader = Channels.newReader(ch, StandardCharsets.UTF_8)) {
if (ch.size() > MAX_CONFIG_SIZE_BYTES) {
throw new IOException("Config file %s exceeds maximum size of %d".formatted(adminPropertiesPath, MAX_CONFIG_SIZE_BYTES));
}
adminProps.load(reader);
} catch (NoSuchFileException _) {
//NO-OP
LOG.debug("No admin properties found at {}.", adminPropertiesPath);
} catch (IOException | IllegalArgumentException e) {
LOG.warn("Failed to read administrative properties from {}. Returning empty properties.", adminPropertiesPath, e);
}
return adminProps;
}
}

View File

@@ -11,9 +11,9 @@ import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.SubstitutingProperties;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +35,8 @@ public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis();
static {
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
var adminProps = AdminPropertiesFactory.create();
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
System.setProperties(lazyProcessedProps);
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
LOG = LoggerFactory.getLogger(Cryptomator.class);
@@ -64,6 +65,7 @@ public class Cryptomator {
}
public static void main(String[] args) {
EventualLogger.INSTANCE.drainTo(LOG);
var printVersion = Optional.ofNullable(args) //
.stream() //Streams either one element (the args-array) or zero elements
.flatMap(Arrays::stream) //

View File

@@ -4,7 +4,6 @@ import dagger.Module;
import dagger.Provides;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
@@ -30,11 +29,6 @@ class CryptomatorModule {
return new ArrayBlockingQueue<>(10);
}
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
return UiAppearanceProvider.get();
}
@Provides
@Singleton

View File

@@ -0,0 +1,106 @@
package org.cryptomator.launcher;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.event.DefaultLoggingEvent;
import org.slf4j.event.Level;
import org.slf4j.event.LoggingEvent;
import org.slf4j.helpers.AbstractLogger;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
class EventualLogger extends AbstractLogger {
static final EventualLogger INSTANCE = new EventualLogger();
private final Queue<LoggingEvent> bufferedEvents = new ArrayDeque<>();
private EventualLogger() {
}
synchronized void drainTo(Logger gutter) {
for (var event : bufferedEvents) {
var builder = gutter.atLevel(event.getLevel()) //
.setCause(event.getThrowable()) //
.setMessage(event.getMessage());
Objects.requireNonNullElse(event.getArguments(), List.of()).forEach(builder::addArgument);
Objects.requireNonNullElse(event.getMarkers(), List.<Marker>of()).forEach(builder::addMarker);
builder.log();
}
bufferedEvents.clear();
}
@Override
protected synchronized void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
var event = new DefaultLoggingEvent(level, this);
if (marker != null) {
event.addMarker(marker);
}
event.setMessage(messagePattern);
for (var arg : Objects.requireNonNullElse(arguments, new Object[]{})) {
event.addArgument(arg);
}
event.setThrowable(throwable);
bufferedEvents.add(event);
}
//Unclear, unused and undocumented method of slf4j, see also https://github.com/qos-ch/slf4j/discussions/348
@Override
protected String getFullyQualifiedCallerName() {
return getClass().getCanonicalName();
}
@Override
public boolean isTraceEnabled() {
return true;
}
@Override
public boolean isTraceEnabled(Marker marker) {
return true;
}
@Override
public boolean isDebugEnabled() {
return true;
}
@Override
public boolean isDebugEnabled(Marker marker) {
return true;
}
@Override
public boolean isInfoEnabled() {
return true;
}
@Override
public boolean isInfoEnabled(Marker marker) {
return true;
}
@Override
public boolean isWarnEnabled() {
return true;
}
@Override
public boolean isWarnEnabled(Marker marker) {
return true;
}
@Override
public boolean isErrorEnabled() {
return true;
}
@Override
public boolean isErrorEnabled(Marker marker) {
return true;
}
}

View File

@@ -59,7 +59,7 @@ public class ChooseExistingVaultController implements FxController {
this.vault = vault;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.screenshot = applicationStyle.appliedThemeProperty().map(this::selectScreenshot);
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
}
private Image selectScreenshot(Theme theme) {

View File

@@ -20,7 +20,6 @@ import java.util.ResourceBundle;
public class PasswordStrengthUtil {
private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length
private static final String RESSOURCE_PREFIX = "passwordStrength.messageLabel.";
private static final List<String> SANITIZED_INPUTS = List.of("cryptomator");
private final ResourceBundle resourceBundle;
@@ -48,13 +47,15 @@ public class PasswordStrengthUtil {
}
public String getStrengthDescription(Number score) {
if (score.intValue() == -1) {
return String.format(resourceBundle.getString(RESSOURCE_PREFIX + "tooShort"), minPwLength);
} else if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
return resourceBundle.getString(RESSOURCE_PREFIX + score.intValue());
} else {
return "";
}
return switch (score.intValue()) {
case -1 -> String.format(resourceBundle.getString("passwordStrength.messageLabel.tooShort"), minPwLength);
case 0 -> resourceBundle.getString("passwordStrength.messageLabel.0");
case 1 -> resourceBundle.getString("passwordStrength.messageLabel.1");
case 2 -> resourceBundle.getString("passwordStrength.messageLabel.2");
case 3 -> resourceBundle.getString("passwordStrength.messageLabel.3");
case 4 -> resourceBundle.getString("passwordStrength.messageLabel.4");
default -> "";
};
}
}

View File

@@ -38,13 +38,15 @@ public enum FxmlFile {
MIGRATION_RUN("/fxml/migration_run.fxml"), //
MIGRATION_START("/fxml/migration_start.fxml"), //
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
NOTIFICATION("/fxml/notification.fxml"), //
PREFERENCES("/fxml/preferences.fxml"), //
QUIT("/fxml/quit.fxml"), //
QUIT_FORCED("/fxml/quit_forced.fxml"), //
RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
RECOVERYKEY_EXPERT_SETTINGS("/fxml/recoverykey_expert_settings.fxml"), //
RECOVERYKEY_ONBOARDING("/fxml/recoverykey_onboarding.fxml"), //
RECOVERYKEY_RECOVER("/fxml/recoverykey_recover.fxml"), //
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
SHARE_VAULT("/fxml/share_vault.fxml"), //
SIMPLE_DIALOG("/fxml/simple_dialog.fxml"), //
@@ -58,6 +60,7 @@ public enum FxmlFile {
VAULT_STATISTICS("/fxml/stats.fxml"), //
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
private final String ressourcePathString;
FxmlFile(String ressourcePathString) {

View File

@@ -0,0 +1,56 @@
package org.cryptomator.ui.common;
import javafx.stage.Screen;
/**
* Utility class providing methods regarding the OS bar.
*/
public class SystemBarUtil {
public enum Placement {
/**
* OS Bar placed at the left screen edge
*/
LEFT,
/**
* OS Bar placed at the top screen edge
*/
TOP,
/**
* OS Bar placed at the right screen edge
*/
RIGHT,
/**
* OS Bar placed at the bottom screen edge
*/
BOTTOM;
}
/**
* Determines the placement of the OS bar on the given screen.
* <p>
* <b>Assuming the OS bar fills one screen edge completely</b>,
* this method determines that screen edge by comparing the actual screen bounds with the visual ones.
* <p>
* If the screen does not have a system bar, the bottom placement is returned.
* If the screen does have multiple system bars, the first in following priority is returned:
* LEFT, TOP, RIGHT, BOTTOM.
*
* @param screen a {@link Screen} where an OS bar exists
* @return {@link Placement} indicating the screen edge.
*/
public static Placement getPlacementOfSystembar(Screen screen) {
var bounds = screen.getBounds();
var vBounds = screen.getVisualBounds();
//assumption: the system bar fills a whole screen side
if (bounds.getMinX() != vBounds.getMinX()) {
return Placement.LEFT;
} else if (bounds.getMinY() != vBounds.getMinY()) {
return Placement.TOP;
} else if (bounds.getMaxX() != vBounds.getMaxX()) {
return Placement.RIGHT;
} else {
return Placement.BOTTOM;
}
}
}

View File

@@ -12,6 +12,8 @@ public enum FontAwesome5Icon {
CARET_DOWN("\uF0D7"), //
CARET_RIGHT("\uF0Da"), //
CHECK("\uF00C"), //
CHEVRON_LEFT("\uF053"), //
CHEVRON_RIGHT("\uF054"), //
CLOCK("\uF017"), //
CLIPBOARD("\uF328"), //
COG("\uF013"), //
@@ -60,6 +62,7 @@ public enum FontAwesome5Icon {
TRASH("\uF1F8"), //
UNLINK("\uf127"), //
USER_COG("\uf4fe"), //
USER_LOCK("\uf502"), //
WRENCH("\uF0AD"), //
WINDOW_MINIMIZE("\uF2D1"), //
;

View File

@@ -4,24 +4,27 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.AccessibleRole;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import java.util.ResourceBundle;
public class NotificationBar extends HBox {
public class InfoBar extends HBox {
@FXML
private Label notificationLabel;
private Label infoMessage;
private final BooleanProperty dismissable = new SimpleBooleanProperty();
private final BooleanProperty notify = new SimpleBooleanProperty();
public NotificationBar() {
public InfoBar() {
setAlignment(Pos.CENTER);
setStyle("-fx-alignment: center;");
getStyleClass().addAll("info-bar");
Region spacer = new Region();
spacer.setMinWidth(40);
@@ -36,14 +39,21 @@ public class NotificationBar extends HBox {
vbox.setAlignment(Pos.CENTER);
HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS);
notificationLabel = new Label();
notificationLabel.getStyleClass().add("notification-label");
notificationLabel.setStyle("-fx-alignment: center;");
vbox.getChildren().add(notificationLabel);
infoMessage = new Label();
infoMessage.setFocusTraversable(true);
infoMessage.setAccessibleRole(AccessibleRole.BUTTON);
vbox.getChildren().add(infoMessage);
Button closeButton = new Button("X");
var closeGraphic = new FontAwesome5IconView();
closeGraphic.setGlyph(FontAwesome5Icon.TIMES);
closeGraphic.setGlyphSize(12);
closeGraphic.getStyleClass().add("glyph");
Button closeButton = new Button();
closeButton.setGraphic(closeGraphic);
closeButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
closeButton.setAccessibleText(ResourceBundle.getBundle("i18n.strings").getString("main.notification.closeButton.tooltip"));
closeButton.setMinWidth(40);
closeButton.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-weight: bold;");
closeButton.visibleProperty().bind(dismissable);
closeButton.setOnAction(_ -> {
@@ -61,11 +71,11 @@ public class NotificationBar extends HBox {
}
public String getText() {
return notificationLabel.getText();
return infoMessage.getText();
}
public void setText(String text) {
notificationLabel.setText(text);
infoMessage.setText(text);
}
public void setStyleClass(String styleClass) {

View File

@@ -4,8 +4,10 @@ import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.DefaultSceneFactory;
@@ -20,6 +22,7 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
@@ -119,8 +122,8 @@ abstract class ConvertVaultModule {
@Provides
@IntoMap
@FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
static FxController provideRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, MasterkeyFileAccess masterkeyFileAccess) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, masterkeyFileAccess, new SimpleObjectProperty<>(RecoveryActionType.CONVERT_VAULT), new SimpleObjectProperty<>(null));
}
}

View File

@@ -194,7 +194,7 @@ public class DecryptFileNamesViewController implements FxController {
}
}
//obvservable getter
//observable getter
public ObservableValue<String> dropZoneTextProperty() {
return dropZoneText;

View File

@@ -2,6 +2,7 @@ package org.cryptomator.ui.dialogs;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
@@ -19,17 +20,21 @@ public class Dialogs {
private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private static final String BUTTON_KEY_CLOSE = "generic.button.close";
@Inject
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) {
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
}
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
private SimpleDialog.Builder createDialogBuilder() {
return new SimpleDialog.Builder(resourceBundle, stageFactory);
return new SimpleDialog.Builder(resourceBundle, stageFactory, sceneFactory);
}
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
@@ -47,6 +52,52 @@ public class Dialogs {
});
}
public SimpleDialog.Builder prepareContactHubVaultOwner(Stage window) {
return createDialogBuilder().setOwner(window) //
.setTitleKey("contactHubVaultOwner.title") //
.setMessageKey("contactHubVaultOwner.message") //
.setDescriptionKey("contactHubVaultOwner.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION)//
.setOkButtonKey(BUTTON_KEY_CLOSE);
}
public SimpleDialog.Builder prepareHubVaultArchived(Stage window, Vault vault) {
return createDialogBuilder().setOwner(window) //
.setTitleKey("unlock.title", vault.getDisplayName()) //
.setMessageKey("hub.archived.message") //
.setDescriptionKey("hub.archived.description") //
.setIcon(FontAwesome5Icon.BAN)//
.setOkButtonKey(BUTTON_KEY_CLOSE);
}
public SimpleDialog.Builder prepareRecoveryVaultAdded(Stage window, String displayName) {
return createDialogBuilder().setOwner(window) //
.setTitleKey("recover.existing.title") //
.setMessageKey("recover.existing.message") //
.setDescriptionKey("recover.existing.description", displayName) //
.setIcon(FontAwesome5Icon.CHECK)//
.setOkButtonKey(BUTTON_KEY_CLOSE);
}
public SimpleDialog.Builder prepareRecoveryVaultAlreadyExists(Stage window, String displayName) {
return createDialogBuilder().setOwner(window) //
.setTitleKey("recover.alreadyExists.title") //
.setMessageKey("recover.alreadyExists.message") //
.setDescriptionKey("recover.alreadyExists.description", displayName) //
.setIcon(FontAwesome5Icon.EXCLAMATION)//
.setOkButtonKey(BUTTON_KEY_CLOSE);
}
public SimpleDialog.Builder prepareRecoverPasswordSuccess(Stage window) {
return createDialogBuilder()
.setOwner(window) //
.setTitleKey("recoveryKey.recover.title") //
.setMessageKey("recoveryKey.recover.resetSuccess.message") //
.setDescriptionKey("recoveryKey.recover.resetSuccess.description") //
.setIcon(FontAwesome5Icon.CHECK)
.setOkAction(Stage::close)
.setOkButtonKey(BUTTON_KEY_CLOSE);
}
public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) {
return createDialogBuilder() //
.setOwner(window) //
@@ -69,7 +120,7 @@ public class Dialogs {
.setMessageKey("dokanySupportEnd.message") //
.setDescriptionKey("dokanySupportEnd.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("generic.button.close") //
.setOkButtonKey(BUTTON_KEY_CLOSE) //
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
.setOkAction(Stage::close) //
.setCancelAction(cancelAction);
@@ -83,8 +134,20 @@ public class Dialogs {
.setDescriptionKey("retryIfReadonly.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("retryIfReadonly.retry") //
.setCancelButtonKey("generic.button.close") //
.setCancelButtonKey(BUTTON_KEY_CLOSE) //
.setOkAction(okAction) //
.setCancelAction(Stage::close);
}
public SimpleDialog.Builder prepareNoDDirectorySelectedDialog(Stage window) {
return createDialogBuilder() //
.setOwner(window) //
.setTitleKey("recover.invalidSelection.title") //
.setMessageKey("recover.invalidSelection.message") //
.setDescriptionKey("recover.invalidSelection.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("generic.button.change") //
.setOkAction(Stage::close);
}
}

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.dialogs;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.StageFactory;
@@ -30,15 +31,15 @@ public class SimpleDialog {
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( //
new SimpleDialogController(resolveText(builder.messageKey, null), //
resolveText(builder.descriptionKey, null), //
resolveText(builder.descriptionKey, builder.descriptionArgs), //
builder.icon, //
resolveText(builder.okButtonKey, null), //
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
() -> builder.okAction.accept(dialogStage), //
() -> builder.cancelAction.accept(dialogStage)), //
Scene::new, builder.resourceBundle);
builder.sceneFactory, builder.resourceBundle);
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
dialogStage.setScene(loaderFactory.createScene(FxmlFile.SIMPLE_DIALOG));
}
public void showAndWait() {
@@ -62,19 +63,22 @@ public class SimpleDialog {
private Stage owner;
private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private String titleKey;
private String[] titleArgs;
private String messageKey;
private String descriptionKey;
private String[] descriptionArgs;
private String okButtonKey;
private String cancelButtonKey;
private FontAwesome5Icon icon;
private Consumer<Stage> okAction = Stage::close;
private Consumer<Stage> cancelAction = Stage::close;
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) {
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
}
public Builder setOwner(Stage owner) {
@@ -93,8 +97,9 @@ public class SimpleDialog {
return this;
}
public Builder setDescriptionKey(String descriptionKey) {
public Builder setDescriptionKey(String descriptionKey, String... args) {
this.descriptionKey = descriptionKey;
this.descriptionArgs = args;
return this;
}

View File

@@ -1,5 +1,8 @@
package org.cryptomator.ui.eventview;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
@@ -115,7 +118,7 @@ public class EventListCellController implements FxController {
eventActionsMenu.hide();
eventActionsMenu.getItems().clear();
eventTooltip.setText(item.getKey().vault().getDisplayName());
addAction("generic.action.dismiss", () -> {
addLocalizedAction("generic.action.dismiss", () -> {
fileSystemEventAggregator.remove(item.getKey());
});
switch (item.getValue().mostRecentEvent()) {
@@ -124,20 +127,41 @@ public class EventListCellController implements FxController {
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
case FileIsInUseEvent fse -> this.adjustToFileInUseEvent(fse);
}
}
private void adjustToFileInUseEvent(FileIsInUseEvent fiiue) {
eventIcon.setValue(FontAwesome5Icon.USER_LOCK);
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
if (revealService != null) {
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
}
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
var user = userAndDevice[0];
var device = userAndDevice.length == 1 ? userAndDevice[0] : userAndDevice[1];
addLocalizedAction("eventView.entry.inUse.copyUserAndDevice", () -> copyToClipboard(user + ", " + device));
addLocalizedAction("eventView.entry.inUse.ignoreLock", fiiue.ignoreMethod());
}
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, bfe.ciphertextPath()));
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
} else {
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
}
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
}
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
@@ -145,9 +169,9 @@ public class EventListCellController implements FxController {
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())));
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else {
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
}
}
@@ -156,11 +180,11 @@ public class EventListCellController implements FxController {
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()));
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("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()));
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
}
}
@@ -169,9 +193,9 @@ public class EventListCellController implements FxController {
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()));
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else {
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
}
}
@@ -180,14 +204,19 @@ public class EventListCellController implements FxController {
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()));
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else {
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
}
}
private void addAction(String localizationKey, Runnable action) {
var entry = new MenuItem(resourceBundle.getString(localizationKey));
private void addLocalizedAction(String localizationKey, Runnable action) {
var entryText = resourceBundle.getString(localizationKey);
addAction(entryText, action);
}
private void addAction(String entryText, Runnable action) {
var entry = new MenuItem(entryText);
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
entry.setOnAction(_ -> action.run());
eventActionsMenu.getItems().addLast(entry);
@@ -234,18 +263,17 @@ public class EventListCellController implements FxController {
}
}
private Path convertVaultPathToSystemPath(Path p) {
if (!(p instanceof CryptoPath)) {
throw new IllegalArgumentException("Path " + p + " is not a vault path");
}
private Path convertVaultPathToSystemPath(String vaultInternalPath) {
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));
var mountPoint = v.getMountPoint().uri().getPath();
if(SystemUtils.IS_OS_WINDOWS) {
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
}
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
}
private void reveal(RevealPathService s, Path p) {

View File

@@ -30,9 +30,20 @@ public class FxApplication {
private final FxApplicationTerminator applicationTerminator;
private final AutoUnlocker autoUnlocker;
private final FxFSEventList fxFSEventList;
private final FxNotificationManager notificationManager;
@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, //
FxFSEventList fxFSEventList, //
FxNotificationManager notificationManager) {
this.startupTime = startupTime;
this.environment = environment;
this.settings = settings;
@@ -43,6 +54,7 @@ public class FxApplication {
this.applicationTerminator = applicationTerminator;
this.autoUnlocker = autoUnlocker;
this.fxFSEventList = fxFSEventList;
this.notificationManager = notificationManager;
}
public void start() {
@@ -88,6 +100,7 @@ public class FxApplication {
launchEventHandler.startHandlingLaunchEvents();
fxFSEventList.schedulePollForUpdates();
notificationManager.schedulePollForUpdates();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
}

View File

@@ -7,28 +7,29 @@ package org.cryptomator.ui.fxapp;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.decryptname.DecryptNameComponent;
import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
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 java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
@Module(subcomponents = {TrayMenuComponent.class, //
DecryptNameComponent.class, //
MainWindowComponent.class, //
PreferencesComponent.class, //
@@ -40,7 +41,9 @@ import java.io.InputStream;
HealthCheckComponent.class, //
UpdateReminderComponent.class, //
ShareVaultComponent.class, //
EventViewComponent.class})
EventViewComponent.class, //
RecoveryKeyComponent.class, //
NotificationComponent.class})
abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException {
@@ -49,6 +52,12 @@ abstract class FxApplicationModule {
}
}
@Provides
@FxApplicationScoped
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
return UiAppearanceProvider.get();
}
@Provides
@FxApplicationScoped
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
@@ -79,4 +88,10 @@ abstract class FxApplicationModule {
return factory.create();
}
@Provides
@FxApplicationScoped
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
return factory.create();
}
}

View File

@@ -36,82 +36,91 @@ public class FxApplicationStyle {
}
public void initialize() {
var uiTheme = settings.theme.get();
if (uiTheme == UiTheme.AUTOMATIC) {
registerOsThemeListener();
}
applyTheme(uiTheme);
settings.theme.addListener(this::appThemeChanged);
loadSelectedStyleSheet(settings.theme.get());
}
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, UiTheme oldValue, UiTheme newValue) {
if (oldValue == newValue) {
// no-op
} else if (newValue == UiTheme.AUTOMATIC) {
registerOsThemeListener();
} else if (oldValue == UiTheme.AUTOMATIC) {
removeOsThemeListener();
}
applyTheme(newValue);
}
private void removeOsThemeListener() {
if (appearanceProvider.isPresent()) {
try {
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to disable automatic theme switching.");
}
}
loadSelectedStyleSheet(newValue);
}
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
case AUTOMATIC -> {
appearanceProvider.ifPresent(provider -> {
try {
provider.addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic theme switching.");
}
});
applySystemTheme();
LOG.warn("Failed to disable automatic theme switching.", e);
}
} else {
LOG.debug("Unable to remove listener os theme changes: No supported UiAppearanceProvider present");
}
}
private void systemInterfaceThemeChanged(Theme theme) {
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
}
}
private void applySystemTheme() {
private void registerOsThemeListener() {
if (appearanceProvider.isPresent()) {
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
try {
appearanceProvider.get().addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.warn("Failed to enable automatic theme switching.", e);
}
} else {
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
applyLightTheme();
LOG.warn("Unable to register for os theme changes: No supported UiAppearanceProvider present");
}
}
private void applyLightTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
.orElse(getClass().getResource("/css/light_theme.css"));
private void applyTheme(UiTheme uiTheme) {
if (!licenseHolder.isValidLicense()) {
loadAndApplyLightTheme();
} else {
switch (uiTheme) {
case AUTOMATIC -> {
var osTheme = appearanceProvider.map(UiAppearanceProvider::getSystemTheme).orElse(Theme.LIGHT);
systemInterfaceThemeChanged(osTheme);
}
case LIGHT -> loadAndApplyLightTheme();
case DARK -> loadAndApplyDarkTheme();
}
}
}
private void systemInterfaceThemeChanged(Theme osTheme) {
switch (osTheme) {
case LIGHT -> loadAndApplyLightTheme();
case DARK -> loadAndApplyDarkTheme();
}
}
private void loadAndApplyLightTheme() {
loadAndApplyTheme(Theme.LIGHT, "/css/light_theme.css");
}
private void loadAndApplyDarkTheme() {
loadAndApplyTheme(Theme.DARK, "/css/dark_theme.css");
}
private void loadAndApplyTheme(Theme appTheme, String cssFile) {
var stylesheet = getClass().getResource(cssFile);
if (stylesheet == null) {
LOG.warn("Failed to load light_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
appliedTheme.set(Theme.LIGHT);
throw new IllegalStateException("Cannot find resource %s".formatted(cssFile));
}
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(appTheme));
appliedTheme.set(appTheme);
}
private void applyDarkTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
.orElse(getClass().getResource("/css/dark_theme.css"));
if (stylesheet == null) {
LOG.warn("Failed to load dark_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
appliedTheme.set(Theme.DARK);
}
}
public ObjectProperty<Theme> appliedThemeProperty() {
public ObjectProperty<Theme> appliedAppThemeProperty() {
return appliedTheme;
}
}

View File

@@ -28,7 +28,7 @@ import static org.cryptomator.common.vaults.VaultState.Value.*;
@FxApplicationScoped
public class FxApplicationTerminator {
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR, VAULT_CONFIG_MISSING, ALL_MISSING);
private static final Set<VaultState.Value> STATES_PREVENT_TERMINATION = EnumSet.of(PROCESSING);
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);

View File

@@ -11,6 +11,7 @@ import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
@@ -54,6 +55,7 @@ public class FxApplicationWindows {
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final Lazy<EventViewComponent> eventViewWindow;
private final Lazy<NotificationComponent> notificationWindow;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
@@ -73,6 +75,7 @@ public class FxApplicationWindows {
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, //
Lazy<NotificationComponent> notificationWindow,
ExecutorService executor, //
Dialogs dialogs) {
this.primaryStage = primaryStage;
@@ -85,6 +88,7 @@ public class FxApplicationWindows {
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
@@ -193,6 +197,10 @@ public class FxApplicationWindows {
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showNotification() {
return CompletableFuture.supplyAsync(() -> notificationWindow.get().showNotification(), Platform::runLater).whenComplete(this::reportErrors);
}
/**
* Displays the generic error scene in the given window.
*

View File

@@ -3,6 +3,8 @@ package org.cryptomator.ui.fxapp;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
@@ -11,6 +13,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -23,6 +26,8 @@ import java.util.concurrent.TimeUnit;
@FxApplicationScoped
public class FxFSEventList {
private static final Logger LOG = LoggerFactory.getLogger(FxFSEventList.class);
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
private final FileSystemEventAggregator eventAggregator;
private final ScheduledExecutorService scheduler;
@@ -37,7 +42,13 @@ public class FxFSEventList {
}
public void schedulePollForUpdates() {
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
try {
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
} catch ( RejectedExecutionException e) {
if(!scheduler.isShutdown()) {
LOG.warn("Failed to poll for filesystem events", e);
}
}
}
/**

View File

@@ -0,0 +1,57 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.event.NotificationManager;
import org.cryptomator.event.VaultEvent;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Notification manager inside the UI domain.
* <p>
* Polls the {@link NotificationManager} for pending events every {@value POLL_INTERVAL_SECONDS } seconds and
* triggers the notification window display when events are available.
* Returns an observable list of events requiring a user notification with {@link #getEventsRequiringNotification()}.
*
* @see NotificationManager
*/
@FxApplicationScoped
public class FxNotificationManager {
private static final int POLL_INTERVAL_SECONDS = 1;
private final NotificationManager notificationManager;
private final ScheduledExecutorService scheduler;
private final FxApplicationWindows applicationWindows;
private final ObservableList<VaultEvent> eventsRequiringNotification;
@Inject
public FxNotificationManager(NotificationManager notificationManager, ScheduledExecutorService scheduler, FxApplicationWindows applicationWindows) {
this.notificationManager = notificationManager;
this.scheduler = scheduler;
this.applicationWindows = applicationWindows;
this.eventsRequiringNotification = FXCollections.observableArrayList();
}
public void schedulePollForUpdates() {
scheduler.scheduleAtFixedRate(this::checkForPendingNotifications, 0, POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
private void checkForPendingNotifications() {
Platform.runLater(() -> {
if (notificationManager.appendToAndClear(eventsRequiringNotification)) {
applicationWindows.showNotification();
}
});
}
public ObservableList<VaultEvent> getEventsRequiringNotification() {
return eventsRequiringNotification;
}
}

View File

@@ -0,0 +1,68 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.integrations.common.DisplayName;
import org.cryptomator.integrations.common.OperatingSystem;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.ColorScheme;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import java.util.concurrent.ConcurrentHashMap;
@DisplayName("JavaFX Color Scheme switcher")
@OperatingSystem(OperatingSystem.Value.LINUX)
@OperatingSystem(OperatingSystem.Value.WINDOWS)
@Priority(1050)
public class JfxUiAppearanceProvider implements UiAppearanceProvider {
private static final Logger LOG = LoggerFactory.getLogger(JfxUiAppearanceProvider.class);
private final ConcurrentHashMap<UiAppearanceListener, ChangeListener<ColorScheme>> uiAppearanceListeners = new ConcurrentHashMap<>();
private final Platform.Preferences preferences = Platform.getPreferences(); //Note: this service impl MUST be loaded in the fx application thread
@Override
public Theme getSystemTheme() {
return switch (preferences.getColorScheme()) {
case DARK -> Theme.DARK;
case LIGHT -> Theme.LIGHT;
};
}
@Override
public void adjustToTheme(Theme theme) {
//no-op
}
@Override
public void addListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
var fxChangeListener = (ChangeListener<ColorScheme>) (_, _, newScheme) -> {
var newTheme = switch (newScheme) {
case DARK -> Theme.DARK;
case LIGHT -> Theme.LIGHT;
};
uiAppearanceListener.systemAppearanceChanged(newTheme);
};
LOG.debug("Register listener for OS theme changes");
uiAppearanceListeners.computeIfAbsent(uiAppearanceListener, k -> {
Platform.runLater(() -> preferences.colorSchemeProperty().addListener(fxChangeListener));
return fxChangeListener;
});
}
@Override
public void removeListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
var fxChangeListener = uiAppearanceListeners.remove(uiAppearanceListener);
if (fxChangeListener != null) {
LOG.debug("Removing listener for OS theme changes");
Platform.runLater(() -> preferences.colorSchemeProperty().removeListener(fxChangeListener));
}
}
}

View File

@@ -1,135 +0,0 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.Environment;
import org.cryptomator.common.SemVerComparator;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Worker;
import javafx.concurrent.WorkerStateEvent;
import javafx.util.Duration;
import java.time.Instant;
import java.util.Comparator;
@FxApplicationScoped
public class UpdateChecker {
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
private final Environment env;
private final Settings settings;
private final StringProperty latestVersion = new SimpleStringProperty();
private final ScheduledService<String> updateCheckerService;
private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
private final Comparator<String> versionComparator = new SemVerComparator();
private final BooleanBinding updateAvailable;
private final BooleanBinding checkFailed;
@Inject
UpdateChecker(Settings settings, //
Environment env, //
ScheduledService<String> updateCheckerService) {
this.env = env;
this.settings = settings;
this.updateCheckerService = updateCheckerService;
this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
this.updateAvailable = Bindings.createBooleanBinding(this::isUpdateAvailable, latestVersion);
this.checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, state);
}
public void automaticallyCheckForUpdatesIfEnabled() {
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
startCheckingForUpdates(AUTO_CHECK_DELAY);
}
}
public void checkForUpdatesNow() {
startCheckingForUpdates(Duration.ZERO);
}
private void startCheckingForUpdates(Duration initialDelay) {
updateCheckerService.cancel();
updateCheckerService.reset();
updateCheckerService.setDelay(initialDelay);
updateCheckerService.setOnRunning(this::checkStarted);
updateCheckerService.setOnSucceeded(this::checkSucceeded);
updateCheckerService.setOnFailed(this::checkFailed);
updateCheckerService.start();
}
private void checkStarted(WorkerStateEvent event) {
LOG.debug("Checking for updates...");
state.set(UpdateCheckState.IS_CHECKING);
}
private void checkSucceeded(WorkerStateEvent event) {
var latestVersionString = updateCheckerService.getValue();
LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersionString);
lastSuccessfulUpdateCheck.set(Instant.now());
latestVersion.set(latestVersionString);
state.set(UpdateCheckState.CHECK_SUCCESSFUL);
}
private void checkFailed(WorkerStateEvent event) {
state.set(UpdateCheckState.CHECK_FAILED);
}
public enum UpdateCheckState {
NOT_CHECKED,
IS_CHECKING,
CHECK_SUCCESSFUL,
CHECK_FAILED;
}
/* Observable Properties */
public BooleanBinding checkingForUpdatesProperty() {
return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING);
}
public ReadOnlyStringProperty latestVersionProperty() {
return latestVersion;
}
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
public BooleanBinding checkFailedProperty() {
return checkFailed;
}
public boolean isUpdateAvailable() {
String currentVersion = getCurrentVersion();
String latestVersionString = latestVersion.get();
if (currentVersion == null || latestVersionString == null) {
return false;
} else {
return versionComparator.compare(currentVersion, latestVersionString) < 0;
}
}
public ObjectProperty<Instant> lastSuccessfulUpdateCheckProperty() {
return lastSuccessfulUpdateCheck;
}
public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
return state;
}
public String getCurrentVersion() {
return env.getAppVersion();
}
}

View File

@@ -1,93 +0,0 @@
package org.cryptomator.ui.fxapp;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
@Module
public abstract class UpdateCheckerModule {
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerModule.class);
private static final URI LATEST_VERSION_URI = URI.create("https://api.cryptomator.org/desktop/latest-version.json");
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
@Provides
@FxApplicationScoped
static Optional<HttpClient> provideHttpClient() {
try {
return Optional.of(HttpClient.newBuilder() //
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
.build());
} catch (UncheckedIOException e) {
LOG.error("HttpClient for update check cannot be created.", e);
return Optional.empty();
}
}
@Provides
@FxApplicationScoped
static HttpRequest provideCheckForUpdatesRequest(Environment env) {
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", //
env.getAppVersion(), //
SystemUtils.OS_NAME, //
SystemUtils.OS_VERSION, //
SystemUtils.OS_ARCH); //
return HttpRequest.newBuilder() //
.uri(LATEST_VERSION_URI) //
.header("User-Agent", userAgent) //
.timeout(java.time.Duration.ofSeconds(10))
.build();
}
@Provides
@Named("checkForUpdatesInterval")
@FxApplicationScoped
static ObjectBinding<Duration> provideCheckForUpdateInterval(Settings settings) {
return Bindings.when(settings.checkForUpdates).then(UPDATE_CHECK_INTERVAL).otherwise(DISABLED_UPDATE_CHECK_INTERVAL);
}
@Provides
@FxApplicationScoped
static ScheduledService<String> provideCheckForUpdatesService(ExecutorService executor, Optional<HttpClient> httpClient, HttpRequest checkForUpdatesRequest, @Named("checkForUpdatesInterval") ObjectBinding<Duration> period) {
ScheduledService<String> service = new ScheduledService<>() {
@Override
protected Task<String> createTask() {
if (httpClient.isPresent()) {
return new UpdateCheckerTask(httpClient.get(), checkForUpdatesRequest);
} else {
return new Task<>() {
@Override
protected String call() {
throw new NullPointerException("No HttpClient present.");
}
};
}
}
};
service.setOnFailed(event -> LOG.error("Failed to execute update service", service.getException()));
service.setExecutor(executor);
service.periodProperty().bind(period);
return service;
}
}

View File

@@ -1,58 +0,0 @@
package org.cryptomator.ui.fxapp;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.ByteStreams;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.concurrent.Task;
import java.io.IOException;
import java.io.InputStream;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class UpdateCheckerTask extends Task<String> {
private static final ObjectMapper JSON = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerTask.class);
private static final long MAX_RESPONSE_SIZE = 10L * 1024; // 10kb should be sufficient. protect against flooding
private final HttpClient httpClient;
private final HttpRequest checkForUpdatesRequest;
UpdateCheckerTask(HttpClient httpClient, HttpRequest checkForUpdatesRequest) {
this.httpClient = httpClient;
this.checkForUpdatesRequest = checkForUpdatesRequest;
setOnFailed(event -> LOG.error("Failed to check for updates", getException()));
}
@Override
protected String call() throws IOException, InterruptedException {
HttpResponse<InputStream> response = httpClient.send(checkForUpdatesRequest, HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() == 200) {
return processBody(response);
} else {
throw new IOException("Unexpected HTTP response code " + response.statusCode());
}
}
private String processBody(HttpResponse<InputStream> response) throws IOException {
try (InputStream in = response.body(); //
InputStream limitedIn = ByteStreams.limit(in, MAX_RESPONSE_SIZE)) {
var json = JSON.reader().readTree(limitedIn);
if (SystemUtils.IS_OS_MAC_OSX) {
return json.get("mac").asText();
} else if (SystemUtils.IS_OS_WINDOWS) {
return json.get("win").asText();
} else if (SystemUtils.IS_OS_LINUX) {
return json.get("linux").asText();
} else {
throw new IllegalStateException("Unsupported operating system");
}
}
}
}

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