Compare commits

..

395 Commits

Author SHA1 Message Date
Armin Schrenk
4114ea6163 Merge branch 'release/1.13.0' 2024-06-26 10:29:15 +02:00
Armin Schrenk
82da9f7a88 finalize release 2024-06-26 10:27:29 +02:00
Armin Schrenk
aa08046c4d Merge branch 'develop' into release/1.13.0 2024-06-21 15:18:39 +02:00
Armin Schrenk
b6d7f63470 [ci skip] also sign binaries from CraC project (dammn zulu jdk) 2024-06-21 15:10:47 +02:00
Armin Schrenk
624d18238c Merge branch 'develop' into release/1.13.0 2024-06-21 14:38:38 +02:00
Armin Schrenk
3bfe2b7a9e fix Windows build 2024-06-21 14:38:17 +02:00
Armin Schrenk
ba79cd0f8c prepare 1.13.0 2024-06-21 11:55:54 +02:00
Armin Schrenk
be63ea1104 Closes #3439 2024-06-21 11:51:49 +02:00
Cryptobot
a3ad2a5677 New Crowdin updates (#3355)
New translations strings.properties

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

[ci skip]
2024-06-21 11:29:09 +02:00
dependabot[bot]
26e5ceea01 Bump the maven-build-plugins group with 2 updates (#3459) 2024-06-21 08:20:05 +00:00
dependabot[bot]
83f1c4ea41 Bump org.mockito:mockito-core in the java-test-dependencies group (#3427) 2024-06-21 08:19:43 +00:00
Armin Schrenk
8d78e1432a Merge pull request #3454 from cryptomator/feature/jfx22
Feature: Update to jfx22
2024-06-20 14:43:15 +02:00
mindmonk
d2ec5a5646 Merge pull request #3457 from cryptomator/feature/dokany-info-dialog
Feature: Migrate volume type Dokany to Default one and Display Info Dialog
2024-06-17 12:20:39 +02:00
Jan-Peter Klein
6bb0328799 removed dialog in naming 2024-06-14 14:20:18 +02:00
Jan-Peter Klein
8a8617b480 renamed method and code refactored 2024-06-14 11:07:21 +02:00
Jan-Peter Klein
7484744038 minor text change 2024-06-13 12:06:38 +02:00
Jan-Peter Klein
0bbc4ecae5 refactored code and new method handleDokanySupportEndAndShowDialog 2024-06-13 12:06:18 +02:00
Jan-Peter Klein
9fcb166047 close dialog by click on 'Open Preferences' 2024-06-13 12:04:34 +02:00
Jan-Peter Klein
1ac017e3ab changed dialog prefix 2024-06-13 11:51:01 +02:00
Jan-Peter Klein
1ba401b8e6 changed wording of button and description
primary color for icon
2024-06-12 12:02:40 +02:00
Armin Schrenk
07303b58f7 Merge branch 'develop' into feature/jfx22 2024-06-12 11:16:41 +02:00
Armin Schrenk
32a463768c Merge pull request #3456 from purejava/fix-#3453
Workaround for malformed javafx POM. Closes #3453.
2024-06-12 11:14:52 +02:00
Armin Schrenk
2307950e3b add javafx.platform property workaround to other build/ci scripts 2024-06-12 10:58:49 +02:00
Ralph Plawetzki
8108128c22 Temporary work around for JavaFX upstream bug
Occurs with Maven 3.9.7, fixed with Gluon JavaFX 23-ea+20, that contains a fixed parent POM
See https://mail.openjdk.org/pipermail/openjfx-dev/2024-May/047279.html
2024-06-11 19:59:18 +02:00
Jan-Peter Klein
58fe6da7a8 Merge branch 'develop' into feature/dokany-info-dialog 2024-06-11 15:25:23 +02:00
Jan-Peter Klein
92bf73297a add dokany info dialog 2024-06-11 14:43:56 +02:00
Armin Schrenk
0bf8fb39dd Update to javafx 22.0.1 2024-06-07 15:39:42 +02:00
Armin Schrenk
0dc9a3a834 align local build scripts 2024-06-07 14:56:54 +02:00
Armin Schrenk
91d6264b9f ignore downloaded artifact 2024-06-06 12:10:03 +02:00
Armin Schrenk
d2599ea525 closes #2656 2024-06-06 12:07:18 +02:00
dependabot[bot]
7180af9bc7 Bump the java-production-dependencies group across 1 directory with 9 updates (#3446) 2024-06-03 10:30:50 +00:00
Armin Schrenk
dc993dc57c fixes #3440 (PR in #3447) 2024-06-03 12:29:54 +02:00
dependabot[bot]
464c74ab6c Bump the maven-build-plugins group across 1 directory with 2 updates (#3431) 2024-06-03 09:39:36 +00:00
Armin Schrenk
8c7dd8c74f Merge pull request #3435 from cryptomator/feature/jdk22
Feature: JDK22
2024-05-21 16:58:57 +02:00
Armin Schrenk
69bcf6fac6 do not forget to update debian files
(dammn you unflexible launchpad!)
2024-05-21 12:51:01 +02:00
Armin Schrenk
7c243dd434 pin cryptolib version due removed jdk.crypto.ec package 2024-05-16 17:51:04 +02:00
Armin Schrenk
357a0b9c31 closes #3376 2024-05-14 17:58:21 +02:00
Armin Schrenk
d69d11b82f update dependencies requiring JDK 22 2024-05-14 17:25:56 +02:00
mindmonk
863e9bbcb3 Merge pull request #3362 from cryptomator/feature/update-checker-refactoring
Feature: Expansion of Preferences Update Tab with UI Elements and Refactoring of UpdateChecker
2024-05-10 16:31:26 +02:00
Jan-Peter Klein
1c47012033 undo getter changes 2024-05-10 15:57:04 +02:00
Jan-Peter Klein
cd99eaa323 updateAvailable ObservableValue to BooleanBinding 2024-05-10 15:51:41 +02:00
Jan-Peter Klein
dda2afda92 refactored ObservableValues to BooleanBindings and code cleanup 2024-05-10 15:45:17 +02:00
Jan-Peter Klein
5519eefcfa cr related changes 2024-05-10 14:55:50 +02:00
Jan-Peter Klein
a81352800d removed twoWeeksAgo var 2024-05-08 11:38:23 +02:00
Jan-Peter Klein
6fec16e498 removed unused ui function 2024-05-08 10:57:42 +02:00
Jan-Peter Klein
42f31204a3 removed lastUpdateReminder setting 2024-05-08 09:41:58 +02:00
Jan-Peter Klein
7ab64cfe46 removed lastUpdateReminder setting 2024-05-08 09:05:26 +02:00
Jan-Peter Klein
1581a10c04 removed unused label id 2024-05-08 08:30:06 +02:00
Jan-Peter Klein
249becc25d removed unused instance var 2024-05-08 08:28:53 +02:00
Jan-Peter Klein
da8e84f39d removed unnecessary HBox 2024-05-07 13:27:52 +02:00
Jan-Peter Klein
9ad2d223c3 changed BooleanBinding updateAvailable to ObservableValue<Boolean> 2024-05-07 13:23:53 +02:00
Jan-Peter Klein
3bf1e659ef changed checkFailed text wording 2024-05-07 12:41:39 +02:00
Jan-Peter Klein
e79257e5ea changed checkFailed property to ObservableValue 2024-05-06 13:46:49 +02:00
Armin Schrenk
dac517a6fb update project ot jdk 22
Note: dependency bumps regarding jdk22 compatiblilty are missing!
2024-05-02 16:59:45 +02:00
mindmonk
1ab808f1b0 Merge pull request #3414 from MSevenR/feature/double-click-unlocked-vault
Double-click on an Unlocked Vault
2024-05-02 09:31:56 +02:00
Jan-Peter Klein
08f81b7df4 registered module in SettingsJsonTest 2024-04-29 11:12:14 +02:00
Jan-Peter Klein
b16f32ca83 introduced new dependency
removed latestVersion from Settings
changed SettingsJson from Date to Instant
new UpdateChecker Property checkFailed
optimized naming
changed wordning
refactoring
2024-04-29 10:34:17 +02:00
MSevenR
d6ed88b544 Double-click on an Unlocked Vault
Double-click on an Unlocked Vault in the List to Reveal It
2024-04-26 01:44:45 -05:00
Jan-Peter Klein
ae697d7b73 refactor isUpdateSuccessfulAndCurrent to instance variable 2024-04-25 10:54:00 +02:00
Jan-Peter Klein
ff3306cc17 removed blank line, unused instance variable, import and Logger
refactored DateTimeFormatter into an instance variable
2024-04-24 15:03:49 +02:00
Armin Schrenk
9ae2f4e0f6 add timezone id to timestamp in settings 2024-04-23 16:12:59 +02:00
Armin Schrenk
4230afcbac refactored timeDiff-message-method to updatePref controller 2024-04-23 16:12:36 +02:00
Armin Schrenk
7fc6ab05a4 use Instant also in UpdateChecker 2024-04-23 15:19:45 +02:00
Armin Schrenk
060f6bfc97 change storage of timestamps in model to Instant 2024-04-23 15:18:37 +02:00
Jan-Peter Klein
919dac6caa refactoring 2024-04-22 16:35:20 +02:00
Jan-Peter Klein
98bcf63b2c remove lastUpdateCheck from Settings; add lastUpdateReminder and lastSuccessfulUpdateCheck
implement updateTimeDifferenceMessage
reorder UI elements
refactor code for clarity and maintainability
2024-04-17 11:40:58 +02:00
Tobias Hagemann
0585262952 Merge pull request #3395 from purejava/flathub-colour-banners
Add coloured banners for app in Flathub store
2024-04-13 16:33:31 +02:00
Ralph Plawetzki
a33dc3980e Use colours from Cryptomator CD manual 2024-04-13 15:36:52 +02:00
Ralph Plawetzki
caa5e77386 Swap the light and dark colors, it's calmer for the eye 2024-04-13 08:48:20 +02:00
Ralph Plawetzki
c037920e79 Add coloured banners for app in Flathub store 2024-04-12 17:45:38 +02:00
Armin Schrenk
3cdd352d39 Update check-jdk-updates.yml
Use latest jdk version for jdk check
2024-04-10 12:27:05 +02:00
dependabot[bot]
8ae80e2932 Bump the maven-build-plugins group with 2 updates (#3391) 2024-04-09 08:34:03 +00:00
dependabot[bot]
2ffa1ee236 Bump the github-actions group with 2 updates (#3387) 2024-04-09 08:16:34 +00:00
Tobias Hagemann
2bf5b57823 Update README.md
[ci skip]
2024-04-05 12:33:38 +02:00
Jan-Peter Klein
853ea69180 updated log error message as suggested by coderabbit 2024-04-04 13:30:47 +02:00
Jan-Peter Klein
43d0dd99ec adjusted code as per PR suggestions 2024-04-04 12:37:56 +02:00
Armin Schrenk
b40e6db701 Merge branch 'main' into develop 2024-03-27 13:02:32 +01:00
Armin Schrenk
387eb420eb Merge branch 'hotfix/1.12.4' 2024-03-27 12:58:36 +01:00
Armin Schrenk
7785bb8820 finalize 1.12.4 2024-03-27 12:57:55 +01:00
Armin Schrenk
de4fa8c7b0 use latest WiX version
fixes CVE-2024-29187 and CVE-2024-29188
2024-03-27 12:55:13 +01:00
Armin Schrenk
17f519e01c prepare 1.12.4 2024-03-27 12:53:49 +01:00
dependabot[bot]
6c50023074 Bump the maven-build-plugins group with 2 updates (#3380) 2024-03-25 09:09:23 +00:00
Sebastian Stenzel
0009940e1e adjusted after dependency update 2024-03-14 12:09:10 +01:00
Armin Schrenk
f2a50b59b5 well, that should be the last reference to dokany 2024-03-12 09:56:30 +01:00
Armin Schrenk
ed03606981 One should also remove the actual dependency 2024-03-12 09:51:11 +01:00
Armin Schrenk
4a1eaf25c7 Closes #3361, Fixes #3351 2024-03-12 09:46:37 +01:00
dependabot[bot]
f5e445a610 Bump the java-production-dependencies group with 3 updates (#3365) 2024-03-12 07:57:00 +00:00
Jan-Peter Klein
6a3a256c0b Merge branch 'develop' into feature/update-checker-refactoring 2024-03-08 10:28:16 +01:00
Jan-Peter Klein
1ed77ebcc0 code cleanup 2024-03-08 10:27:35 +01:00
Jan-Peter Klein
db224e9e5c added latestVersion and lastUpdateCheck to settings and integrated with update checker 2024-03-08 09:58:59 +01:00
dependabot[bot]
c719982ef3 Bump the github-actions group with 1 update (#3354) 2024-03-05 17:24:28 +00:00
dependabot[bot]
f783f5d5ec Bump the java-test-dependencies group with 1 update (#3358) 2024-03-05 17:21:45 +00:00
dependabot[bot]
dd4f87b54c Bump the java-production-dependencies group with 4 updates (#3359) 2024-03-05 17:21:09 +00:00
Jan-Peter Klein
b3789700e1 add upToDateLabel and fix update indicator issue 2024-03-05 12:56:38 +01:00
Jan-Peter Klein
b39834f4eb Merge branch 'develop' into feature/update-checker-refactoring 2024-03-05 10:18:32 +01:00
Jan-Peter Klein
8064d75102 removed state label and reorganized ui elements 2024-03-05 10:15:11 +01:00
Jan-Peter Klein
4064b61cd7 refactored updateChecker by incorporating state management and date tracking 2024-02-29 09:29:26 +01:00
Armin Schrenk
ef3affece3 [ci skip] fix wrong PR description in flathub workflow 2024-02-28 13:42:37 +01:00
Armin Schrenk
414465371b [ci skip] refactor win-exe workflow
* only publish, if msi and exe succeed
* make stable release jobs dependent of publish
2024-02-28 13:19:54 +01:00
Armin Schrenk
feed72a729 [ci skip] fix flathub workflow 2024-02-27 22:52:56 +01:00
Armin Schrenk
bca20a7a66 [ci skip] seperate av whitelisting into own workflow to be able to upload assets on failure 2024-02-27 18:55:14 +01:00
Armin Schrenk
a38377baaa [ci skip] Merge branch 'main' into develop 2024-02-27 12:53:30 +01:00
Armin Schrenk
61a45fc738 Merge branch 'release/1.12.3' 2024-02-27 12:52:00 +01:00
Armin Schrenk
3a65b5551f adjust release check to changed metainfo file
references fcb940e29c
2024-02-27 12:48:36 +01:00
Armin Schrenk
94ed5c18b8 damnn merge from develop to release 2024-02-27 12:41:06 +01:00
Armin Schrenk
a559483d86 finalize 1.12.3 2024-02-27 12:38:52 +01:00
Armin Schrenk
3acdef1dd0 Merge branch 'develop' into release/1.12.3 2024-02-27 12:37:13 +01:00
Armin Schrenk
def6f8ab95 [ci skip] clean up 2024-02-27 12:35:55 +01:00
Armin Schrenk
11ba1f3ddc revert 1ab73be1f4 and 2d968eac8c 2024-02-27 12:32:37 +01:00
Cryptobot
3228f2cf5f New Crowdin updates (#3343)
New translations strings.properties

Chinese Simplified; Dutch; French; Japanese; Portuguese; Russian; Slovak; Swedish; 

[ci skip]
2024-02-27 10:23:56 +01:00
Armin Schrenk
9c4f7ad79d [ci skip] remove unknown env/style combination from appstream metadata 2024-02-26 17:41:18 +01:00
Armin Schrenk
3bd57d162b [ci skip] update appstream metadata 2024-02-26 17:37:04 +01:00
dependabot[bot]
5ea73a5a8d Bump the java-production-dependencies group with 1 update (#3347) 2024-02-26 09:50:42 +00:00
Armin Schrenk
38670838c7 [ci skip] Drop yq usage 2024-02-26 10:15:25 +01:00
Armin Schrenk
3e0b84dbce [ci skip] fix pr body description & reduce log noise 2024-02-23 17:39:38 +01:00
Armin Schrenk
2302db6206 [ci skip] fix create pr command in flathub job 2024-02-23 17:27:48 +01:00
Armin Schrenk
a94bf99660 [ci skip] automatically set up tracking branch on push 2024-02-23 17:13:11 +01:00
Armin Schrenk
0c5ce353b1 [ci skip] use github.actor 2024-02-23 17:05:26 +01:00
Armin Schrenk
b3ce777a42 [ci skip] configure git in flathub job 2024-02-23 16:29:18 +01:00
Armin Schrenk
3085df3397 [ci skip] further fixes:
* remove yq after update
* set token for git
2024-02-23 16:14:21 +01:00
Armin Schrenk
fb740b605f add missing quote char [ci skip] 2024-02-23 15:57:06 +01:00
Armin Schrenk
6a74d9f3b2 use same varaible in get-version job 2024-02-23 15:53:22 +01:00
Armin Schrenk
1c7dffb63f Add ci job to create flathub PR on release [ci skip] 2024-02-23 15:44:48 +01:00
Jan-Peter Klein
a213f073b1 enhance screen selection by adding primary fallback 2024-02-23 10:51:23 +01:00
Armin Schrenk
1ab73be1f4 fixes #3344 2024-02-22 16:11:54 +01:00
Armin Schrenk
8412871090 apply proper formatting and state to error dialog 2024-02-22 12:23:43 +01:00
Armin Schrenk
fa63f3ca67 Reset develop to SNAPSHOT version 2024-02-22 12:21:00 +01:00
Armin Schrenk
dbc0f52481 fix not-uploading artifacts on release 2024-02-21 22:04:35 +01:00
Armin Schrenk
b8cd1caeac also upload asc file as artifact for mac build 2024-02-21 13:09:14 +01:00
Armin Schrenk
43c25b6d97 fix ci
references 84732337ca
2024-02-21 12:53:01 +01:00
Armin Schrenk
e44bc09074 Merge branch 'develop' into release/1.12.3 2024-02-21 12:20:36 +01:00
Cryptobot
7b02f78ef5 New Crowdin updates (#3342)
New translations strings.properties

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

[ci skip]
2024-02-21 12:13:24 +01:00
Armin Schrenk
f7e8a4d1e6 prepare 1.12.3 2024-02-20 18:13:01 +01:00
Armin Schrenk
664375d692 fix wrong method name 2024-02-20 18:06:55 +01:00
Armin Schrenk
2d968eac8c Merge pull request #3336 from cryptomator/feature/3272-vaultid-as-mountpoint
Feature: For FUSE-Ts default mountpoint use vault id as mountpoint
2024-02-20 17:34:09 +01:00
Armin Schrenk
542d2fcfe1 only create winget PR for stable versions [ci skip] 2024-02-19 16:42:42 +01:00
mindmonk
144e929896 Merge pull request #3335 from cryptomator/feature/center-unlock-dialog-on-mainstage
Center Unlock Success Dialog on Main Application Window
2024-02-19 15:05:28 +01:00
Armin Schrenk
803748f78d update code-sign-action [ci skip] 2024-02-19 14:46:58 +01:00
Armin Schrenk
3410e7243a Update minimal required macOS version for dmg
due to javafx update in 7d281e2878
2024-02-19 13:02:05 +01:00
Armin Schrenk
84732337ca removed GTK-2 support
due to javafx update in 7d281e2878
2024-02-19 12:17:11 +01:00
dependabot[bot]
c527808710 Bump the java-production-dependencies group with 4 updates (#3340) 2024-02-19 09:35:25 +00:00
Armin Schrenk
99c89dbf39 fix possible null comparsion 2024-02-19 10:22:22 +01:00
Jan-Peter Klein
829b64cd3d dialog centers on main app screen if mainWindow is hidden 2024-02-19 08:55:16 +01:00
Jan-Peter Klein
f4007267fb simplified the code and removed the function 2024-02-16 17:31:20 +01:00
Armin Schrenk
c82a0bfaf3 cleanup 2024-02-16 17:09:05 +01:00
Armin Schrenk
251ad65344 Merge pull request #3327 from cryptomator/feature/3233-load-presets-background
Feature: Load LocationPresets in background and show indicator in UI
2024-02-16 16:21:01 +01:00
Jan-Peter Klein
155ba4607b introduced functionality to center the unlock screen within the main application window 2024-02-16 15:24:52 +01:00
Armin Schrenk
62f6865d3e cleanup created directories after mount
fails silently
2024-02-14 10:30:51 +01:00
Armin Schrenk
ffb3290248 Merge branch 'develop' into feature/3272-vaultid-as-mountpoint 2024-02-14 10:29:30 +01:00
Armin Schrenk
a013ae3d91 [ci skip] change mountPointDir for macOS-Dev-run profile
aligns production build
2024-02-14 10:29:14 +01:00
Armin Schrenk
19a954e677 use correct mountProvider 2024-02-14 10:05:56 +01:00
Armin Schrenk
f262980acc closes #3272 2024-02-13 16:37:46 +01:00
Armin Schrenk
3b3aa18c92 fixes #3233
this time with gc prevention of a binding
2024-02-13 00:55:07 +01:00
Armin Schrenk
c7b8bc89c2 also don't bother upload to av scan, if not published 2024-02-12 13:18:17 +01:00
Armin Schrenk
5fc981abd3 prevent automatic overwrite of already released files 2024-02-12 13:16:45 +01:00
Armin Schrenk
36ec1a5ebc update windows update url [ci skip] 2024-02-09 18:37:19 +01:00
Armin Schrenk
be4aad4168 set default value for Windows debug flag 2024-02-09 17:03:22 +01:00
Armin Schrenk
fcb940e29c update release section of appstream metainfo file [ci skip] 2024-02-09 14:52:10 +01:00
Armin Schrenk
4f3ca2a6c4 fix appimage build script [ci skip] 2024-02-09 12:56:33 +01:00
Armin Schrenk
27cd34bee0 Merge branch 'main' into develop [ci skip] 2024-02-09 12:53:16 +01:00
Armin Schrenk
c07e51be51 Merge branch 'release/1.12.2' 2024-02-09 12:51:57 +01:00
Armin Schrenk
0421879b39 finalize 1.12.2 2024-02-09 12:51:19 +01:00
Armin Schrenk
2d627717a0 prepare 1.12.2 2024-02-09 10:59:33 +01:00
Armin Schrenk
dc0e88a694 fixes #3319 2024-02-09 10:55:27 +01:00
Armin Schrenk
a5e3630375 also specify version for winget release [ci skip] 2024-02-08 17:47:50 +01:00
Armin Schrenk
17335e8f70 shorten workflow name [ci skip] 2024-02-08 17:39:38 +01:00
Armin Schrenk
42dd2fba48 github cli needs special token in CI env [ci skip] 2024-02-08 17:37:50 +01:00
Armin Schrenk
e1cca6427c Add ci job for releasing to winget 2024-02-08 17:34:02 +01:00
Armin Schrenk
42d3dbaa23 always use latest version of winfsp-uninstaller 2024-02-08 17:10:40 +01:00
Armin Schrenk
185d67c492 Merge branch 'main' into develop [ci skip] 2024-02-07 11:43:59 +01:00
Armin Schrenk
a8af3c8b40 Merge branch 'release/1.12.1' 2024-02-07 11:42:58 +01:00
Armin Schrenk
038a7fac62 finalize 1.12.1 2024-02-07 11:42:37 +01:00
Armin Schrenk
48408fa40d prepare 1.12.1 2024-02-07 11:37:44 +01:00
Armin Schrenk
18a417667e Fixes #3315 2024-02-07 11:33:45 +01:00
JaniruTEC
aab07b13e3 Changed version specifier for dependency-check
See: https://github.com/cryptomator/cryptofs/pull/202#discussion_r1453615249
2024-02-06 22:28:16 +01:00
Armin Schrenk
869e40e351 use latest coffeelibs-jdk release 2024-02-06 15:34:09 +01:00
Armin Schrenk
bf91e3f15c Merge branch 'main' into develop [ci skip] 2024-02-06 14:29:19 +01:00
Armin Schrenk
17544828e4 Merge branch 'release/1.12.0' 2024-02-06 14:28:08 +01:00
Armin Schrenk
cfef3139b1 finalize 1.12.0 2024-02-06 14:20:50 +01:00
Armin Schrenk
b01ba27b06 Partially reverting 24293b316f 2024-02-06 14:16:03 +01:00
Armin Schrenk
5a33e66a7d prepare 1.12.0 2024-02-06 11:31:42 +01:00
Armin Schrenk
3c4e35406e Bump dependencies 2024-02-06 11:27:02 +01:00
Cryptobot
d3275fa4e7 New Crowdin updates (#3256)
New translations strings.properties

Arabic; Bashkir; Belarusian; Bulgarian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Czech; Danish; Dutch; Filipino; French; German; Greek; Hebrew; Hungarian; Italian; Japanese; Korean; Latvian; Norwegian Bokmal; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Romanian; Russian; Slovak; Spanish; Swahili, Tanzania; Swedish; Turkish; Vietnamese;

[ci skip]
2024-02-06 11:21:07 +01:00
dependabot[bot]
a30db34c19 Bump the java-production-dependencies group with 6 updates (#3295) 2024-02-06 10:18:33 +00:00
dependabot[bot]
509100e7ae Bump the java-test-dependencies group with 2 updates (#3312) 2024-02-06 10:17:11 +00:00
dependabot[bot]
024c34172b Bump the github-actions group with 5 updates (#3308) 2024-02-06 10:16:24 +00:00
dependabot[bot]
638570562b Bump the maven-build-plugins group with 3 updates (#3300) 2024-02-06 10:15:06 +00:00
Sebastian Stenzel
ee5a5c6563 Merge pull request #3291 from cryptomator/feature/legacy-device-migration
Hub 1.2.x → Hub 1.3.x Legacy Device Migration
2024-02-02 14:59:30 +01:00
Armin Schrenk
85c52c60c5 Revert "update javafx to 21.0.2"
This reverts commit 93184abd3c.
2024-02-02 10:45:34 +01:00
Armin Schrenk
93184abd3c update javafx to 21.0.2 2024-01-30 12:55:33 +01:00
Armin Schrenk
8062392ccd update jdk to 21.0.2+13 2024-01-30 12:17:45 +01:00
Jan-Peter Klein
d49c02c7de optimize share vault fxml 2024-01-29 17:37:55 +01:00
Sebastian Stenzel
d66cfe0e7c adjust to new migration API 2024-01-26 16:48:08 +01:00
mindmonk
fa26fa9dee Merge pull request #3289 from cryptomator/feature/share-vault
Feature: Introduce 'Share Vault' Functionality
2024-01-26 16:08:06 +01:00
Jan-Peter Klein
52fc94ca01 fixed a spelling error 2024-01-26 16:03:51 +01:00
Jan-Peter Klein
e9a412ae16 improved layout and added styleClass for add-box 2024-01-26 15:42:25 +01:00
Jan-Peter Klein
8104c35d76 more specific translation keys and consistent spacing 2024-01-23 19:09:49 +01:00
Jan-Peter Klein
baa2e05e69 improved wording and layout 2024-01-23 14:00:20 +01:00
Armin Schrenk
f50b204cef Merge pull request #3288 from cryptomator/feature/3246-split-device-registration
Feature: Split device registration into "normal" and "legacy" flow
2024-01-22 14:56:45 +01:00
lectrical
ed902bc59f Hello 2024 2024-01-20 16:22:27 +01:00
Sebastian Stenzel
693299a5d7 first draft of legacy device migration
actual migration still missing due to API discussion
2024-01-20 13:28:56 +01:00
Sebastian Stenzel
def64aa2ac added tests 2024-01-20 13:25:57 +01:00
Sebastian Stenzel
c5bb8a131d dedup 2024-01-20 13:15:33 +01:00
Sebastian Stenzel
2e443c72a9 add new API encryptVaultKey(vaultKey, userKey)
and `decodeECPublicKey(byte[])`
2024-01-20 13:12:59 +01:00
Sebastian Stenzel
e9ee17493b refactor API, expose decodeECPrivateKey(byte[]) 2024-01-20 13:01:48 +01:00
Sebastian Stenzel
35eb548d8e replaced Guava's BaseEncoding with Base64 2024-01-20 12:58:44 +01:00
Sebastian Stenzel
dc5d6e734e deduplicate code 2024-01-19 17:55:36 +01:00
Sebastian Stenzel
729f38866f use string templates for URI construction 2024-01-19 17:40:39 +01:00
Jan-Peter Klein
4de8434f1b Merge branch 'develop' into feature/share-vault 2024-01-19 12:31:37 +01:00
Jan-Peter Klein
cd40731b5f implemented open hub feature
optimized fxml files and img
2024-01-19 12:30:56 +01:00
Armin Schrenk
24293b316f Merge pull request #3236 from cryptomator/feature/3233-locationpresets-background
Feature: Load LocationPresets in background and show indicator in UI
2024-01-19 10:51:46 +01:00
Armin Schrenk
062c674ef1 Always hide spinner, as soon as locationPresetLoading is finished 2024-01-19 10:44:06 +01:00
Armin Schrenk
830bb5776f Use naming convention for comparator 2024-01-19 10:04:15 +01:00
Armin Schrenk
bfdb23785e Merge branch 'develop' into feature/3233-locationpresets-background 2024-01-18 15:57:45 +01:00
Armin Schrenk
248d655afb ditch custom in-place sorting and rely on javafx methods 2024-01-18 15:45:45 +01:00
Armin Schrenk
995c0a4b92 remove unnecessary TODO 2024-01-17 23:30:41 +01:00
Armin Schrenk
42425afe56 closes #3246
splitting registerSuccess into two scenes, one for hub 1.3.x and one for legacy
2024-01-17 16:52:35 +01:00
Sebastian Stenzel
a1b712495f switch back to UI thread later
see https://github.com/cryptomator/cryptomator/pull/3287#discussion_r1453196792
2024-01-17 13:35:23 +01:00
Sebastian Stenzel
2225476bf8 reordered methods
[ci skip]
2024-01-17 13:33:38 +01:00
Sebastian Stenzel
325057c548 Merge pull request #3287 from cryptomator/feature/refactored-hub-unlock
Refactored Hub Unlock
2024-01-17 13:32:09 +01:00
JaniruTEC
b1a5eed2aa Merge pull request #3286 from cryptomator/refactoring/centralize-dependency-check
Externalize dependency-check
2024-01-16 19:54:32 +01:00
Jan-Peter Klein
4ca190cfb9 implemented a 'Share Vault' button in vault_detail_locked and added a new window for vault sharing options 2024-01-16 17:21:07 +01:00
GiwrgosTsifoutis
828fd321cc Fixed "Locked Vault Options after unlocking vault #3249" (#3267)
Fixes #3249 

Bound the UIs disabled state to the vault's lock state without the need of a map
2024-01-16 12:32:36 +01:00
Armin Schrenk
2e32f39986 [ci skip] fix missing translation key
(cherry picked from commit daa026b285)
2024-01-16 11:38:54 +01:00
Armin Schrenk
daa026b285 [ci skip] fix missing translation key 2024-01-16 11:38:33 +01:00
Sebastian Stenzel
87084e8c2a TODO now obsolete
as we will now re-attempt unlock when returning from device registration

[ci skip]
2024-01-16 04:14:06 +01:00
Sebastian Stenzel
8e52058373 re-attempt receiving key after registering device 2024-01-16 03:58:30 +01:00
Armin Schrenk
8bf0acf35f build release artifacts with javafx 21.0.1
references 7d281e2878
2024-01-15 16:35:34 +01:00
JaniruTEC
e0d3a3d9c7 Externalized dependency-check 2024-01-15 16:12:27 +01:00
Sebastian Stenzel
b59ce75ecd add step 0: check API level 2024-01-14 15:02:41 +01:00
Sebastian Stenzel
748f895b98 change unlock request order 2024-01-14 15:01:51 +01:00
Sebastian Stenzel
6d974c7fcf fix incorrect vaultId 2024-01-14 14:58:58 +01:00
Sebastian Stenzel
0ed73e8b41 use string templates for building /api/* URIs 2024-01-14 14:02:35 +01:00
Sebastian Stenzel
538b4ecd0b added javadoc 2024-01-14 13:59:23 +01:00
Armin Schrenk
8f32b46b30 set no jlink compression for build artifacts
references 00fc2a9837
2024-01-12 13:18:05 +01:00
Armin Schrenk
4de25afde0 wrap locations in scrollPane 2024-01-11 23:48:26 +01:00
Armin Schrenk
2e6c228f3f Merge branch 'develop' into feature/3233-locationpresets-background 2024-01-11 22:59:41 +01:00
Armin Schrenk
00fc2a9837 remove deprecate compress options and use default ones
see also https://bugs.openjdk.org/browse/JDK-8301124?jql=text%20~%20%22compress%22
2024-01-11 22:59:08 +01:00
Armin Schrenk
22e80e7dac update local appimage buildscript to download jmods 2024-01-11 22:56:41 +01:00
Armin Schrenk
517e05c378 use stable version of winfsp-uninstaller 2024-01-11 22:35:51 +01:00
Sebastian Stenzel
5759b7e596 Merge pull request #3210 from cryptomator/feature/3203-device-already-registered
Feature: Adjust Hub workflow, if device is already registerd
2024-01-11 12:05:25 +01:00
Sebastian Stenzel
bfb26f6171 renamed field 2024-01-11 11:46:06 +01:00
Sebastian Stenzel
f36e2bb4b7 reduced visibility 2024-01-11 11:45:57 +01:00
Sebastian Stenzel
b85e455ff5 renamed field 2024-01-11 11:42:08 +01:00
Sebastian Stenzel
a902400522 get rid of registerException
use separate views, no need to pass state between views
2024-01-11 11:23:54 +01:00
Sebastian Stenzel
a6d6474294 Merge branch 'develop' into feature/3203-device-already-registered 2024-01-11 11:02:45 +01:00
Sebastian Stenzel
06859fe36b remove newline
[ci skip]
2024-01-11 10:27:42 +01:00
Sebastian Stenzel
7d7b7c1dc9 Merge pull request #3260 from cryptomator/feature/annihilate-FUPFMS
annihilate FUPFMS
2024-01-11 10:25:21 +01:00
Sebastian Stenzel
706d2b51be remove added newline
[ci skip]
2024-01-11 10:24:28 +01:00
Armin Schrenk
92bf136a8b Merge pull request #3278 from mooons/issue-3277
Bump README's build dependency JDK version from 19 to 21
2024-01-09 12:03:21 +01:00
mooons
f00f028d8b Bump README's build dependency JDK version from 19 to 21. Fix #3277 2024-01-09 01:57:59 -08:00
dependabot[bot]
325092efb0 Bump the java-test-dependencies group with 1 update (#3238) 2023-12-26 04:32:24 +00:00
dependabot[bot]
aaf8036713 Bump the maven-build-plugins group with 3 updates (#3264) 2023-12-25 19:40:21 +00:00
Sebastian Stenzel
c86ee679a9 renamed fuse-specific exception to be more generic 2023-12-21 10:05:22 +01:00
Sebastian Stenzel
a0fcb63a1a replaced FUPFMS
with more generic set of used mount services
2023-12-21 09:40:43 +01:00
Armin Schrenk
55ba255651 Update dependency-check.yml
to not run into 403 due to rate limit
2023-12-18 10:49:47 +01:00
dependabot[bot]
47df4213c3 Bump the java-production-dependencies group with 5 updates (#3255) 2023-12-18 09:40:34 +00:00
Armin Schrenk
6929760979 adjust dependency check plugin 2023-12-13 16:47:30 +01:00
Armin Schrenk
720fbd0e6b use separate cache for dependency-cache data 2023-12-13 16:47:08 +01:00
mindmonk
923af4bc83 Merge pull request #3199 from cryptomator/feature/vault-volume-type
Feature: Customizable Volume Type Options for Each Vault
2023-12-13 16:16:08 +01:00
Jan-Peter Klein
e9c464ba8f sonar cloud issues solved 2023-12-13 15:56:39 +01:00
Jan-Peter Klein
7b0f616747 Merge branch 'develop' into feature/vault-volume-type 2023-12-13 15:50:55 +01:00
Jan-Peter Klein
65788f3c1f pr mentioned changes 2023-12-13 15:50:37 +01:00
Jan-Peter Klein
f55fbf0f64 code cleanup and optimized a description text 2023-12-13 09:25:50 +01:00
Armin Schrenk
851838e3a2 remove run of dependency-chec workflow for release branches (already done in check release workflow) 2023-12-12 14:58:48 +01:00
Armin Schrenk
e194f9b205 update dependency check and refactor to own workflow 2023-12-12 14:56:48 +01:00
Jan-Peter Klein
2f8df88ad5 Merge branch 'develop' into feature/vault-volume-type
# Conflicts:
#	src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
2023-12-12 12:48:59 +01:00
Jan-Peter Klein
55247d9f8a moved provideDefaultMountService to MountModule and renamed problematicFuseMountServices to CONFLICTING_MOUNT_SERVICES 2023-12-12 12:38:33 +01:00
dependabot[bot]
48f273b755 Bump ch.qos.logback:logback-core from 1.4.13 to 1.4.14 (#3251) 2023-12-12 08:48:54 +00:00
Jan-Peter Klein
4fc31cb13f refactored 'loopbackPortSupported' from 'BooleanExpression' to 'ObservableValue<Boolean>' in VolumePreferencesController and MountOptionsController 2023-12-11 15:16:41 +01:00
Jan-Peter Klein
a795b82a42 vault options ui change 2023-12-11 14:49:54 +01:00
Jan-Peter Klein
1e28a04a7b renamed string properties 2023-12-11 10:16:26 +01:00
Jan-Peter Klein
b88eef4591 renamed string property 2023-12-08 12:03:08 +01:00
Jan-Peter Klein
eef5edf775 code cleanup 2023-12-08 11:14:54 +01:00
Armin Schrenk
bdb1d605eb Add cloud vendor to example of involved software 2023-12-07 18:43:10 +01:00
Armin Schrenk
dfe4b74f3d clean code:
* remove unused methods
* combine or move methods
2023-12-07 17:48:58 +01:00
Jan-Peter Klein
985a3c30de changed string properties for volume type labels 2023-12-07 13:56:04 +01:00
Jan-Peter Klein
1b5125dfed refactored code, global default volume type settings 2023-12-07 10:48:33 +01:00
Jan-Peter Klein
da240ce8b9 optimized error message 2023-12-07 08:40:39 +01:00
Jan-Peter Klein
52affb891e refactored mount method 2023-12-06 16:41:36 +01:00
Jan-Peter Klein
fac72ca24a undo remove settings port 2023-12-06 09:44:05 +01:00
Jan-Peter Klein
5cbed502ed undo VaultListManager changes 2023-12-06 08:49:21 +01:00
Jan-Peter Klein
dce4c60881 removed pr unrelated changes and code cleanup 2023-12-06 08:40:43 +01:00
Jan-Peter Klein
63bf0315c7 created global mountService 2023-12-05 16:21:12 +01:00
Armin Schrenk
8b543c48ee [ci skip] Merge branch 'main' into develop 2023-12-05 11:11:31 +01:00
Armin Schrenk
087a5326df Merge branch 'release/1.11.1' 2023-12-05 11:09:36 +01:00
Armin Schrenk
a4545352d8 finalize 1.11.1 2023-12-05 11:09:09 +01:00
dependabot[bot]
a07dea7ca8 Bump the github-actions group with 2 updates (#3235) 2023-12-05 08:39:49 +00:00
Jan-Peter Klein
c369487e8e Merge branch 'develop' into feature/vault-volume-type 2023-12-01 09:13:38 +01:00
Jan-Peter Klein
98590ecec5 refactoring 2023-12-01 09:11:43 +01:00
Armin Schrenk
316f3975ed add missing import 2023-11-30 18:24:52 +01:00
Armin Schrenk
71bf7e0913 Simplify handling of window hiding events 2023-11-30 18:22:07 +01:00
dependabot[bot]
c7839e2c46 Bump the java-production-dependencies group with 2 updates (#3234) 2023-11-30 14:26:07 +00:00
Armin Schrenk
89b8bc4148 adjust ui and add localization 2023-11-30 11:44:41 +01:00
Armin Schrenk
43ad1c05c0 cancel background task, if window is closed 2023-11-30 11:18:33 +01:00
Armin Schrenk
1343099be6 [ci skip] correct dependabot config 2023-11-30 10:03:29 +01:00
Armin Schrenk
b895ac69fb fixes #3233 2023-11-29 18:45:52 +01:00
dependabot[bot]
7d281e2878 Bump the javafx group with 5 updates (#3229) 2023-11-29 15:08:31 +00:00
Armin Schrenk
688450bf5a prepare 1.11.1 2023-11-28 13:38:05 +01:00
Cryptobot
e994133177 New Crowdin updates (#3191)
New translations strings.properties

Bulgarian; Chinese Simplified; Chinese Traditional; Dutch; Filipino; French; Greek; Hungarian; Norwegian Bokmal; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Romanian; Spanish; Swedish; Turkish; 

[ci skip]
2023-11-28 13:32:08 +01:00
dependabot[bot]
68713ef9a7 Merge pull request #3228 from cryptomator/dependabot/maven/maven-build-plugins-907533b1a4 2023-11-28 12:28:35 +00:00
Armin Schrenk
dbacbc8874 do not exectue dependency-check on CI (due to missing NVD API key) 2023-11-28 13:25:47 +01:00
dependabot[bot]
8382299a05 Bump the maven-build-plugins group with 4 updates
Bumps the maven-build-plugins group with 4 updates: [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin), [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire), [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) and [org.owasp:dependency-check-maven](https://github.com/jeremylong/DependencyCheck).


Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.6.0 to 3.6.1
- [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.6.0...maven-dependency-plugin-3.6.1)

Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.1.2 to 3.2.2
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.2)

Updates `org.codehaus.mojo:license-maven-plugin` from 2.2.0 to 2.3.0
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.2.0...2.3.0)

Updates `org.owasp:dependency-check-maven` from 8.4.0 to 9.0.1
- [Release notes](https://github.com/jeremylong/DependencyCheck/releases)
- [Changelog](https://github.com/jeremylong/DependencyCheck/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jeremylong/DependencyCheck/compare/v8.4.0...v9.0.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.owasp:dependency-check-maven
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: maven-build-plugins
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 12:20:39 +00:00
dependabot[bot]
93b09cf449 Bump the java-production-dependencies group with 6 updates (#3230) 2023-11-28 12:19:58 +00:00
dependabot[bot]
dac3311b81 Bump the java-test-dependencies group with 2 updates (#3227) 2023-11-28 12:05:42 +00:00
Armin Schrenk
5b1ca7a533 update dependabot config 2023-11-28 12:59:39 +01:00
Armin Schrenk
e940c29110 Closes #3226 2023-11-28 12:44:42 +01:00
Jan-Peter Klein
6a704ca0ad Merge branch 'develop' into feature/vault-volume-type 2023-11-27 16:24:35 +01:00
Jan-Peter Klein
38c102a64b refactored code by removing unnecessary observables 2023-11-27 12:46:29 +01:00
Armin Schrenk
b632c84bb6 more renaming 2023-11-24 11:23:22 +01:00
Armin Schrenk
6bb5ed1d73 fixes #3207 2023-11-24 11:19:44 +01:00
Jan-Peter Klein
1052e4c3d2 fix gui issues in vault mount settings 2023-11-23 10:55:17 +01:00
Armin Schrenk
44f776050f Apply suggestions from code review
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2023-11-22 21:36:15 +01:00
Armin Schrenk
baa29dbd73 remove unused translation key 2023-11-17 16:33:53 +01:00
Armin Schrenk
1caf391936 use correct word for button label 2023-11-17 16:24:23 +01:00
Armin Schrenk
82caf76e12 clean up 2023-11-17 15:51:20 +01:00
Armin Schrenk
de27bbf96e always cancle unlock result when closing the unlock window 2023-11-17 15:50:39 +01:00
Armin Schrenk
1199ef40dd on 409 repsonse of registerDevice-request, show setupFailed with adjusted message 2023-11-17 15:47:08 +01:00
Jan-Peter Klein
e7e88f13e3 improved code quality 2023-11-17 14:51:13 +01:00
Armin Schrenk
41c22b7840 Rename leftovers 2023-11-17 13:30:40 +01:00
Armin Schrenk
19cc4e8a37 Merge pull request #3194 from AntoineJT/feature/3155-toggle-vault-status-on-double-click
Feature: Double click on a vault in the list to unlock it
2023-11-15 16:51:44 +01:00
Jan-Peter Klein
7bc1d52bf8 set deprecated since of settingsjson port to 1.12.0, changed button text and added tooltip to 'show vault options' button 2023-11-12 13:49:47 +01:00
Antoine James Tournepiche
fd84ff09e1 No longer lock vault on double click
See https://github.com/cryptomator/cryptomator/pull/3194#issuecomment-1806106843
2023-11-10 22:05:58 +01:00
Antoine James Tournepiche
3dce175f94 Improve naming, avoid shadowing 2023-11-10 17:42:19 +01:00
Antoine James Tournepiche
0aaa3263cf Improve feature #3155 implementation 2023-11-09 23:50:34 +01:00
Antoine James Tournepiche
c4dcbd8c44 Implement requested feature #3155
Toggle selected vault lock status on double click on a vault item
in the vault list on the main window
2023-11-09 23:42:22 +01:00
Jan-Peter Klein
ef9cdd8e92 Merge branch 'develop' into feature/vault-volume-type
# Conflicts:
#	src/main/resources/i18n/strings.properties
2023-11-08 11:45:03 +01:00
Armin Schrenk
09ced50590 [ci skip] reset to snapshot version 2023-11-08 11:28:21 +01:00
Armin Schrenk
b2ab1a30a5 Merge branch 'main' into develop 2023-11-08 11:26:49 +01:00
Armin Schrenk
9eff3916b3 Merge branch 'release/1.11.0' 2023-11-08 11:26:13 +01:00
Armin Schrenk
98472d1952 use correct release date 2023-11-08 11:25:45 +01:00
Armin Schrenk
1ceafb69a5 finalize 1.11.0 2023-11-08 11:22:27 +01:00
Armin Schrenk
f3f4b6576c Merge branch 'develop' into release/1.11.0
# Conflicts:
#	src/main/resources/i18n/strings.properties
2023-11-08 10:52:30 +01:00
Cryptobot
e699d7dafc New Crowdin updates (#3189)
* New translations strings.properties
German, Spanish, Turkish
[ci skip]
2023-11-08 10:51:07 +01:00
Cryptobot
5eb6d186c0 New Crowdin updates (#3188)
New translations strings.properties

Arabic; 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; 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; Vietnamese; 

[ci skip]
2023-11-08 10:41:05 +01:00
Armin Schrenk
edc9409e56 pin wording 2023-11-08 10:19:12 +01:00
JaniruTEC
d015719fae Removed outdated translations 2023-11-07 18:52:48 +01:00
JaniruTEC
afc69e447e Renamed "setup code" to "Account Key" in localization files 2023-11-07 18:20:10 +01:00
JaniruTEC
54fbbd734f Renamed "setup code" to "Account Key" in UI 2023-11-07 18:19:12 +01:00
Jan-Peter Klein
2f657c7c14 migrate port to vault settings 2023-11-07 12:42:07 +01:00
Sebastian Stenzel
d29872e69e add "Action Required" prompt
when encountering http status 449 during vault unlock. fixes #3181

Co-authored-by: SailReal <julian.raufelder@skymatic.de>
(cherry picked from commit f2e7d0fae2)
2023-11-07 12:20:27 +01:00
Jan-Peter Klein
ab9278a9fc Merge branch 'develop' into feature/vault-volume-type 2023-11-03 10:48:00 +01:00
Jan-Peter Klein
49769f986a remove port from settings 2023-11-03 10:47:43 +01:00
Jan-Peter Klein
b68b895bba removed tcp port from volume preferences controller 2023-11-02 16:53:29 +01:00
Tobias Hagemann
99fb9972f9 updated sponsors
[ci skip]
2023-11-02 15:37:19 +01:00
Sebastian Stenzel
f2e7d0fae2 add "Action Required" prompt
when encountering http status 449 during vault unlock. fixes #3181

Co-authored-by: SailReal <julian.raufelder@skymatic.de>
2023-11-02 14:41:58 +01:00
Jan-Peter Klein
77f9e6c411 some ui and wording changes 2023-11-02 13:47:58 +01:00
Jan-Peter Klein
31fcf294e9 fix misbehaivour 2023-11-01 14:59:25 +01:00
Jan-Peter Klein
a03f00ee9a set up default mount service as pre fallbackProvider 2023-10-31 20:29:54 +01:00
Jan-Peter Klein
80af32bd82 introduced FuseRestartRequiredException and UnlockFuseRestartRequired window 2023-10-30 17:05:09 +01:00
Jan-Peter Klein
b7e4c0fe31 set default mount service and port while adding a vault 2023-10-30 15:27:11 +01:00
Jan-Peter Klein
76d499596f Merge branch 'develop' into feature/vault-volume-type 2023-10-30 14:33:32 +01:00
Jan-Peter Klein
1818344c49 different labeling for volume type option in general volume preferences and vault options 2023-10-30 14:33:01 +01:00
Armin Schrenk
7ff2e22f17 Merge branch 'develop' into release/1.11.0 2023-10-30 10:41:11 +01:00
Cryptobot
4eee66b0ef New Crowdin updates (#3128)
New translations strings.properties

Arabic; Belarusian; Bulgarian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Czech; Danish; Dutch; Filipino; Finnish; French; German; Greek; Hebrew; Hungarian; Italian; Japanese; Korean; Norwegian Bokmal; Polish; Portuguese; Portuguese, Brazilian; Romanian; Russian; Slovak; Spanish; Swahili, Tanzania; Swedish; Turkish; Ukrainian; Vietnamese; 

[ci skip]
2023-10-30 10:39:04 +01:00
Sebastian Stenzel
19fe7ba6bf updated to tiny-oauth2-client 0.8.0 2023-10-27 19:54:58 +02:00
Armin Schrenk
7b522582fb Merge pull request #3163 from cryptomator/feature/3075-fix-ci-jdk-version
Feature: Use fixed JDK version for releases
2023-10-27 12:51:49 +02:00
Jan-Peter Klein
e79c975d7b prevent mounting via MountFailedException when fuseRestartRequired 2023-10-27 12:39:25 +02:00
Armin Schrenk
7594e51e2b Update debian.yml
Remove architecture in apt install dependency
2023-10-27 12:30:37 +02:00
Armin Schrenk
b3f0ff3662 use coffeelibs JDK for release builds 2023-10-27 12:10:37 +02:00
Armin Schrenk
8ce304a0c3 Add link to how-to-jdk-update in notification 2023-10-26 16:21:22 +02:00
Jan-Peter Klein
c85823fe04 code cleanup 2023-10-26 12:18:40 +02:00
Jan-Peter Klein
8f46aec851 created MountServiceConverter class 2023-10-26 10:35:16 +02:00
Jan-Peter Klein
cd6d1eeab3 moved fupfms to VaultModule 2023-10-26 10:25:11 +02:00
Jan-Peter Klein
52b74a74fe display specific mount options according to selected volume type in vault mounting options 2023-10-25 14:02:29 +02:00
Jan-Peter Klein
368f0630c1 some sonar cloud fixes 2023-10-25 12:44:22 +02:00
Jan-Peter Klein
040bc4c5df fix sonar cloud issues 2023-10-24 14:26:06 +02:00
Jan-Peter Klein
07906fbea5 modified mounter to utilize the mount service settings specified in the vault settings 2023-10-24 14:04:53 +02:00
Armin Schrenk
f893b2b5be Merge pull request #3026 from cryptomator/feature/uninstall-old-winfsp
Feature: Update to WinFsp 2.x and uninstall old winfsp in Windows EXE installer
2023-10-23 12:26:28 +02:00
Armin Schrenk
db868c6df7 move worklfow to correct directory 2023-10-20 16:22:45 +02:00
Jan-Peter Klein
44f67b7c89 Merge branch 'develop' into feature/vault-volume-type 2023-10-20 15:58:52 +02:00
Armin Schrenk
652a6f5c4d use environment variables and update current jdk 2023-10-20 13:42:00 +02:00
Armin Schrenk
a7a94099a8 Closes #3075 2023-10-20 13:35:03 +02:00
Armin Schrenk
71a3cbc334 add workflow to check for jdk updates 2023-10-20 13:29:43 +02:00
Armin Schrenk
56624fc079 Replace URL files by hardcoded links in build scripts 2023-10-16 16:35:17 +02:00
Armin Schrenk
218c5243e3 Merge branch 'develop' into feature/uninstall-old-winfsp 2023-10-16 16:29:57 +02:00
Armin Schrenk
cf09eff640 Use new version of uninstaller:
* logging
* correct reboot
2023-10-13 17:49:48 +02:00
Armin Schrenk
38a5d40b64 Merge branch 'develop' into feature/uninstall-old-winfsp 2023-10-11 16:14:26 +02:00
Jan-Peter Klein
44ec19122a added port and mountService to vaultSettings 2023-09-12 02:19:08 +02:00
Jan-Peter Klein
8ba2540b35 implemented volume type selection in 'vault options mount' 2023-09-11 14:23:31 +02:00
Armin Schrenk
06f288ef76 only execute winfsp-uninstaller on install or repair 2023-08-29 17:51:05 +02:00
Armin Schrenk
ffcad9f1ec reformat 2023-08-29 17:32:49 +02:00
Armin Schrenk
031dc8a31a bump exe uninstaller to new version and skip uninstaller on bundle uninstall 2023-08-29 17:25:43 +02:00
Armin Schrenk
422efcc89f [ci skip] use new version of winfsp-uninstaller 2023-08-23 15:43:57 +02:00
Armin Schrenk
c1c0c2e82f [ci skip] use new version of winfsp-uninstaller 2023-08-22 10:47:18 +02:00
Armin Schrenk
c6790fec70 [ci skip] use new version of winfsp-uninstaller 2023-08-17 12:01:58 +02:00
Armin Schrenk
e9ef122e7f Merge branch 'develop' into feature/uninstall-old-winfsp 2023-08-14 15:13:11 +02:00
Armin Schrenk
40611b4ebe improve wording 2023-08-01 10:48:42 +02:00
Armin Schrenk
0800c53aac update to uninstaller-beta4 2023-07-24 10:36:23 +02:00
Armin Schrenk
c6ab05979c cleanup [ci skip] 2023-07-19 16:33:40 +02:00
Armin Schrenk
2c8d5d3d5d update ci script 2023-07-19 16:30:08 +02:00
Armin Schrenk
b76a311ddc Replace winfsp-uninstaller by downloadlink 2023-07-19 16:24:44 +02:00
Armin Schrenk
315bf0d51f Format confirmation dialog 2023-07-14 16:00:49 +02:00
Armin Schrenk
4cdbe50eb6 adjust to new winfsp-uninstaller exe 2023-07-13 17:29:50 +02:00
Armin Schrenk
ead0f0fae1 add exe for winfsp uninstallation and include in burn bundle 2023-07-11 09:41:54 +02:00
183 changed files with 6503 additions and 1421 deletions

View File

@@ -26,6 +26,7 @@ body:
Examples:
- Operating System: Windows 10
- Cryptomator: 1.5.16
- OneDrive: 23.226
- LibreOffice: 7.1.4
value: |
- Operating System:

View File

@@ -6,11 +6,38 @@ updates:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "UTC"
timezone: "Etc/UTC"
groups:
maven-dependencies:
java-test-dependencies:
patterns:
- "org.junit.jupiter:*"
- "org.mockito:*"
- "org.hamcrest:*"
- "com.google.jimfs:jimfs"
maven-build-plugins:
patterns:
- "org.apache.maven.plugins:*"
- "org.jacoco:jacoco-maven-plugin"
- "org.owasp:dependency-check-maven"
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
- "org.codehaus.mojo:license-maven-plugin"
javafx:
patterns:
- "org.openjfx:*"
java-production-dependencies:
patterns:
- "*"
exclude-patterns:
- "org.openjfx:*"
- "org.apache.maven.plugins:*"
- "org.jacoco:jacoco-maven-plugin"
- "org.owasp:dependency-check-maven"
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
- "org.codehaus.mojo:license-maven-plugin"
- "org.junit.jupiter:*"
- "org.mockito:*"
- "org.hamcrest:*"
- "com.google.jimfs:jimfs"
- package-ecosystem: "github-actions"
directory: "/" # even for `.github/workflows`

View File

@@ -11,7 +11,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: '22.0.1+8'
jobs:
get-version:
@@ -29,16 +29,16 @@ jobs:
include:
- os: ubuntu-latest
appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_linux-x64_bin-jmods.zip'
openjfx-sha: 'fbb22f35951c2e049cc2554dd03c2c56b4f5adc4b2ae9248872f46175ac103d8'
- os: [self-hosted, Linux, ARM64]
appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_linux-aarch64_bin-jmods.zip'
openjfx-sha: '1982ad168a5e8d7cf4a9458a7d088b4f0552d0ac3f24f23fb88f8bc7e8d69a13'
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -68,28 +68,24 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
run: mvn -B clean package -Plinux -DskipTests -Djavafx.platform=linux
- name: Patch target dir
run: |
cp LICENSE.txt target
cp target/cryptomator-*.jar target/mods
- name: Run jlink
#Remark: no compression is applied for improved build compression later (here appimage)
run: >
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
--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
--strip-native-commands
--no-header-files
--no-man-pages
--strip-debug
--compress=1
- name: Prepare additional launcher
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
env:
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
REVISION_NUM: ${{ needs.get-version.outputs.revNum }}
--compress zip-0
- name: Run jpackage
run: >
${JAVA_HOME}/bin/jpackage
@@ -102,7 +98,7 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2023 Skymatic GmbH"
--copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
@@ -163,7 +159,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@v3
uses: actions/upload-artifact@v4
with:
name: appimage-${{ matrix.appimage-suffix }}
path: |
@@ -172,8 +168,8 @@ jobs:
cryptomator-*.asc
if-no-files-found: error
- name: Publish AppImage on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@v2
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}

40
.github/workflows/av-whitelist.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: AntiVirus Whitelisting
on:
workflow_call:
inputs:
url:
description: "Url to the file to upload"
required: true
type: string
workflow_dispatch:
inputs:
url:
description: "Url to the file to upload"
required: true
type: string
jobs:
allowlist:
name: Anti Virus Allowlisting
runs-on: ubuntu-latest
steps:
- name: Download file
run: |
curl --remote-name ${{ inputs.url }} -L
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
protocol: ftps
server: allowlist.kaspersky-labs.com
port: 990
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
- name: Upload to Avast
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
protocol: ftp
server: whitelisting.avast.com
port: 21
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}

View File

@@ -7,7 +7,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
defaults:
run:
@@ -19,13 +19,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
@@ -33,10 +33,10 @@ jobs:
- name: Build and Test
run: >
xvfb-run
mvn -B verify
mvn -B verify -Djavafx.platform=linux
jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Pcoverage,dependency-check
-Pcoverage
-Dsonar.projectKey=cryptomator_cryptomator
-Dsonar.organization=cryptomator
-Dsonar.host.url=https://sonarcloud.io
@@ -45,7 +45,7 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Draft a release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
draft: true
discussion_category_name: releases

64
.github/workflows/check-jdk-updates.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Checks JDK version for minor updates
on:
schedule:
- cron: '0 0 1 * *' # run once a month at the first day of month
env:
JDK_VERSION: '22.0.1+8'
JDK_VENDOR: zulu
jobs:
jdk-current:
name: Check out current version
runs-on: ubuntu-latest
outputs:
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
steps:
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JDK_VERSION }}
distribution: ${{ env.JDK_VENDOR }}
check-latest: false
- name: Read JAVA_VERSION_DATE and store in env variable
id: get-data
run: |
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
jdk-latest:
name: Checkout latest jdk version
runs-on: ubuntu-latest
outputs:
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
steps:
- uses: actions/setup-java@v4
with:
java-version: 21
distribution: ${{ env.JDK_VENDOR }}
check-latest: true
- name: Read JAVA_VERSION_DATE and store in env variable
id: get-data
run: |
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
version=$(cat ${JAVA_HOME}/release | grep "JAVA_RUNTIME_VERSION=\"" | awk -F'=' '{print $2}' | tr -d '"')
echo "jdk-version=${version}" >> "$GITHUB_OUTPUT"
notify:
name: Notifies for jdk update
runs-on: ubuntu-latest
needs: [jdk-current, jdk-latest]
if: ${{ needs.jdk-latest.outputs.jdk-date }} > ${{ needs.jdk-current.outputs.jdk-date }}
steps:
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "JDK update available"
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ needs.jdk-latest.outputs.jdk-version }}. See https://github.com/cryptomator/cryptomator/wiki/How-to-update-the-build-JDK for instructions."
SLACK_FOOTER: false
MSG_MINIMAL: true

View File

@@ -17,12 +17,13 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
COFFEELIBS_JDK_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
JAVA_VERSION: '22.0.1+8'
COFFEELIBS_JDK: 22
COFFEELIBS_JDK_VERSION: '22.0.1+8-0ppa1'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'fbb22f35951c2e049cc2554dd03c2c56b4f5adc4b2ae9248872f46175ac103d8'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '1982ad168a5e8d7cf4a9458a7d088b4f0552d0ac3f24f23fb88f8bc7e8d69a13'
jobs:
build:
@@ -43,16 +44,16 @@ jobs:
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
run: mvn -B clean package -Plinux -Djavafx.platform=linux -DskipTests
- name: Download OpenJFX jmods
id: download-jmods
run: |
@@ -127,7 +128,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@v3
uses: actions/upload-artifact@v4
with:
name: linux-deb-package
path: |
@@ -141,12 +142,11 @@ jobs:
- name: Publish on PPA
if: inputs.dput
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
# If ref is a tag, also upload to GitHub Releases:
- name: Publish Debian package on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && inputs.dput
env:
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
run: |
artifacts=$(ls | grep cryptomator*.deb)
gh release upload ${{ github.ref_name }} $artifacts
gh release upload ${{ github.ref_name }} $artifacts

18
.github/workflows/dependency-check.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: OWASP Maven Dependency Check
on:
schedule:
- cron: '0 8 * * 0'
workflow_dispatch:
jobs:
check-dependencies:
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@v1
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 22
check-command: 'mvn -B validate -Pdependency-check -Djavafx.platform=linux'
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- name: Get download count of latest releases
id: get-stats
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
const query = `query($owner:String!, $name:String!) {

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@v6
uses: actions/github-script@v7
with:
script: |
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {

88
.github/workflows/flathub.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Create PR for flathub
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag'
required: true
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.tag }}
tarball:
name: Determines tarball url and compute checksum
runs-on: ubuntu-latest
needs: [get-version]
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
outputs:
url: ${{ steps.url.outputs.url}}
sha512: ${{ steps.sha512.outputs.sha512}}
steps:
- 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
echo "url=${URL}" >> "$GITHUB_OUTPUT"
- name: Download source tarball and compute checksum
id: sha512
run: |
curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
echo "sha512=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
flathub:
name: Create PR for flathub
runs-on: ubuntu-latest
needs: [tarball, get-version]
env:
FLATHUB_PR_URL: tbd
steps:
- uses: actions/checkout@v4
with:
repository: 'flathub/org.cryptomator.Cryptomator'
token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
- name: Checkout release branch
run: |
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
- name: Update build file
run: |
sed -i -e 's/VERSION: [0-9]\+\.[0-9]\+\.[0-9]\+.*/VERSION: ${{ needs.get-version.outputs.semVerStr }}/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's/sha512: [0-9A-Za-z_\+-]\{128\} #CRYPTOMATOR/sha512: ${{ needs.tarball.outputs.sha512 }} #CRYPTOMATOR/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's;url: https://github.com/cryptomator/cryptomator/archive/refs/tags/[^[:blank:]]\+;url: ${{ needs.tarball.outputs.url }};g' org.cryptomator.Cryptomator.yaml
- name: Commit and push
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
git config push.autoSetupRemote true
git stage .
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
git push
- name: Create pull request
run: |
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update maven dependencies\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV"
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
if: github.event_name == 'release'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
SLACK_MESSAGE: "See <${{ env.FLATHUB_PR_URL }}|PR> on how to proceed.>."
SLACK_FOOTER: false
MSG_MINIMAL: true

View File

@@ -23,7 +23,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
jobs:
determine-version:
@@ -39,7 +39,7 @@ jobs:
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -50,7 +50,7 @@ jobs:
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="${{ github.event.inputs.version }}"
SEM_VER_STR="${{ inputs.version }}"
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -71,6 +71,6 @@ jobs:
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
echo "type=${TYPE}" >> $GITHUB_OUTPUT
- name: Validate Version
uses: skymatic/semver-validation-action@v2
uses: skymatic/semver-validation-action@v3
with:
version: ${{ steps.versions.outputs.semVerStr }}
version: ${{ steps.versions.outputs.semVerStr }}

View File

@@ -16,7 +16,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: '22.0.1+8'
jobs:
get-version:
@@ -37,19 +37,19 @@ jobs:
output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip'
openjfx-sha: '55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c'
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_osx-x64_bin-jmods.zip'
openjfx-sha: 'e07a11c112abbdebe7c058b44c151e1e475de748671d896aef3d73f32453c248'
- os: [self-hosted, macOS, ARM64]
architecture: aarch64
output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf'
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_osx-aarch64_bin-jmods.zip'
openjfx-sha: '572fce94b9b09d316b960a49e3c2b5d35231ed0463e3b1c4020b8de89783b51d'
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -79,23 +79,24 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,mac -DskipTests
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
cp target/cryptomator-*.jar target/mods
- name: Run jlink
#Remark: no compression is applied for improved build compression later (here dmg)
run: >
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--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
--strip-native-commands
--no-header-files
--no-man-pages
--strip-debug
--compress=1
--compress zip-0
- name: Run jpackage
run: >
${JAVA_HOME}/bin/jpackage
@@ -108,7 +109,7 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2023 Skymatic GmbH"
--copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
@@ -141,7 +142,7 @@ jobs:
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
- name: Generate license for dmg
run: >
mvn -B license:add-third-party
mvn -B -Djavafx.platform=mac license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -174,7 +175,7 @@ jobs:
run: |
echo "Codesigning jdk files..."
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name 'jspawnhelper' -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ \( -name 'jspawnhelper' -o -name 'pauseengine' -o -name 'simengine' \) -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
echo "Codesigning jar contents..."
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
@@ -249,14 +250,16 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: dmg-${{ matrix.output-suffix }}
path: Cryptomator-*.dmg
path: |
Cryptomator-*.dmg
Cryptomator-*.asc
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@v2
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@v8
- uses: actions/stale@v9
with:
days-before-stale: 14
days-before-close: 0

View File

@@ -19,7 +19,7 @@ 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@v1
uses: softprops/action-gh-release@v2
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}

View File

@@ -5,7 +5,7 @@ on:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 22
defaults:
run:
@@ -18,10 +18,10 @@ jobs:
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
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,dependency-check
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage -Djavafx.platform=linux

View File

@@ -10,12 +10,22 @@ defaults:
run:
shell: bash
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 22
jobs:
release-check-precondition:
check-preconditions:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- id: validate-pom-version
name: Validate POM version
run: |
@@ -34,7 +44,22 @@ jobs:
fi
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
run: |
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\"/>" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
exit 1
fi
fi
- name: Cache NVD DB
uses: actions/cache@v4
with:
path: ~/.m2/repository/org/owasp/dependency-check-data/
key: dependency-check-${{ github.run_id }}
restore-keys: |
dependency-check
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
- name: Run org.owasp:dependency-check plugin
id: dependency-check
continue-on-error: true
run: mvn -B verify -Pdependency-check -DskipTests -Djavafx.platform=linux
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
- uses: actions/stale@v9
with:
days-before-stale: 365
days-before-close: 90

View File

@@ -11,13 +11,16 @@ on:
isDebug:
description: 'Build debug version with console output'
type: boolean
default: false
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
JAVA_VERSION: '22.0.1+8'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.1/openjfx-22.0.1_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'de82e53179032a49bec005deb4438e8f261d08c4b58864a5c17e1d87286b09dd'
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
defaults:
run:
@@ -37,9 +40,12 @@ jobs:
LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps:
- name: Upgrade WIX to latest version
run: choco install wixtoolset --version 3.14.1
shell: pwsh
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -71,23 +77,24 @@ jobs:
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,win -DskipTests
run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
- name: Patch target dir
run: |
cp LICENSE.txt target
cp target/cryptomator-*.jar target/mods
- name: Run jlink
#Remark: no compression is applied for improved build compression later (here msi)
run: >
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--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
--strip-native-commands
--no-header-files
--no-man-pages
--strip-debug
--compress=1
--compress zip-0
- name: Change win-console flag if debug is active
if: ${{ inputs.isDebug }}
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
@@ -103,7 +110,7 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2023 Skymatic GmbH"
--copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
@@ -167,7 +174,7 @@ jobs:
& $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"
- name: Codesign
uses: skymatic/code-sign-action@v2
uses: skymatic/code-sign-action@v3
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -192,7 +199,7 @@ jobs:
}
- name: Generate license for MSI
run: >
mvn -B license:add-third-party
mvn -B license:add-third-party "-Djavafx.platform=win"
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
@@ -211,12 +218,12 @@ jobs:
--dest installer
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2023 Skymatic GmbH"
--copyright "(C) 2016 - 2024 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
--win-menu
--win-dir-chooser
--win-shortcut-prompt
--win-update-url "https:\\cryptomator.org"
--win-update-url "https:\\cryptomator.org\downloads"
--win-menu-group Cryptomator
--resource-dir dist/win/resources
--license-file dist/win/resources/license.rtf
@@ -225,7 +232,7 @@ jobs:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Codesign MSI
uses: skymatic/code-sign-action@v2
uses: skymatic/code-sign-action@v3
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -243,22 +250,13 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: msi
path: |
Cryptomator-*.msi
Cryptomator-*.asc
if-no-files-found: error
- name: Publish .msi on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
*.msi
*.asc
build-exe:
name: Build .exe installer
@@ -267,13 +265,13 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Download .msi
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: msi
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
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -281,7 +279,7 @@ jobs:
cache: 'maven'
- name: Generate license for exe
run: >
mvn -B license:add-third-party
mvn -B license:add-third-party "-Djavafx.platform=win"
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources"
@@ -292,8 +290,11 @@ jobs:
shell: pwsh
- name: Download WinFsp
run: |
$winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
shell: pwsh
- name: Download Legacy-WinFsp uninstaller
run: |
curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
shell: pwsh
- name: Compile to wixObj file
run: >
@@ -303,7 +304,7 @@ jobs:
-out dist/win/bundle/
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
-dBundleVendor="Skymatic GmbH"
-dBundleCopyright="(C) 2016 - 2023 Skymatic GmbH"
-dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
-dAboutUrl="https://cryptomator.org"
-dHelpUrl="https://cryptomator.org/contact"
-dUpdateUrl="https://cryptomator.org/downloads/"
@@ -319,7 +320,7 @@ jobs:
-ib installer/unsigned/Cryptomator-Installer.exe
-o tmp/engine.exe
- name: Codesign burn engine
uses: skymatic/code-sign-action@v2
uses: skymatic/code-sign-action@v3
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -333,7 +334,7 @@ jobs:
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
-o installer/Cryptomator-Installer.exe
- name: Codesign EXE
uses: skymatic/code-sign-action@v2
uses: skymatic/code-sign-action@v3
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -351,59 +352,66 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: exe
path: |
Cryptomator-*.exe
Cryptomator-*.asc
if-no-files-found: error
publish:
name: Publish installers to the github release
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
runs-on: ubuntu-latest
needs: [build-msi, build-exe]
outputs:
download-url-msi: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
download-url-exe: ${{ fromJSON(steps.publish.outputs.assets)[1].browser_download_url }}
steps:
- name: Download installers
uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Publish .msi on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
id: publish
uses: softprops/action-gh-release@v2
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
# do not change ordering of filelist, required for correct job output
files: |
Cryptomator-*.exe
Cryptomator-*.asc
*.msi
*.exe
*.asc
allowlist:
name: Anti Virus Allowlisting
if: startsWith(github.ref, 'refs/tags/')
allowlist-msi:
uses: ./.github/workflows/av-whitelist.yml
needs: [publish]
with:
url: ${{ needs.publish.outputs.download-url-msi }}
allowlist-exe:
uses: ./.github/workflows/av-whitelist.yml
needs: [publish]
with:
url: ${{ needs.publish.outputs.download-url-exe }}
notify-winget:
name: Notify for winget-release
if: needs.get-version.outputs.versionType == 'stable'
needs: [publish, get-version]
runs-on: ubuntu-latest
needs: [build-msi, build-exe]
steps:
- name: Download .msi
uses: actions/download-artifact@v3
with:
name: msi
path: msi
- name: Download .exe
uses: actions/download-artifact@v3
with:
name: exe
path: exe
- name: Collect files
run: |
mkdir files
cp msi/*.msi files
cp exe/*.exe files
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
with:
protocol: ftps
server: allowlist.kaspersky-labs.com
port: 990
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
local-dir: files/
- name: Upload to Avast
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
with:
protocol: ftp
server: whitelisting.avast.com
port: 21
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
local-dir: files/
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "MSI of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release to winget>."
SLACK_FOOTER: false
MSG_MINIMAL: true

27
.github/workflows/winget.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Publish MSI to winget-pkgs
on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag'
required: true
jobs:
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Sync winget-pkgs fork
run: |
gh repo sync cryptomator/winget-pkgs -b master --force
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
- name: Submit package
uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: Cryptomator.Cryptomator
version: ${{ inputs.tag }}
release-tag: ${{ inputs.tag }}
installers-regex: '\.msi$'
token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}

22
.idea/compiler.xml generated
View File

@@ -14,31 +14,29 @@
<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.48.1/dagger-compiler-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.49/dagger-compiler-2.49.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.49/dagger-2.49.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.49/dagger-spi-2.49.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/1.9.20-1.0.14/symbol-processing-api-1.9.20-1.0.14.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.20/kotlin-stdlib-1.9.20.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.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$/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" />
</processorPath>
<module name="cryptomator" />
</profile>

2
.idea/misc.xml generated
View File

@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" project-jdk-name="22" 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-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/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" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -30,7 +30,7 @@ Cryptomator is provided free of charge as an open-source project despite the hig
<table>
<tbody>
<tr>
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="40"></a></td>
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="28"></a></td>
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
</tr>
@@ -85,7 +85,7 @@ For more information on the security details visit [cryptomator.org](https://doc
### Dependencies
* JDK 19 (e.g. temurin)
* JDK 21 (e.g. temurin, zulu)
* Maven 3
### Run Maven

View File

@@ -1,4 +1,6 @@
# created during build
# downloaded/created during build
openjfx-jmods.zip
*.jmod
Cryptomator.AppDir
*.AppImage
*.AppImage.zsync

View File

@@ -8,32 +8,62 @@ REVISION_NO=`git rev-list --count HEAD`
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
SEMVER_STR=${VERSION}
MACHINE_TYPE=$(uname -m)
CPU_ARCH=$(uname -p)
if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not supported"; exit 1; fi
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=linux
cp ../../../LICENSE.txt ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods
JAVAFX_VERSION=22.0.1
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256='fbb22f35951c2e049cc2554dd03c2c56b4f5adc4b2ae9248872f46175ac103d8'
if [ "${CPU_ARCH}" = "aarch64" ]; then
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256='1982ad168a5e8d7cf4a9458a7d088b4f0552d0ac3f24f23fb88f8bc7e8d69a13'
fi
# download javaFX jmods
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-21.0.1_linux-${JAVAFX_ARCH}_bin-jmods.zip"
curl -L ${JAVAFX_JMODS_URL} -o openjfx-jmods.zip
echo "${JAVAFX_JMODS_SHA256} openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods
unzip -o -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
exit 1
fi
# add runtime
${JAVA_HOME}/bin/jlink \
--verbose \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=1
--compress zip-0
# create app dir
envsubst '${SEMVER_STR} ${REVISION_NUM}' < ../launcher-gtk2.properties > launcher-gtk2.properties
${JAVA_HOME}/bin/jpackage \
--verbose \
--type app-image \
@@ -46,7 +76,7 @@ ${JAVA_HOME}/bin/jpackage \
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \
@@ -61,7 +91,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources
# transform AppDir
@@ -85,17 +114,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage
/tmp/appimagetool.AppImage \
Cryptomator.AppDir \
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
cryptomator-${SEMVER_STR}-${CPU_ARCH}.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${CPU_ARCH}.AppImage.zsync'
echo ""
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${CPU_ARCH}.AppImage"
echo ""
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir runtime squashfs-root openjfx-jmods; rm /tmp/appimagetool.AppImage openjfx-jmods.zip"
echo ""
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.AppImage"
echo ""

View File

@@ -5,26 +5,27 @@
<metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
<name>Cryptomator</name>
<summary>Multi-platform client-side encryption tool optimized for cloud storages</summary>
<summary>Encryption made easy and optimized for the cloud</summary>
<description>
<p>
Cryptomator provides transparent, client-side encryption for your cloud. Protect your documents from unauthorized
access. Cryptomator is free and open source software, so you can rest assured there are no backdoors.
Cryptomator provides easy-to-use, transparent, client-side encryption for your cloud.
It protects your documents from unauthorized access and prying eyes, while you will still be able to view and edit your documents locally.
By not requiring any registration or account and performing all encryption locally, it gives you back control over your data and ensures your privacy.
Cryptomator is offered for all major platforms (including Android and iOS).
</p>
<p>
Cryptomator encrypts file contents and names using AES. Your passphrase is protected against bruteforcing attempts
using scrypt. Directory structures get obfuscated. The only thing which cannot be encrypted without breaking your
cloud synchronization is the modification date of your files.
Cryptomator encrypts file contents and names using the widespread industry standard AES.
Your passphrase is protected against brute forcing attempts using scrypt.
Additionally, directory structures get obfuscated.
For more info about the Cryptomator encryption scheme, check out the online documentation.
</p>
<p>
Cryptomator is a free and open source software licensed under the GPLv3. This allows anyone to check our code. It
is impossible to introduce backdoors for third parties. Also we cannot hide vulnerabilities. And the best thing
is: There is no need to trust us, as you can control us!
</p>
<p>
Vendor lock-ins are impossible. Even if we decided to stop development: The source code is already cloned by
hundreds of other developers. As you don't need an account, you will never stand in front of locked doors.
Cryptomator is a free and open-source software licensed under the GPLv3.
This allows anyone to check our code.
Thus, it is impossible to introduce backdoors for third parties or to hide vulnerabilities, so you do not need to trust Cryptomator.
Also, vendor lock-ins are impossible.
Even if we decided to stop development: The source code is already cloned by hundreds of other developers and development can be picked up by others.
</p>
</description>
@@ -42,7 +43,7 @@
</provides>
<screenshots>
<screenshot>
<screenshot type="default">
<caption>Light theme</caption>
<image>https://user-images.githubusercontent.com/11858409/156986109-6e58f59c-8b8c-4501-b33b-bb1e33007cea.png</image>
</screenshot>
@@ -52,43 +53,123 @@
</screenshot>
</screenshots>
<branding>
<color type="primary" scheme_preference="light">#EBF5EB</color>
<color type="primary" scheme_preference="dark">#2F4858</color>
</branding>
<url type="homepage">https://cryptomator.org/</url>
<url type="bugtracker">https://github.com/cryptomator/cryptomator/issues/</url>
<url type="donation">https://cryptomator.org/donate</url>
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
<url type="help">https://community.cryptomator.org/</url>
<url type="help">https://docs.cryptomator.org/</url>
<url type="translate">https://translate.cryptomator.org</url>
<developer_name>Skymatic GmbH</developer_name>
<developer id="de.skymatic">
<name>Skymatic GmbH</name>
</developer>
<content_rating type="oars-1.1">
<content_attribute id="social-info">mild</content_attribute> <!-- update checker connects to https://api.cryptomator.org/updates/latestVersion.json -->
</content_rating>
<releases>
<release date="2023-09-20" version="1.10.1"/>
<release date="2023-09-11" version="1.10.0"/>
<release date="2023-08-11" version="1.9.4"/>
<release date="2023-08-07" version="1.9.3"/>
<release date="2023-07-24" version="1.9.2"/>
<release date="2023-06-07" version="1.9.1"/>
<release date="2023-05-30" version="1.9.0"/>
<release date="2023-04-25" version="1.8.0"/>
<release date="2023-04-07" version="1.7.5"/>
<release date="2023-04-05" version="1.7.4"/>
<release date="2023-03-15" version="1.7.3"/>
<release date="2023-03-07" version="1.7.2"/>
<release date="2023-03-03" version="1.7.1"/>
<release date="2023-03-01" version="1.7.0"/>
<release date="2022-12-14" version="1.6.17"/>
<release date="2022-12-06" version="1.6.16"/>
<release date="2022-10-06" version="1.6.15"/>
<release date="2022-08-31" version="1.6.14"/>
<release date="2022-07-27" version="1.6.12"/>
<release date="2022-07-26" version="1.6.11"/>
<release date="2022-05-03" version="1.6.10"/>
<release date="2022-04-27" version="1.6.9"/>
<release date="2022-03-30" version="1.6.8"/>
<release date="2021-12-16" version="1.6.5"/>
<release date="2024-06-26" version="1.13.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.13.0</url>
</release>
<release date="2024-03-27" version="1.12.4">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.4</url>
</release>
<release date="2024-02-27" version="1.12.3">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.3</url>
</release>
<release date="2024-02-09" version="1.12.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.2</url>
</release>
<release date="2024-02-07" version="1.12.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.1</url>
</release>
<release date="2024-02-06" version="1.12.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.0</url>
</release>
<release date="2023-12-05" version="1.11.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.11.1</url>
</release>
<release date="2023-11-08" version="1.11.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.11.0</url>
</release>
<release date="2023-09-20" version="1.10.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.10.1</url>
</release>
<release date="2023-09-11" version="1.10.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.10.0</url>
</release>
<release date="2023-08-11" version="1.9.4">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.9.4</url>
</release>
<release date="2023-08-07" version="1.9.3">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.9.3</url>
</release>
<release date="2023-07-24" version="1.9.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.9.2</url>
</release>
<release date="2023-06-07" version="1.9.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.9.1</url>
</release>
<release date="2023-05-30" version="1.9.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.9.0</url>
</release>
<release date="2023-04-25" version="1.8.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.8.0</url>
</release>
<release date="2023-04-07" version="1.7.5">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.5</url>
</release>
<release date="2023-04-05" version="1.7.4">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.4</url>
</release>
<release date="2023-03-15" version="1.7.3">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.3</url>
</release>
<release date="2023-03-07" version="1.7.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.2</url>
</release>
<release date="2023-03-03" version="1.7.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.1</url>
</release>
<release date="2023-03-01" version="1.7.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.7.0</url>
</release>
<release date="2022-12-14" version="1.6.17">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.17</url>
</release>
<release date="2022-12-06" version="1.6.16">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.16</url>
</release>
<release date="2022-10-06" version="1.6.15">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.15</url>
</release>
<release date="2022-08-31" version="1.6.14">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.14</url>
</release>
<release date="2022-07-27" version="1.6.12">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.12</url>
</release>
<release date="2022-07-26" version="1.6.11">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.11</url>
</release>
<release date="2022-05-03" version="1.6.10">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.10</url>
</release>
<release date="2022-04-27" version="1.6.9">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.9</url>
</release>
<release date="2022-03-30" version="1.6.8">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.8</url>
</release>
<release date="2021-12-16" version="1.6.5">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.6.5</url>
</release>
</releases>
</component>

View File

@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-21, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), coffeelibs-jdk-22 (>= 22.0.1+8-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git

View File

@@ -4,11 +4,11 @@ Upstream-Contact: Cryptomator <info@cryptomator.org>
Source: https://cryptomator.org
Files: *
Copyright: 2016-2023 Skymatic GmbH
Copyright: 2016-2024 Skymatic GmbH
License: GPL-3+
Files: debian/org.cryptomator.Cryptomator.appdata.xml
Copyright: 2016-2023 Skymatic GmbH
Copyright: 2016-2024 Skymatic GmbH
License: FSFAP
License: GPL-3+

View File

@@ -4,7 +4,7 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
JAVA_HOME = /usr/lib/jvm/java-22-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
@@ -24,15 +24,16 @@ override_dh_auto_clean:
override_dh_auto_build:
mkdir resources
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
# Remark: no compression is applied for improved build compression later (here deb)
$(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.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--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 \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=2
--compress zip-0
$(JAVA_HOME)/bin/jpackage \
--type app-image \
--runtime-image runtime \
@@ -44,7 +45,7 @@ override_dh_auto_build:
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \

View File

@@ -1,14 +0,0 @@
java-options=-Xss5m \
-Xmx256m \
--enable-preview \
--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64 \
-Dfile.encoding=\"utf-8\" \
-Dcryptomator.appVersion=\"${SEMVER_STR}\" \
-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\" \
-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\" \
-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\" \
-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\" \
-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\" \
-Dcryptomator.showTrayIcon=false \
-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\" \
-Djdk.gtk.version=2

View File

@@ -1,6 +1,8 @@
# created during build
# downloaded/created during build
Cryptomator.app/
runtime/
dmg/
*.dmg
license.rtf
license.rtf
openjfx-jmods.zip
*.jmod

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

@@ -21,7 +21,7 @@ rm -rf runtime dmg *.app *.dmg
# set variables
APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2023"
COPYRIGHT_YEARS="2016 - 2024"
PACKAGE_IDENTIFIER="org.cryptomator"
MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
@@ -29,13 +29,17 @@ 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"
ARCH="undefined"
JAVAFX_VERISON=22.0.1
JAVAFX_ARCH="undefined"
JAVAFX_JMODS_SHA256="undefined"
if [ "$(machine)" = "arm64e" ]; then
ARCH="aarch64"
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256="572fce94b9b09d316b960a49e3c2b5d35231ed0463e3b1c4020b8de89783b51d"
else
ARCH="x64"
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256="e07a11c112abbdebe7c058b44c151e1e475de748671d896aef3d73f32453c248"
fi
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-${ARCH}_bin-jmods.zip"
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
# check preconditions
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
@@ -47,7 +51,8 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
fi
# download and check jmods
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
curl -L ${JAVAFX_JMODS_URL} -o openjfx-jmods.zip
echo "${JAVAFX_JMODS_SHA256} openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods/
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
@@ -63,7 +68,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
fi
# compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
mvn -B -Djavafx.platform=mac -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
@@ -71,12 +76,12 @@ cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
--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 \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=1
--compress zip-0
# create app dir
${JAVA_HOME}/bin/jpackage \
@@ -118,7 +123,7 @@ 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
# generate license
mvn -B -f../../../pom.xml license:add-third-party \
mvn -B -Djavafx.platform=mac -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \

View File

@@ -17,7 +17,7 @@
\f1\b0 \
\
\f0\b \'a9 2016 \'96 2023 Skymatic GmbH
\f0\b \'a9 2016 \'96 2024 Skymatic GmbH
\f1\b0 \
\
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<string>11</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleAllowMixedLocalizations</key>

3
dist/win/.gitignore vendored
View File

@@ -4,4 +4,7 @@ installer
*.wixobj
*.pdb
*.msi
*.exe
*.jmod
resources/jfxJmods.zip
license.rtf

42
dist/win/build.ps1 vendored
View File

@@ -41,7 +41,7 @@ Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin -Djavafx.platform=win
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime
@@ -51,35 +51,36 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
}
## download jfx jmods
$jmodsVersion='20.0.2'
$jmodsUrl = "https://download2.gluonhq.com/openjfx/${jmodsVersion}/openjfx-${jmodsVersion}_windows-x64_bin-jmods.zip"
$jfxJmodsChecksum = '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
$jfxJmodsZip = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $jfxJmodsZip) ) {
Write-Output "Downloading ${jmodsUrl}..."
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
$javaFxVersion='22.0.1'
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
$javaFxJmodsSHA256 = 'de82e53179032a49bec005deb4438e8f261d08c4b58864a5c17e1d87286b09dd'
$javaFxJmods = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $javaFxJmods) ) {
Write-Output "Downloading ${javaFxJmodsUrl}..."
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
}
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
$jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash
if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
, actual: $jmodsChecksumActual"
exit 1;
}
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
## create custom runtime
& "$Env:JAVA_HOME\bin\jlink" `
--verbose `
--output runtime `
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,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,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--strip-native-commands `
--no-header-files `
--no-man-pages `
--strip-debug `
--compress=1
--compress "zip-0" #do not compress to have improved msi compression
$appPath = ".\$AppName"
if ($clean -and (Test-Path -Path $appPath)) {
@@ -121,7 +122,7 @@ if ($clean -and (Test-Path -Path $appPath)) {
--icon resources/$AppName.ico
#Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
&mvn -B -f $buildDir/../../pom.xml license:add-third-party -Djavafx.platform=win `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\resources\" `
@@ -166,7 +167,7 @@ $Env:JP_WIXHELPER_DIR = "."
--file-associations resources/FAvaultFile.properties
#Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
&mvn -B -f $buildDir/../../pom.xml license:add-third-party -Djavafx.platform=win `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
@@ -176,10 +177,15 @@ $Env:JP_WIXHELPER_DIR = "."
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# download Winfsp
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
# download legacy-winfsp uninstaller
$winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
Write-Output "Downloading ${winfspUninstaller}..."
Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
# copy MSI to bundle resources
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0"?>
<!-- For Built in variables, see https://wixtoolset.org/docs/tools/burn/builtin-variables/-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html-->
<!-- Attributes explicitly not used:
@@ -10,21 +11,10 @@
AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico">
<!-- detect outdated WinFsp installations -->
<?include "resources\winFspMetaData.wxi" ?>
<util:ProductSearch
Variable="InstalledWinFspVersion"
Variable="InstalledLegacyWinFspVersion"
Result="version"
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"
/>
<!-- Note: The bundle engine takes the Message format literaly -->
<bal:Condition Message=
"The WinFsp driver used by Cryptomator is outdated and must be removed before the installation.
1. Open the view of installed apps
2. Search for &quot;WinFsp&quot;
3. Uninstall the listed application
4. Reboot your device
5. Restart this installer">(InstalledWinFspVersion = v0.0.0.0) OR ($(var.BundledWinFspVersion) &lt;= InstalledWinFspVersion)</bal:Condition>
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"/>
<!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/-->
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense">
@@ -36,26 +26,41 @@
SuppressOptionsUI="yes"
ThemeFile="bundle\customBootstrapperTheme.xml"
LocalizationFile="bundle\customBootstrapperTheme.wxl"
LogoFile="bundle\resources\logo.png"
/>
LogoFile="bundle\resources\logo.png"/>
<Payload SourceFile="bundle\resources\logoSide.png" />
</BootstrapperApplicationRef>
<Chain>
<ExePackage Cache="yes" PerMachine="yes" Permanent="no"
SourceFile="resources\winfsp-uninstaller.exe"
DisplayName="Removing outdated WinFsp Driver"
Description="Executable to remove old winfsp"
DetectCondition="false"
InstallCondition="(InstalledLegacyWinFspVersion &lt;&gt; v0.0.0.0) AND ((WixBundleAction = 7) OR (WixBundleAction = 5))">
<CommandLine Condition="WixBundleUILevel &lt;= 3" InstallArgument="-q -l &quot;[WixBundleLog].winfsp-uninstaller.log&quot;" RepairArgument="-q" UninstallArgument="-s" />
<!-- XML allows line breaks in attributes, hence keep the line breaks here -->
<CommandLine Condition="WixBundleUILevel &gt; 3" InstallArgument="-l &quot;[WixBundleLog].winfsp-uninstaller.log&quot; -t &quot;Cryptomator Installer&quot; -m &quot;Cryptomator requires a newer version of the WinFsp driver. The installer will now uninstall WinFsp, possibly reboot, and afterwards proceed with the installation.
Do you want to continue?&quot;" RepairArgument="-q" UninstallArgument="-s" />
<ExitCode Behavior="success" Value="0"/>
<ExitCode Behavior="success" Value="1"/>
<ExitCode Behavior="error" Value="2"/>
<ExitCode Behavior="error" Value="3"/>
<ExitCode Behavior="forceReboot" Value="4"/>
<ExitCode Behavior="success" Value="5"/>
</ExePackage>
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html-->
<MsiPackage
SourceFile="resources\Cryptomator.msi"
CacheId="cryptomator-bundle-cryptomator"
DisplayInternalUI="no"
Visible="no"
/>
Visible="no"/>
<MsiPackage
SourceFile="resources\winfsp.msi"
CacheId="cryptomator-bundle-winfsp"
Visible="yes"
DisplayInternalUI="no"
Vital="no"
Permanent="yes"
/>
Permanent="yes"/>
</Chain>
</Bundle>
</Wix>

View File

@@ -10,7 +10,7 @@
\vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
\par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
<?define BundledWinFspVersion="v1.12.22339" ?>
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
</Include>

View File

@@ -1,5 +0,0 @@
@echo off
:: see comments in file ./version170-migrate-settings.ps1
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1

View File

@@ -1,35 +0,0 @@
# This script migrates Cryptomator settings for all local users on Windows in case the users uses custom directories as mountpoint
# See also https://github.com/cryptomator/cryptomator/pull/2654.
#
# TODO: This script should be evaluated in a yearly interval if it is still needed and if not, should be removed
#
#Requires -RunAsAdministrator
#Get all active, local user profiles
$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
Get-ChildItem $profileList | ForEach-Object {
$profilePath = $_.GetValue("ProfileImagePath")
$settingsPath = "$profilePath\AppData\Roaming\Cryptomator\settings.json"
if(!(Test-Path -Path $settingsPath -PathType Leaf)) {
#No settings file, nothing to do.
return;
}
$settings = Get-Content -Path $settingsPath | ConvertFrom-Json
if($settings.preferredVolumeImpl -ne "FUSE") {
#Fuse not used, nothing to do
return;
}
#check if customMountPoints are used
$atLeastOneCustomPath = $false;
foreach ($vault in $settings.directories){
$atLeastOneCustomPath = $atLeastOneCustomPath -or ($vault.useCustomMountPath -eq "True")
}
#if so, use WinFsp Local Drive
if( $atLeastOneCustomPath ) {
Add-Member -Force -InputObject $settings -Name "mountService" -Value "org.cryptomator.frontend.fuse.mount.WinFspMountProvider" -MemberType NoteProperty
$newSettings = $settings | Select-Object * -ExcludeProperty "preferredVolumeImpl"
ConvertTo-Json $newSettings | Set-Content -Path $settingsPath
}
}

View File

@@ -10,7 +10,7 @@
\vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
\par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par

View File

@@ -139,11 +139,6 @@
Sequence="execute" Before="PatchWebDAV" />
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
<SetProperty Id="V170MigrateSettings" Value="&quot;[INSTALLDIR]version170-migrate-settings.bat&quot;"
Sequence="execute" Before="V170MigrateSettings" />
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
<util:CloseApplication
@@ -195,7 +190,6 @@
<RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to WixCloseApplications -->
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence>
<InstallUISequence>

80
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.11.0</version>
<version>1.13.0</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,56 +26,60 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>21</project.jdk.version>
<project.jdk.version>22</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.6.7</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>4.0.0-beta4</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
<cryptomator.cryptofs.version>2.6.9</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.3.1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.5</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.4</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.4.5</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.0.0</cryptomator.fuse.version>
<cryptomator.webdav.version>2.0.6</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<commons-lang3.version>3.13.0</commons-lang3.version>
<dagger.version>2.48.1</dagger.version>
<commons-lang3.version>3.14.0</commons-lang3.version>
<dagger.version>2.51.1</dagger.version>
<easybind.version>2.2</easybind.version>
<guava.version>32.1.3-jre</guava.version>
<jackson.version>2.15.3</jackson.version>
<javafx.version>20.0.2</javafx.version>
<guava.version>33.2.1-jre</guava.version>
<jackson.version>2.17.1</jackson.version>
<javafx.version>22.0.1</javafx.version>
<jwt.version>4.4.0</jwt.version>
<nimbus-jose.version>9.37</nimbus-jose.version>
<logback.version>1.4.11</logback.version>
<slf4j.version>2.0.9</slf4j.version>
<tinyoauth2.version>0.7.0</tinyoauth2.version>
<zxcvbn.version>1.8.2</zxcvbn.version>
<nimbus-jose.version>9.37.3</nimbus-jose.version>
<logback.version>1.5.6</logback.version>
<slf4j.version>2.0.13</slf4j.version>
<tinyoauth2.version>0.8.0</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<mockito.version>5.6.0</mockito.version>
<junit.jupiter.version>5.10.2</junit.jupiter.version>
<mockito.version>5.12.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>24.0.1</jetbrains.annotations.version>
<dependency-check.version>8.4.0</dependency-check.version>
<jacoco.version>0.8.11</jacoco.version>
<license-generator.version>2.2.0</license-generator.version>
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
<dependency-check.version>9.2.0</dependency-check.version>
<jacoco.version>0.8.12</jacoco.version>
<license-generator.version>2.4.0</license-generator.version>
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
<mvn-compiler.version>3.11.0</mvn-compiler.version>
<mvn-compiler.version>3.13.0</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.6.0</mvn-dependency.version>
<mvn-surefire.version>3.1.2</mvn-surefire.version>
<mvn-jar.version>3.3.0</mvn-jar.version>
<mvn-dependency.version>3.7.0</mvn-dependency.version>
<mvn-surefire.version>3.3.0</mvn-surefire.version>
<mvn-jar.version>3.4.1</mvn-jar.version>
</properties>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
@@ -86,11 +90,6 @@
<artifactId>fuse-nio-adapter</artifactId>
<version>${cryptomator.fuse.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>dokany-nio-adapter</artifactId>
<version>${cryptomator.dokany.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter</artifactId>
@@ -164,11 +163,18 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>
@@ -460,17 +466,19 @@
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<cveValidForHours>24</cveValidForHours>
<nvdValidForHours>24</nvdValidForHours>
<failBuildOnCVSS>0</failBuildOnCVSS>
<skipTestScope>true</skipTestScope>
<detail>true</detail>
<suppressionFile>suppression.xml</suppressionFile>
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
<phase>validate</phase>
</execution>
</executions>
</plugin>

View File

@@ -21,7 +21,6 @@ open module org.cryptomator.desktop {
requires org.cryptomator.cryptolib;
requires org.cryptomator.cryptofs;
requires org.cryptomator.frontend.dokany;
requires org.cryptomator.frontend.fuse;
requires org.cryptomator.frontend.webdav;
requires org.cryptomator.integrations.api;
@@ -32,13 +31,13 @@ open module org.cryptomator.desktop {
requires javafx.graphics;
requires javafx.controls;
requires javafx.fxml;
requires jdk.crypto.ec;
// 3rd party:
requires ch.qos.logback.classic;
requires ch.qos.logback.core;
requires com.auth0.jwt;
requires com.google.common;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind;

View File

@@ -5,10 +5,8 @@
*******************************************************************************/
package org.cryptomator.common;
import com.tobiasdiez.easybind.EasyBind;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.keychain.KeychainModule;
import org.cryptomator.common.mount.MountModule;
import org.cryptomator.common.settings.Settings;
@@ -22,8 +20,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.beans.value.ObservableValue;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
@@ -136,13 +132,4 @@ public abstract class CommonsModule {
LOG.error("Uncaught exception in " + thread.getName(), throwable);
}
@Provides
@Singleton
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
return settings.port.map(port -> {
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
return InetSocketAddress.createUnresolved(host, settings.port.intValue());
});
}
}

View File

@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SubstitutingProperties extends PropertiesDecorator {
@@ -58,7 +59,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
return "";
} else {
return val.replace("\\", "\\\\");
return Matcher.quoteReplacement(val);
}
}

View File

@@ -1,6 +0,0 @@
package org.cryptomator.common.mount;
import org.cryptomator.integrations.mount.MountService;
public record ActualMountService(MountService service, boolean isDesired) {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.common.mount;
import org.cryptomator.integrations.mount.MountFailedException;
/**
* Thrown by {@link Mounter} to indicate that the selected mount service can not be used
* due to incompatibilities with a different mount service that is already in use.
*/
public class ConflictingMountServiceException extends MountFailedException {
public ConflictingMountServiceException(String msg) {
super(msg);
}
}

View File

@@ -4,21 +4,18 @@ import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountService;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.beans.value.ObservableValue;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Module
public class MountModule {
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
@Provides
@Singleton
static List<MountService> provideSupportedMountServices() {
@@ -27,46 +24,18 @@ public class MountModule {
@Provides
@Singleton
@Named("FUPFMS")
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
return new AtomicReference<>(null);
static ObservableValue<MountService> provideDefaultMountService(List<MountService> mountProviders, Settings settings) {
var fallbackProvider = mountProviders.stream().findFirst().get(); //there should always be a mount provider, at least webDAV
return ObservableUtil.mapWithDefault(settings.mountService, //
serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), //
fallbackProvider);
}
@Provides
@Singleton
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
desiredServiceImpl -> { //
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
var targetedService = serviceFromSettings.orElse(fallbackProvider);
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
}, //
() -> { //
return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
});
return observableMountService;
@Named("usedMountServices")
static Set<MountService> provideSetOfUsedMountServices() {
return ConcurrentHashMap.newKeySet();
}
//see https://github.com/cryptomator/cryptomator/issues/2786
private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) {
//set the first used problematic fuse service if applicable
var targetIsProblematicFuse = isProblematicFuseService(targetedService);
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
firstUsedProblematicFuseMountService.set(targetedService);
}
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
return new ActualMountService(formerSelectedMountService.get(), false);
} else {
formerSelectedMountService.set(targetedService);
return new ActualMountService(targetedService, isDesired);
}
}
public static boolean isProblematicFuseService(MountService service) {
return problematicFuseMountServices.contains(service.getClass().getName());
}
}
}

View File

@@ -7,13 +7,19 @@ import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountBuilder;
import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.MountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.beans.value.ObservableValue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
@@ -24,24 +30,41 @@ import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
@Singleton
public class Mounter {
private final Settings settings;
private static final Logger LOG = LoggerFactory.getLogger(Mounter.class);
// mount providers (key) can not be used if any of the conflicting mount providers (values) are already in use
private static final Map<String, Set<String>> CONFLICTING_MOUNT_SERVICES = Map.of(
"org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.FuseTMountProvider"),
"org.cryptomator.frontend.fuse.mount.FuseTMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider")
);
private final Environment env;
private final Settings settings;
private final WindowsDriveLetters driveLetters;
private final ObservableValue<ActualMountService> mountServiceObservable;
private final List<MountService> mountProviders;
private final Set<MountService> usedMountServices;
private final ObservableValue<MountService> defaultMountService;
@Inject
public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue<ActualMountService> mountServiceObservable) {
this.settings = settings;
public Mounter(Environment env, //
Settings settings, //
WindowsDriveLetters driveLetters, //
List<MountService> mountProviders, //
@Named("usedMountServices") Set<MountService> usedMountServices, //
ObservableValue<MountService> defaultMountService) {
this.env = env;
this.settings = settings;
this.driveLetters = driveLetters;
this.mountServiceObservable = mountServiceObservable;
this.mountProviders = mountProviders;
this.usedMountServices = usedMountServices;
this.defaultMountService = defaultMountService;
}
private class SettledMounter {
private MountService service;
private MountBuilder builder;
private VaultSettings vaultSettings;
private final MountService service;
private final MountBuilder builder;
private final VaultSettings vaultSettings;
public SettledMounter(MountService service, MountBuilder builder, VaultSettings vaultSettings) {
this.service = service;
@@ -53,8 +76,13 @@ public class Mounter {
for (var capability : service.capabilities()) {
switch (capability) {
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
case LOOPBACK_PORT ->
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
case LOOPBACK_PORT -> {
if (vaultSettings.mountService.getValue() == null) {
builder.setLoopbackPort(settings.port.get());
} else {
builder.setLoopbackPort(vaultSettings.port.get());
}
}
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
case MOUNT_FLAGS -> {
@@ -103,9 +131,7 @@ public class Mounter {
}
} else if (canMountToParent && !canMountToDir) {
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
cleanup = () -> {
MountWithinParentUtil.cleanup(userChosenMountPoint);
};
cleanup = () -> MountWithinParentUtil.cleanup(userChosenMountPoint);
}
try {
builder.setMountpoint(userChosenMountPoint);
@@ -131,13 +157,26 @@ public class Mounter {
}
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
var mountService = this.mountServiceObservable.getValue().service();
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
if (isConflictingMountService(mountService)) {
var msg = STR."\{mountService.getClass()} unavailable due to conflict with either of \{CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName())}";
throw new ConflictingMountServiceException(msg);
}
usedMountServices.add(mountService);
var builder = mountService.forFileSystem(cryptoFsRoot);
var internal = new SettledMounter(mountService, builder, vaultSettings);
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);
}
public boolean isConflictingMountService(MountService service) {
var conflictingServices = CONFLICTING_MOUNT_SERVICES.getOrDefault(service.getClass().getName(), Set.of());
return usedMountServices.stream().map(MountService::getClass).map(Class::getName).anyMatch(conflictingServices::contains);
}
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {
}

View File

@@ -25,6 +25,7 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
import java.time.Instant;
import java.util.function.Consumer;
public class Settings {
@@ -44,8 +45,7 @@ public class Settings {
static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01";
public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
public final ObservableList<VaultSettings> directories;
public final BooleanProperty askedForUpdateCheck;
public final BooleanProperty checkForUpdates;
@@ -67,7 +67,7 @@ public class Settings {
public final IntegerProperty windowHeight;
public final StringProperty language;
public final StringProperty mountService;
public final StringProperty lastUpdateCheck;
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
private Consumer<Settings> saveCmd;
@@ -104,7 +104,7 @@ public class Settings {
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
this.language = new SimpleStringProperty(this, "language", json.language);
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -131,7 +131,7 @@ public class Settings {
windowHeight.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
lastUpdateCheck.addListener(this::somethingChanged);
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -185,7 +185,7 @@ public class Settings {
json.windowHeight = windowHeight.get();
json.language = language.get();
json.mountService = mountService.get();
json.lastUpdateCheck = lastUpdateCheck.get();
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
return json;
}

View File

@@ -1,9 +1,11 @@
package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -80,7 +82,8 @@ class SettingsJson {
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String preferredVolumeImpl;
@JsonProperty("lastUpdateCheck")
String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;
@JsonProperty("lastSuccessfulUpdateCheck")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
Instant lastSuccessfulUpdateCheck = Settings.DEFAULT_TIMESTAMP;
}

View File

@@ -10,6 +10,7 @@ package org.cryptomator.common.settings;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Suppliers;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
@@ -36,7 +37,7 @@ import java.util.stream.Stream;
@Singleton
public class SettingsProvider implements Supplier<Settings> {
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true).registerModule(new JavaTimeModule());
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final long SAVE_DELAY_MS = 1000;

View File

@@ -8,7 +8,6 @@ package org.cryptomator.common.settings;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import javafx.beans.Observable;
@@ -40,6 +39,7 @@ public class VaultSettings {
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
static final int DEFAULT_PORT = 42427;
private static final Random RNG = new Random();
@@ -56,6 +56,8 @@ public class VaultSettings {
public final IntegerProperty autoLockIdleSeconds;
public final ObjectProperty<Path> mountPoint;
public final StringExpression mountName;
public final StringProperty mountService;
public final IntegerProperty port;
VaultSettings(VaultSettingsJson json) {
this.id = json.id;
@@ -70,6 +72,8 @@ public class VaultSettings {
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.port = new SimpleIntegerProperty(this, "port", json.port);
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name;
@@ -95,7 +99,7 @@ public class VaultSettings {
}
Observable[] observables() {
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
}
public static VaultSettings withRandomId() {
@@ -124,6 +128,8 @@ public class VaultSettings {
json.autoLockWhenIdle = autoLockWhenIdle.get();
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
json.mountPoint = mountPoint.map(Path::toString).getValue();
json.mountService = mountService.get();
json.port = port.get();
return json;
}

View File

@@ -45,6 +45,12 @@ class VaultSettingsJson {
@JsonProperty("autoLockIdleSeconds")
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
@JsonProperty("mountService")
String mountService;
@JsonProperty("port")
int port = VaultSettings.DEFAULT_PORT;
@Deprecated(since = "1.7.0")
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String winDriveLetter;

View File

@@ -11,7 +11,6 @@ package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.common.mount.Mounter;
import org.cryptomator.common.mount.WindowsDriveLetters;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
@@ -73,7 +72,13 @@ public class Vault {
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@Inject
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
Vault(VaultSettings vaultSettings, //
VaultConfigCache configCache, //
AtomicReference<CryptoFileSystem> cryptoFileSystem, //
VaultState state, //
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
VaultStats stats, //
Mounter mounter) {
this.vaultSettings = vaultSettings;
this.configCache = configCache;
this.cryptoFileSystem = cryptoFileSystem;

View File

@@ -13,31 +13,37 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
@AddVaultWizardScoped
public class CreateNewVaultLocationController implements FxController {
@@ -49,19 +55,23 @@ public class CreateNewVaultLocationController implements FxController {
private final Stage window;
private final Lazy<Scene> chooseNameScene;
private final Lazy<Scene> chooseExpertSettingsScene;
private final List<RadioButton> locationPresetBtns;
private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName;
private final ExecutorService backgroundExecutor;
private final ResourceBundle resourceBundle;
private final ObservableValue<VaultPathStatus> vaultPathStatus;
private final ObservableValue<Boolean> validVaultPath;
private final BooleanProperty usePresetPath;
private final BooleanProperty loadingPresetLocations = new SimpleBooleanProperty(false);
private final ObservableList<Node> radioButtons;
private final ObservableList<Node> sortedRadioButtons;
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
//FXML
public ToggleGroup locationPresetsToggler;
public VBox radioButtonVBox;
public HBox customLocationRadioBtn;
public RadioButton customRadioButton;
public Label locationStatusLabel;
public FontAwesome5IconView goodLocation;
@@ -73,25 +83,20 @@ public class CreateNewVaultLocationController implements FxController {
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
ObjectProperty<Path> vaultPath, //
@Named("vaultName") StringProperty vaultName, //
ResourceBundle resourceBundle) {
ExecutorService backgroundExecutor, ResourceBundle resourceBundle) {
this.window = window;
this.chooseNameScene = chooseNameScene;
this.chooseExpertSettingsScene = chooseExpertSettingsScene;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
this.backgroundExecutor = backgroundExecutor;
this.resourceBundle = resourceBundle;
this.vaultPathStatus = ObservableUtil.mapWithDefault(vaultPath, this::validatePath, new VaultPathStatus(false, "error.message"));
this.validVaultPath = ObservableUtil.mapWithDefault(vaultPathStatus, VaultPathStatus::valid, false);
this.vaultPathStatus.addListener(this::updateStatusLabel);
this.usePresetPath = new SimpleBooleanProperty();
this.locationPresetBtns = LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
.flatMap(LocationPresetsProvider::getLocations) //
.sorted(Comparator.comparing(LocationPreset::name)) //
.map(preset -> { //
var btn = new RadioButton(preset.name());
btn.setUserData(preset.path());
return btn;
}).toList();
this.radioButtons = FXCollections.observableArrayList();
this.sortedRadioButtons = radioButtons.sorted(this::compareLocationPresets);
}
private VaultPathStatus validatePath(Path p) throws NullPointerException {
@@ -137,12 +142,45 @@ public class CreateNewVaultLocationController implements FxController {
@FXML
public void initialize() {
radioButtonVBox.getChildren().addAll(1, locationPresetBtns); //first item is the list header
locationPresetsToggler.getToggles().addAll(locationPresetBtns);
var task = backgroundExecutor.submit(this::loadLocationPresets);
window.addEventHandler(WindowEvent.WINDOW_HIDING, _ -> task.cancel(true));
locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
radioButtons.add(customLocationRadioBtn);
Bindings.bindContent(radioButtonVBox.getChildren(), sortedRadioButtons); //to prevent garbage collection of the binding, we bind explicitly to the sorted list
}
private void loadLocationPresets() {
Platform.runLater(() -> loadingPresetLocations.set(true));
try {
LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
.flatMap(LocationPresetsProvider::getLocations) //we do not use sorted(), because it evaluates the stream elements, blocking until all elements are gathered
.forEach(this::createRadioButtonFor);
} finally {
Platform.runLater(() -> loadingPresetLocations.set(false));
}
}
private void createRadioButtonFor(LocationPreset preset) {
Platform.runLater(() -> {
var btn = new RadioButton(preset.name());
btn.setUserData(preset.path());
radioButtons.add(btn);
locationPresetsToggler.getToggles().add(btn);
});
}
private int compareLocationPresets(Node left, Node right) {
if (customLocationRadioBtn.getId().equals(left.getId())) {
return 1;
} else if (customLocationRadioBtn.getId().equals(right.getId())) {
return -1;
} else {
return ((RadioButton) left).getText().compareToIgnoreCase(((RadioButton) right).getText());
}
}
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath);
vaultPath.set(storagePath.resolve(vaultName.get()));
@@ -197,7 +235,15 @@ public class CreateNewVaultLocationController implements FxController {
}
public boolean isValidVaultPath() {
return validVaultPath.getValue();
return Boolean.TRUE.equals(validVaultPath.getValue());
}
public boolean isLoadingPresetLocations() {
return loadingPresetLocations.getValue();
}
public BooleanProperty loadingPresetLocationsProperty() {
return loadingPresetLocations;
}
public BooleanProperty usePresetPathProperty() {

View File

@@ -12,6 +12,7 @@ public enum FxmlFile {
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
DOKANY_SUPPORT_END("/fxml/dokany_support_end.fxml"), //
ERROR("/fxml/error.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
HEALTH_START("/fxml/health_start.fxml"), //
@@ -21,10 +22,13 @@ public enum FxmlFile {
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
HUB_LEGACY_REGISTER_DEVICE("/fxml/hub_legacy_register_device.fxml"), //
HUB_LEGACY_REGISTER_SUCCESS("/fxml/hub_legacy_register_success.fxml"), //
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
HUB_REGISTER_DEVICE_ALREADY_EXISTS("/fxml/hub_register_device_already_exists.fxml"), //
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
HUB_SETUP_DEVICE("/fxml/hub_setup_device.fxml"), //
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //
MAIN_WINDOW("/fxml/main_window.fxml"), //
@@ -42,8 +46,10 @@ public enum FxmlFile {
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
SHARE_VAULT("/fxml/share_vault.fxml"), //
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //

View File

@@ -47,12 +47,14 @@ public enum FontAwesome5Icon {
QUESTION_CIRCLE("\uf059"), //
REDO("\uF01E"), //
SEARCH("\uF002"), //
SHARE("\uF064"), //
SPINNER("\uF110"), //
STETHOSCOPE("\uF0f1"), //
SYNC("\uF021"), //
TIMES("\uF00D"), //
TRASH("\uF1F8"), //
UNLINK("\uf127"), //
USER_COG("\uf4fe"), //
WRENCH("\uF0AD"), //
WINDOW_MINIMIZE("\uF2D1"), //
;

View File

@@ -0,0 +1,34 @@
package org.cryptomator.ui.dokanysupportend;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
@DokanySupportEndScoped
@Subcomponent(modules = {DokanySupportEndModule.class})
public interface DokanySupportEndComponent {
@DokanySupportEndWindow
Stage window();
@FxmlScene(FxmlFile.DOKANY_SUPPORT_END)
Lazy<Scene> dokanySupportEndScene();
default void showDokanySupportEndWindow() {
Stage stage = window();
stage.setScene(dokanySupportEndScene().get());
stage.sizeToScene();
stage.show();
}
@Subcomponent.Factory
interface Factory {
DokanySupportEndComponent create();
}
}

View File

@@ -0,0 +1,34 @@
package org.cryptomator.ui.dokanysupportend;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@DokanySupportEndScoped
public class DokanySupportEndController implements FxController {
private final Stage window;
private final FxApplicationWindows applicationWindows;
@Inject
DokanySupportEndController(@DokanySupportEndWindow Stage window, FxApplicationWindows applicationWindows) {
this.window = window;
this.applicationWindows = applicationWindows;
}
@FXML
public void close() {
window.close();
}
public void openVolumePreferences() {
applicationWindows.showPreferencesWindow(SelectedPreferencesTab.VOLUME);
window.close();
}
}

View File

@@ -0,0 +1,57 @@
package org.cryptomator.ui.dokanysupportend;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class DokanySupportEndModule {
@Provides
@DokanySupportEndWindow
@DokanySupportEndScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@DokanySupportEndWindow
@DokanySupportEndScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("dokanySupportEnd.title"));
stage.setMinWidth(500);
stage.setMinHeight(100);
stage.initModality(Modality.APPLICATION_MODAL);
return stage;
}
@Provides
@FxmlScene(FxmlFile.DOKANY_SUPPORT_END)
@DokanySupportEndScoped
static Scene provideDokanySupportEndScene(@DokanySupportEndWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.DOKANY_SUPPORT_END);
}
@Binds
@IntoMap
@FxControllerKey(DokanySupportEndController.class)
abstract FxController bindDokanySupportEndController(DokanySupportEndController controller);
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.dokanysupportend;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface DokanySupportEndScoped {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.dokanysupportend;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface DokanySupportEndWindow {
}

View File

@@ -75,6 +75,7 @@ public class ErrorController implements FxController {
private final BooleanExpression errorSolutionFound = matchingErrorDiscussion.isNotNull();
private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty();
private final BooleanProperty askedForLookupDatabasePermission = new SimpleBooleanProperty();
private final boolean formerSceneWasResizable;
@Inject
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) {
@@ -85,12 +86,14 @@ public class ErrorController implements FxController {
this.window = window;
this.environment = environment;
this.executorService = executorService;
this.formerSceneWasResizable = window.isResizable();
}
@FXML
public void back() {
if (previousScene != null) {
window.setScene(previousScene);
window.setResizable(formerSceneWasResizable);
}
}

View File

@@ -3,6 +3,7 @@ package org.cryptomator.ui.fxapp;
import dagger.Lazy;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,7 +76,27 @@ public class FxApplication {
appWindows.checkAndShowUpdateReminderWindow();
}
migrateAndInformDokanyRemoval();
launchEventHandler.startHandlingLaunchEvents();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
}
private void migrateAndInformDokanyRemoval() {
var dokanyProviderId = "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
boolean dokanyFound = false;
if (settings.mountService.getValueSafe().equals(dokanyProviderId)) {
dokanyFound = true;
settings.mountService.set(null);
}
for (VaultSettings vaultSettings : settings.directories) {
if (vaultSettings.mountService.getValueSafe().equals(dokanyProviderId)) {
dokanyFound = true;
vaultSettings.mountService.set(null);
}
}
if (dokanyFound) {
appWindows.showDokanySupportEndWindow();
}
}
}

View File

@@ -7,12 +7,14 @@ package org.cryptomator.ui.fxapp;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.ui.dokanysupportend.DokanySupportEndComponent;
import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
@@ -22,7 +24,18 @@ import javafx.scene.image.Image;
import java.io.IOException;
import java.io.InputStream;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class, UpdateReminderComponent.class})
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
MainWindowComponent.class, //
PreferencesComponent.class, //
VaultOptionsComponent.class, //
UnlockComponent.class, //
LockComponent.class, //
QuitComponent.class, //
ErrorComponent.class, //
HealthCheckComponent.class, //
UpdateReminderComponent.class, //
DokanySupportEndComponent.class, //
ShareVaultComponent.class})
abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException {

View File

@@ -5,12 +5,14 @@ import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.dokanysupportend.DokanySupportEndComponent;
import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.unlock.UnlockWorkflow;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
@@ -47,23 +49,27 @@ public class FxApplicationWindows {
private final QuitComponent.Builder quitWindowBuilder;
private final UnlockComponent.Factory unlockWorkflowFactory;
private final UpdateReminderComponent.Factory updateReminderWindowBuilder;
private final DokanySupportEndComponent.Factory dokanySupportEndWindowBuilder;
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
private final FilteredList<Window> visibleWindows;
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage,
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
Optional<TrayIntegrationProvider> trayIntegration, //
Lazy<MainWindowComponent> mainWindow, //
Lazy<PreferencesComponent> preferencesWindow, //
QuitComponent.Builder quitWindowBuilder, //
UnlockComponent.Factory unlockWorkflowFactory, //
UpdateReminderComponent.Factory updateReminderWindowBuilder, //
DokanySupportEndComponent.Factory dokanySupportEndWindowBuilder, //
LockComponent.Factory lockWorkflowFactory, //
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
ExecutorService executor) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
@@ -72,10 +78,12 @@ public class FxApplicationWindows {
this.quitWindowBuilder = quitWindowBuilder;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.updateReminderWindowBuilder = updateReminderWindowBuilder;
this.dokanySupportEndWindowBuilder = dokanySupportEndWindowBuilder;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
}
@@ -122,6 +130,10 @@ public class FxApplicationWindows {
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
}
public void showShareVaultWindow(Vault vault) {
CompletableFuture.runAsync(() -> shareVaultWindow.create(vault).showShareVaultWindow(), Platform::runLater);
}
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
}
@@ -134,6 +146,11 @@ public class FxApplicationWindows {
CompletableFuture.runAsync(() -> updateReminderWindowBuilder.create().checkAndShowUpdateReminderWindow(), Platform::runLater);
}
public void showDokanySupportEndWindow() {
CompletableFuture.runAsync(() -> dokanySupportEndWindowBuilder.create().showDokanySupportEndWindow(), Platform::runLater);
}
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");

View File

@@ -1,45 +1,57 @@
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 javax.inject.Named;
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 AUTOCHECK_DELAY = Duration.seconds(5);
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
private final Environment env;
private final Settings settings;
private final StringProperty latestVersionProperty;
private final Comparator<String> semVerComparator;
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, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
UpdateChecker(Settings settings, //
Environment env, //
ScheduledService<String> updateCheckerService) {
this.env = env;
this.settings = settings;
this.latestVersionProperty = latestVersionProperty;
this.semVerComparator = semVerComparator;
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(AUTOCHECK_DELAY);
startCheckingForUpdates(AUTO_CHECK_DELAY);
}
}
@@ -59,36 +71,65 @@ public class UpdateChecker {
private void checkStarted(WorkerStateEvent event) {
LOG.debug("Checking for updates...");
state.set(UpdateCheckState.IS_CHECKING);
}
private void checkSucceeded(WorkerStateEvent event) {
String latestVersion = updateCheckerService.getValue();
LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion);
if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) {
// update is available
latestVersionProperty.set(latestVersion);
} else {
latestVersionProperty.set(null);
}
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) {
LOG.warn("Error checking for updates", event.getSource().getException());
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 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

@@ -11,8 +11,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
@@ -32,13 +30,6 @@ public abstract class UpdateCheckerModule {
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
@Named("latestVersion")
@FxApplicationScoped
static StringProperty provideLatestVersion() {
return new SimpleStringProperty();
}
@Provides
@FxApplicationScoped
static Optional<HttpClient> provideHttpClient() {

View File

@@ -1,7 +1,6 @@
package org.cryptomator.ui.keyloading.hub;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.coffeelibs.tinyoauth2client.AuthFlow;
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
import io.github.coffeelibs.tinyoauth2client.http.response.Response;
@@ -23,7 +22,7 @@ class AuthFlowTask extends Task<String> {
/**
* Spawns a server and waits for the redirectUri to be called.
*
* @param hubConfig Configuration object holding parameters required by {@link AuthFlow}
* @param hubConfig Configuration object holding parameters required by {@link io.github.coffeelibs.tinyoauth2client.AuthorizationCodeGrant}
* @param redirectUriConsumer A callback invoked with the redirectUri, as soon as the server has started
*/
public AuthFlowTask(HubConfig hubConfig, AuthFlowContext authFlowContext, Consumer<URI> redirectUriConsumer) {
@@ -37,10 +36,10 @@ class AuthFlowTask extends Task<String> {
var response = TinyOAuth2.client(hubConfig.clientId) //
.withTokenEndpoint(URI.create(hubConfig.tokenEndpoint)) //
.withRequestTimeout(Duration.ofSeconds(10)) //
.authFlow(URI.create(hubConfig.authEndpoint)) //
.authorizationCodeGrant(URI.create(hubConfig.authEndpoint)) //
.setSuccessResponse(Response.redirect(URI.create(hubConfig.authSuccessUrl + "&device=" + authFlowContext.deviceId()))) //
.setErrorResponse(Response.redirect(URI.create(hubConfig.authErrorUrl + "&device=" + authFlowContext.deviceId()))) //
.authorize(redirectUriConsumer);
.authorize(HttpClient.newHttpClient(), redirectUriConsumer);
if (response.statusCode() != 200) {
throw new NotOkResponseException("Authorization returned status code " + response.statusCode());
}

View File

@@ -0,0 +1,12 @@
package org.cryptomator.ui.keyloading.hub;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
/**
* Thrown, when Hub registerDevice-Request returns with 409
*/
class DeviceAlreadyExistsException extends MasterkeyLoadingFailedException {
public DeviceAlreadyExistsException() {
super("Device already registered on this Hub instance");
}
}

View File

@@ -1,7 +1,7 @@
package org.cryptomator.ui.keyloading.hub;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.URI;
@@ -19,13 +19,44 @@ public class HubConfig {
@Deprecated // use apiBaseUrl + "/devices/"
public String devicesResourceUrl;
/**
* A collection of String template processors to construct URIs related to this Hub instance.
*/
@JsonIgnore
public final URIProcessors URIs = new URIProcessors();
/**
* Get the URI pointing to the <code>/api/</code> base resource.
*
* @return <code>/api/</code> URI
* @apiNote URI is guaranteed to end on <code>/</code>
* @see #URIs
*/
public URI getApiBaseUrl() {
if (apiBaseUrl != null) {
return URI.create(apiBaseUrl);
} else {
// legacy approach
// make sure to end on "/":
return URI.create(apiBaseUrl + "/").normalize();
} else { // legacy approach
assert devicesResourceUrl != null;
// make sure to end on "/":
return URI.create(devicesResourceUrl + "/..").normalize();
}
}
public URI getWebappBaseUrl() {
return getApiBaseUrl().resolve("../app/");
}
public class URIProcessors {
/**
* Resolves paths relative to the <code>/api/</code> endpoint of this Hub instance.
*/
public final StringTemplate.Processor<URI, RuntimeException> API = template -> {
var path = template.interpolate();
var relPath = path.startsWith("/") ? path.substring(1) : path;
return getApiBaseUrl().resolve(relPath);
};
}
}

View File

@@ -119,6 +119,13 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS)
@KeyLoadingScoped
static Scene provideHubLegacyRegisterSuccessScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS);
}
@Provides
@FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS)
@KeyLoadingScoped
@@ -134,10 +141,17 @@ public abstract class HubKeyLoadingModule {
}
@Provides
@FxmlScene(FxmlFile.HUB_SETUP_DEVICE)
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_SETUP_DEVICE);
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceAlreadyExistsScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS);
}
@Provides
@@ -147,6 +161,13 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
@KeyLoadingScoped
static Scene provideRequireAccountInitScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT);
}
@Binds
@IntoMap
@FxControllerKey(NoKeychainController.class)
@@ -177,6 +198,11 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(LegacyRegisterDeviceController.class)
abstract FxController bindLegacyRegisterDeviceController(LegacyRegisterDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(LegacyRegisterSuccessController.class)
abstract FxController bindLegacyRegisterSuccessController(LegacyRegisterSuccessController controller);
@Binds
@IntoMap
@FxControllerKey(RegisterSuccessController.class)
@@ -191,4 +217,9 @@ public abstract class HubKeyLoadingModule {
@IntoMap
@FxControllerKey(UnauthorizedDeviceController.class)
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(RequireAccountInitController.class)
abstract FxController bindRequireAccountInitController(RequireAccountInitController controller);
}

View File

@@ -1,7 +1,6 @@
package org.cryptomator.ui.keyloading.hub;
import com.google.common.base.Preconditions;
import com.nimbusds.jose.JWEObject;
import dagger.Lazy;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
@@ -44,6 +43,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
this.window = window;
this.keychainManager = keychainManager;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.authFlowScene = authFlowScene;
this.noKeychainScene = noKeychainScene;
this.result = result;

View File

@@ -1,7 +1,6 @@
package org.cryptomator.ui.keyloading.hub;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEAlgorithm;
@@ -13,19 +12,20 @@ import com.nimbusds.jose.crypto.ECDHEncrypter;
import com.nimbusds.jose.crypto.PasswordBasedDecrypter;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.jwk.gen.JWKGenerator;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
@@ -37,26 +37,16 @@ class JWEHelper {
private static final String JWE_PAYLOAD_KEY_FIELD = "key";
private static final String EC_ALG = "EC";
private JWEHelper(){}
private JWEHelper() {}
public static JWEObject encryptUserKey(ECPrivateKey userKey, ECPublicKey deviceKey) {
try {
var encodedUserKey = Base64.getEncoder().encodeToString(userKey.getEncoded());
var keyGen = new ECKeyGenerator(Curve.P_384);
var ephemeralKeyPair = keyGen.generate();
var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedUserKey));
var jwe = new JWEObject(header, payload);
jwe.encrypt(new ECDHEncrypter(deviceKey));
return jwe;
} catch (JOSEException e) {
throw new RuntimeException(e);
}
return encryptKey(userKey, deviceKey);
}
public static ECPrivateKey decryptUserKey(JWEObject jwe, String setupCode) throws InvalidJweKeyException {
try {
jwe.decrypt(new PasswordBasedDecrypter(setupCode));
return decodeUserKey(jwe);
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
} catch (JOSEException e) {
throw new InvalidJweKeyException(e);
}
@@ -65,17 +55,23 @@ class JWEHelper {
public static ECPrivateKey decryptUserKey(JWEObject jwe, ECPrivateKey deviceKey) throws InvalidJweKeyException {
try {
jwe.decrypt(new ECDHDecrypter(deviceKey));
return decodeUserKey(jwe);
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
} catch (JOSEException e) {
throw new InvalidJweKeyException(e);
}
}
private static ECPrivateKey decodeUserKey(JWEObject decryptedJwe) {
/**
* Attempts to decode a DER-encoded EC private key.
*
* @param encoded DER-encoded EC private key
* @return the decoded key
* @throws KeyDecodeFailedException On malformed input
*/
public static ECPrivateKey decodeECPrivateKey(byte[] encoded) throws KeyDecodeFailedException {
try {
var keySpec = readKey(decryptedJwe, JWE_PAYLOAD_KEY_FIELD, PKCS8EncodedKeySpec::new);
var factory = KeyFactory.getInstance(EC_ALG);
var privateKey = factory.generatePrivate(keySpec);
KeyFactory factory = KeyFactory.getInstance(EC_ALG);
var privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
if (privateKey instanceof ECPrivateKey ecPrivateKey) {
return ecPrivateKey;
} else {
@@ -84,8 +80,49 @@ class JWEHelper {
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(EC_ALG + " not supported");
} catch (InvalidKeySpecException e) {
LOG.warn("Unexpected JWE payload: {}", decryptedJwe.getPayload());
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
throw new KeyDecodeFailedException(e);
}
}
/**
* Attempts to decode a DER-encoded EC public key.
*
* @param encoded DER-encoded EC public key
* @return the decoded key
* @throws KeyDecodeFailedException On malformed input
*/
public static ECPublicKey decodeECPublicKey(byte[] encoded) throws KeyDecodeFailedException {
try {
KeyFactory factory = KeyFactory.getInstance(EC_ALG);
var publicKey = factory.generatePublic(new X509EncodedKeySpec(encoded));
if (publicKey instanceof ECPublicKey ecPublicKey) {
return ecPublicKey;
} else {
throw new IllegalStateException(EC_ALG + " key factory not generating ECPublicKeys");
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(EC_ALG + " not supported");
} catch (InvalidKeySpecException e) {
throw new KeyDecodeFailedException(e);
}
}
public static JWEObject encryptVaultKey(Masterkey vaultKey, ECPublicKey userKey) {
return encryptKey(vaultKey, userKey);
}
private static JWEObject encryptKey(Key key, ECPublicKey userKey) {
try {
var encodedVaultKey = Base64.getEncoder().encodeToString(key.getEncoded());
var keyGen = new ECKeyGenerator(Curve.P_384);
var ephemeralKeyPair = keyGen.generate();
var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedVaultKey));
var jwe = new JWEObject(header, payload);
jwe.encrypt(new ECDHEncrypter(userKey));
return jwe;
} catch (JOSEException e) {
throw new RuntimeException(e);
}
}
@@ -108,12 +145,12 @@ class JWEHelper {
var keyBytes = new byte[0];
try {
if (fields.get(keyField) instanceof String key) {
keyBytes = BaseEncoding.base64().decode(key);
keyBytes = Base64.getDecoder().decode(key);
return rawKeyFactory.apply(keyBytes);
} else {
throw new IllegalArgumentException("JWE payload doesn't contain field " + keyField);
}
} catch (IllegalArgumentException e) {
} catch (IllegalArgumentException | KeyDecodeFailedException e) {
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
} finally {
@@ -127,4 +164,11 @@ class JWEHelper {
super("Invalid key", cause);
}
}
public static class KeyDecodeFailedException extends CryptoException {
public KeyDecodeFailedException(Throwable cause) {
super("Malformed key", cause);
}
}
}

View File

@@ -64,7 +64,7 @@ public class LegacyRegisterDeviceController implements FxController {
public Button registerBtn;
@Inject
public LegacyRegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
public LegacyRegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;

View File

@@ -0,0 +1,24 @@
package org.cryptomator.ui.keyloading.hub;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@KeyLoadingScoped
public class LegacyRegisterSuccessController implements FxController {
private final Stage window;
@Inject
public LegacyRegisterSuccessController(@KeyLoading Stage window) {
this.window = window;
}
@FXML
public void close() {
window.close();
}
}

View File

@@ -3,6 +3,7 @@ package org.cryptomator.ui.keyloading.hub;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.nimbusds.jose.JWEObject;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
@@ -11,7 +12,6 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +32,6 @@ import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -48,27 +47,29 @@ public class ReceiveKeyController implements FxController {
private final Stage window;
private final HubConfig hubConfig;
private final String vaultId;
private final String deviceId;
private final String bearerToken;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> setupDeviceScene;
private final Lazy<Scene> registerDeviceScene;
private final Lazy<Scene> legacyRegisterDeviceScene;
private final Lazy<Scene> unauthorizedScene;
private final URI vaultBaseUri;
private final Lazy<Scene> accountInitializationScene;
private final Lazy<Scene> invalidLicenseScene;
private final HttpClient httpClient;
@Inject
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_SETUP_DEVICE) Lazy<Scene> setupDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
this.window = window;
this.hubConfig = hubConfig;
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
this.deviceId = deviceId;
this.bearerToken = Objects.requireNonNull(tokenRef.get());
this.result = result;
this.setupDeviceScene = setupDeviceScene;
this.registerDeviceScene = registerDeviceScene;
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
this.unauthorizedScene = unauthorizedScene;
this.vaultBaseUri = getVaultBaseUri(vault);
this.accountInitializationScene = accountInitializationScene;
this.invalidLicenseScene = invalidLicenseScene;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
@@ -76,69 +77,76 @@ public class ReceiveKeyController implements FxController {
@FXML
public void initialize() {
requestVaultMasterkey();
receiveKey();
}
public void receiveKey() {
requestApiConfig();
}
/**
* STEP 1 (Request): GET vault key for this user
* STEP 0 (Request): GET /api/config
*/
private void requestVaultMasterkey() {
var accessTokenUri = appendPath(vaultBaseUri, "/access-token");
var request = HttpRequest.newBuilder(accessTokenUri) //
.header("Authorization", "Bearer " + bearerToken) //
private void requestApiConfig() {
var configUri = hubConfig.URIs.API."config";
var request = HttpRequest.newBuilder(configUri) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
.thenAcceptAsync(this::receivedVaultMasterkey, Platform::runLater) //
.thenAcceptAsync(this::receivedApiConfig, Platform::runLater) //
.exceptionally(this::retrievalFailed);
}
/**
* STEP 1 (Response): GET vault key for this user
* STEP 0 (Response): GET /api/config
*
* @param response Response
*/
private void receivedVaultMasterkey(HttpResponse<String> response) {
private void receivedApiConfig(HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
switch (response.statusCode()) {
case 200 -> requestUserKey(response.body());
case 402 -> licenseExceeded();
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
case 404 -> requestLegacyAccessToken();
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
Preconditions.checkState(response.statusCode() == 200, "Unexpected response " + response.statusCode());
try {
var config = JSON.reader().readValue(response.body(), ConfigDto.class);
if (config.apiLevel >= 1) {
requestDeviceData();
} else {
requestLegacyAccessToken();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* STEP 2 (Request): GET user key for this device
* STEP 1 (Request): GET user key for this device
*/
private void requestUserKey(String encryptedVaultKey) {
var deviceTokenUri = URI.create(hubConfig.getApiBaseUrl() + "/devices/" + deviceId);
var request = HttpRequest.newBuilder(deviceTokenUri) //
private void requestDeviceData() {
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
var request = HttpRequest.newBuilder(deviceUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
.thenAcceptAsync(response -> receivedUserKey(encryptedVaultKey, response), Platform::runLater) //
.thenAcceptAsync(this::receivedDeviceData) //
.exceptionally(this::retrievalFailed);
}
/**
* STEP 2 (Response): GET user key for this device
* STEP 1 (Response): GET user key for this device
*
* @param response Response
*/
private void receivedUserKey(String encryptedVaultKey, HttpResponse<String> response) {
private void receivedDeviceData(HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
try {
switch (response.statusCode()) {
case 200 -> {
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
receivedBothEncryptedKeys(encryptedVaultKey, device.userPrivateKey);
requestVaultMasterkey(device.userPrivateKey);
}
case 404 -> needsDeviceSetup(); // TODO: using the setup code, we can theoretically immediately unlock
case 404 -> Platform.runLater(this::needsDeviceRegistration);
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
}
} catch (IOException e) {
@@ -146,18 +154,49 @@ public class ReceiveKeyController implements FxController {
}
}
private void needsDeviceSetup() {
window.setScene(setupDeviceScene.get());
private void needsDeviceRegistration() {
window.setScene(registerDeviceScene.get());
}
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) throws IOException {
/**
* STEP 2 (Request): GET vault key for this user
*/
private void requestVaultMasterkey(String encryptedUserKey) {
var vaultKeyUri = hubConfig.URIs.API."vaults/\{vaultId}/access-token";
var request = HttpRequest.newBuilder(vaultKeyUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
.thenAcceptAsync(response -> receivedVaultMasterkey(encryptedUserKey, response), Platform::runLater) //
.exceptionally(this::retrievalFailed);
}
/**
* STEP 2 (Response): GET vault key for this user
*
* @param response Response
*/
private void receivedVaultMasterkey(String encryptedUserKey, HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
switch (response.statusCode()) {
case 200 -> receivedBothEncryptedKeys(response.body(), encryptedUserKey);
case 402 -> licenseExceeded();
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
case 449 -> accountInitializationRequired();
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
}
}
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) {
try {
var vaultKeyJwe = JWEObject.parse(encryptedVaultKey);
var userKeyJwe = JWEObject.parse(encryptedUserKey);
result.complete(ReceivedKey.vaultKeyAndUserKey(vaultKeyJwe, userKeyJwe));
window.close();
} catch (ParseException e) {
throw new IOException("Failed to parse JWE", e);
retrievalFailed(e);
}
}
@@ -166,7 +205,7 @@ public class ReceiveKeyController implements FxController {
*/
@Deprecated
private void requestLegacyAccessToken() {
var legacyAccessTokenUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
var legacyAccessTokenUri = hubConfig.URIs.API."vaults/\{vaultId}/keys/\{deviceId}";
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
@@ -221,6 +260,10 @@ public class ReceiveKeyController implements FxController {
window.setScene(unauthorizedScene.get());
}
private void accountInitializationRequired() {
window.setScene(accountInitializationScene.get());
}
private Void retrievalFailed(Throwable cause) {
result.completeExceptionally(cause);
return null;
@@ -244,19 +287,15 @@ public class ReceiveKeyController implements FxController {
}
}
private static URI getVaultBaseUri(Vault vault) {
try {
var url = vault.getVaultConfigCache().get().getKeyId();
assert url.getScheme().startsWith(SCHEME_PREFIX);
var correctedScheme = url.getScheme().substring(SCHEME_PREFIX.length());
return new URI(correctedScheme, url.getSchemeSpecificPart(), url.getFragment());
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (URISyntaxException e) {
throw new IllegalStateException("URI constructed from params known to be valid", e);
}
private static String extractVaultId(URI vaultKeyUri) {
assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX);
var path = vaultKeyUri.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
@JsonIgnoreProperties(ignoreUnknown = true)
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
@JsonIgnoreProperties(ignoreUnknown = true)
private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {}
}

View File

@@ -31,19 +31,25 @@ import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.ECPublicKey;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@KeyLoadingScoped
public class RegisterDeviceController implements FxController {
@@ -57,12 +63,12 @@ public class RegisterDeviceController implements FxController {
private final String bearerToken;
private final Lazy<Scene> registerSuccessScene;
private final Lazy<Scene> registerFailedScene;
private final Lazy<Scene> deviceAlreadyExistsScene;
private final String deviceId;
private final P384KeyPair deviceKeyPair;
private final CompletableFuture<ReceivedKey> result;
private final HttpClient httpClient;
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
private final BooleanProperty invalidSetupCode = new SimpleBooleanProperty(false);
private final BooleanProperty workInProgress = new SimpleBooleanProperty(false);
public TextField setupCodeField;
@@ -70,7 +76,7 @@ public class RegisterDeviceController implements FxController {
public Button registerBtn;
@Inject
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS) Lazy<Scene> deviceAlreadyExistsScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
@@ -79,13 +85,13 @@ public class RegisterDeviceController implements FxController {
this.bearerToken = Objects.requireNonNull(bearerToken.get());
this.registerSuccessScene = registerSuccessScene;
this.registerFailedScene = registerFailedScene;
this.deviceAlreadyExistsScene = deviceAlreadyExistsScene;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
}
public void initialize() {
deviceNameField.setText(determineHostname());
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
deviceNameField.disableProperty().bind(workInProgress);
setupCodeField.textProperty().addListener(observable -> invalidSetupCode.set(false));
setupCodeField.disableProperty().bind(workInProgress);
@@ -108,9 +114,8 @@ public class RegisterDeviceController implements FxController {
public void register() {
workInProgress.set(true);
var apiRootUrl = hubConfig.getApiBaseUrl();
var userReq = HttpRequest.newBuilder(apiRootUrl.resolve("users/me")) //
var userReq = HttpRequest.newBuilder(hubConfig.URIs.API."users/me") //
.GET() //
.timeout(REQ_TIMEOUT) //
.header("Authorization", "Bearer " + bearerToken) //
@@ -126,17 +131,19 @@ public class RegisterDeviceController implements FxController {
}
}).thenApply(user -> {
try {
assert user.privateKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
assert user.privateKey != null && user.publicKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
var userPublicKey = JWEHelper.decodeECPublicKey(Base64.getDecoder().decode(user.publicKey));
migrateLegacyDevices(userPublicKey); // TODO: remove eventually, when most users have migrated to Hub 1.3.x or newer
var userKey = JWEHelper.decryptUserKey(JWEObject.parse(user.privateKey), setupCodeField.getText());
return JWEHelper.encryptUserKey(userKey, deviceKeyPair.getPublic());
} catch (ParseException e) {
} catch (ParseException | JWEHelper.KeyDecodeFailedException e) {
throw new RuntimeException("Server answered with unparsable user key", e);
}
}).thenCompose(jwe -> {
var now = Instant.now().toString();
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
var json = toJson(dto);
var deviceUri = apiRootUrl.resolve("devices/" + deviceId);
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
.timeout(REQ_TIMEOUT) //
@@ -146,7 +153,7 @@ public class RegisterDeviceController implements FxController {
return httpClient.sendAsync(putDeviceReq, HttpResponse.BodyHandlers.discarding());
}).whenCompleteAsync((response, throwable) -> {
if (response != null) {
this.handleResponse(response);
this.handleRegisterDeviceResponse(response);
} else {
this.setupFailed(throwable);
}
@@ -154,6 +161,46 @@ public class RegisterDeviceController implements FxController {
}, Platform::runLater);
}
private void migrateLegacyDevices(ECPublicKey userPublicKey) {
try {
// GET legacy access tokens
var getUri = hubConfig.URIs.API."devices/\{deviceId}/legacy-access-tokens";
var getReq = HttpRequest.newBuilder(getUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
var getRes = httpClient.send(getReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (getRes.statusCode() != 200) {
LOG.debug("GET {} resulted in status code {}. Skipping migration.", getUri, getRes.statusCode());
return;
}
Map<String, String> legacyAccessTokens = JSON.readerForMapOf(String.class).readValue(getRes.body());
if (legacyAccessTokens.isEmpty()) {
return; // no migration required
}
// POST new access tokens
Map<String, String> newAccessTokens = legacyAccessTokens.entrySet().stream().<Map.Entry<String, String>>mapMulti((entry, consumer) -> {
try (var vaultKey = JWEHelper.decryptVaultKey(JWEObject.parse(entry.getValue()), deviceKeyPair.getPrivate())) {
var newAccessToken = JWEHelper.encryptVaultKey(vaultKey, userPublicKey).serialize();
consumer.accept(Map.entry(entry.getKey(), newAccessToken));
} catch (ParseException | JWEHelper.InvalidJweKeyException e) {
LOG.warn("Failed to decrypt legacy access token for vault {}. Skipping migration.", entry.getKey());
}
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
var postUri = hubConfig.URIs.API."users/me/access-tokens";
var postBody = JSON.writer().writeValueAsString(newAccessTokens);
var postReq = HttpRequest.newBuilder(postUri).POST(HttpRequest.BodyPublishers.ofString(postBody)).timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
var postRes = httpClient.send(postReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (postRes.statusCode() != 200) {
throw new IOException(STR."Unexpected response from POST \{postUri}: \{postRes.statusCode()}");
}
} catch (IOException e) {
// log and ignore: this is merely a best-effort attempt of migrating legacy devices. Failure is uncritical as this is merely a convenience feature.
LOG.error("Legacy Device Migration failed.", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new UncheckedIOException(new InterruptedIOException("Legacy Device Migration interrupted"));
}
}
private UserDto fromJson(String json) {
try {
return JSON.reader().readValue(json, UserDto.class);
@@ -170,12 +217,12 @@ public class RegisterDeviceController implements FxController {
}
}
private void handleResponse(HttpResponse<Void> response) {
private void handleRegisterDeviceResponse(HttpResponse<Void> response) {
if (response.statusCode() == 201) {
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
window.setScene(registerSuccessScene.get());
} else if (response.statusCode() == 409) {
deviceNameAlreadyExists.set(true);
setupFailed(new DeviceAlreadyExistsException());
} else {
setupFailed(new IllegalStateException("Unexpected http status code " + response.statusCode()));
}
@@ -184,10 +231,13 @@ public class RegisterDeviceController implements FxController {
private void setupFailed(Throwable cause) {
switch (cause) {
case CompletionException e when e.getCause() instanceof JWEHelper.InvalidJweKeyException -> invalidSetupCode.set(true);
case DeviceAlreadyExistsException e -> {
LOG.debug("Device already registered in hub instance {} for different user", hubConfig.authSuccessUrl);
window.setScene(deviceAlreadyExistsScene.get());
}
default -> {
LOG.warn("Device setup failed.", cause);
window.setScene(registerFailedScene.get());
result.completeExceptionally(cause);
}
}
}
@@ -202,15 +252,6 @@ public class RegisterDeviceController implements FxController {
}
//--- Getters & Setters
public BooleanProperty deviceNameAlreadyExistsProperty() {
return deviceNameAlreadyExists;
}
public boolean getDeviceNameAlreadyExists() {
return deviceNameAlreadyExists.get();
}
public BooleanProperty invalidSetupCodeProperty() {
return invalidSetupCode;
}

View File

@@ -1,6 +1,5 @@
package org.cryptomator.ui.keyloading.hub;
import com.nimbusds.jose.JWEObject;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
@@ -22,8 +21,8 @@ public class RegisterFailedController implements FxController {
@FXML
public void close() {
result.cancel(true);
window.close();
}
}

View File

@@ -1,24 +1,43 @@
package org.cryptomator.ui.keyloading.hub;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.util.concurrent.CompletableFuture;
public class RegisterSuccessController implements FxController {
private final Stage window;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> receiveKeyScene;
private final ReceiveKeyController receiveKeyController;
@Inject
public RegisterSuccessController(@KeyLoading Stage window) {
public RegisterSuccessController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene, ReceiveKeyController receiveKeyController) {
this.window = window;
this.result = result;
this.receiveKeyScene = receiveKeyScene;
this.receiveKeyController = receiveKeyController;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@FXML
public void close() {
window.close();
public void complete() {
window.setScene(receiveKeyScene.get());
receiveKeyController.receiveKey();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
}

View File

@@ -0,0 +1,46 @@
package org.cryptomator.ui.keyloading.hub;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
public class RequireAccountInitController implements FxController {
private final Application application;
private final HubConfig hubConfig;
private final Stage window;
private final CompletableFuture<ReceivedKey> result;
@Inject
public RequireAccountInitController(Application application, HubConfig hubConfig, @KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
this.application = application;
this.hubConfig = hubConfig;
this.window = window;
this.result = result;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@FXML
public void completeSetup() {
application.getHostServices().showDocument(hubConfig.getWebappBaseUrl().resolve("profile").toString());
close();
}
@FXML
public void close() {
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
}

View File

@@ -46,7 +46,7 @@ public class MainWindowTitleController implements FxController {
this.appWindows = appWindows;
this.trayMenuInitialized = trayMenu.isInitialized();
this.updateChecker = updateChecker;
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
this.updateAvailable = updateChecker.updateAvailableProperty();
this.licenseHolder = licenseHolder;
this.settings = settings;
this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton, settings.showTrayIcon);

View File

@@ -44,6 +44,11 @@ public class VaultDetailLockedController implements FxController {
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
}
@FXML
public void share() {
appWindows.showShareVaultWindow(vault.get());
}
@FXML
public void showVaultOptions() {
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);

View File

@@ -7,6 +7,8 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,6 +39,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
@@ -56,6 +59,7 @@ public class VaultListController implements FxController {
private final Stage mainWindow;
private final ObservableList<Vault> vaults;
private final VaultService vaultService;
private final ObjectProperty<Vault> selectedVault;
private final VaultListCellFactory cellFactory;
private final AddVaultWizardComponent.Builder addVaultWizard;
@@ -64,6 +68,7 @@ public class VaultListController implements FxController {
private final VaultListManager vaultListManager;
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
private final ResourceBundle resourceBundle;
private final FxApplicationWindows appWindows;
public ListView<Vault> vaultList;
public StackPane root;
@@ -76,18 +81,22 @@ public class VaultListController implements FxController {
ObservableList<Vault> vaults, //
ObjectProperty<Vault> selectedVault, //
VaultListCellFactory cellFactory, //
VaultService vaultService, //
AddVaultWizardComponent.Builder addVaultWizard, //
RemoveVaultComponent.Builder removeVaultDialogue, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle) {
ResourceBundle resourceBundle, //
FxApplicationWindows appWindows) {
this.mainWindow = mainWindow;
this.vaults = vaults;
this.selectedVault = selectedVault;
this.cellFactory = cellFactory;
this.vaultService = vaultService;
this.addVaultWizard = addVaultWizard;
this.removeVaultDialogue = removeVaultDialogue;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.appWindows = appWindows;
this.emptyVaultList = Bindings.isEmpty(vaults);
@@ -108,6 +117,18 @@ public class VaultListController implements FxController {
});
vaultList.addEventFilter(MouseEvent.MOUSE_RELEASED, this::deselect);
//unlock vault on double click
vaultList.addEventFilter(MouseEvent.MOUSE_CLICKED, click -> {
if (click.getClickCount() >= 2) {
Optional.ofNullable(selectedVault.get())
.filter(Vault::isLocked)
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
Optional.ofNullable(selectedVault.get())
.filter(Vault::isUnlocked)
.ifPresent(vaultService::reveal);
}
});
//don't show context menu when no vault selected
vaultList.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, request -> {
if (selectedVault.get() == null) {

View File

@@ -1,18 +1,33 @@
package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import javax.inject.Inject;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
import java.util.ResourceBundle;
@PreferencesScoped
public class UpdatesPreferencesController implements FxController {
@@ -20,29 +35,55 @@ public class UpdatesPreferencesController implements FxController {
private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
private final Application application;
private final Environment environment;
private final ResourceBundle resourceBundle;
private final Settings settings;
private final UpdateChecker updateChecker;
private final ObjectBinding<ContentDisplay> checkForUpdatesButtonState;
private final ReadOnlyStringProperty latestVersion;
private final ObservableValue<Instant> lastSuccessfulUpdateCheck;
private final StringBinding lastUpdateCheckMessage;
private final ObservableValue<String> timeDifferenceMessage;
private final String currentVersion;
private final BooleanBinding updateAvailable;
private final BooleanBinding checkFailed;
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
private final DateTimeFormatter formatter;
private final BooleanBinding upToDate;
/* FXML */
public CheckBox checkForUpdatesCheckbox;
@Inject
UpdatesPreferencesController(Application application, Settings settings, UpdateChecker updateChecker) {
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker) {
this.application = application;
this.environment = environment;
this.resourceBundle = resourceBundle;
this.settings = settings;
this.updateChecker = updateChecker;
this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
this.latestVersion = updateChecker.latestVersionProperty();
this.updateAvailable = latestVersion.isNotNull();
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
this.currentVersion = updateChecker.getCurrentVersion();
this.updateAvailable = updateChecker.updateAvailableProperty();
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
this.checkFailed = updateChecker.checkFailedProperty();
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
}
public void initialize() {
checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates);
upToDate.addListener((_, _, newVal) -> {
if (newVal) {
upToDateLabelVisible.set(true);
PauseTransition delay = new PauseTransition(javafx.util.Duration.seconds(5));
delay.setOnFinished(_ -> upToDateLabelVisible.set(false));
delay.play();
}
});
}
@FXML
@@ -55,6 +96,11 @@ public class UpdatesPreferencesController implements FxController {
application.getHostServices().showDocument(DOWNLOADS_URI);
}
@FXML
public void showLogfileDirectory() {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
}
/* Observable Properties */
public ObjectBinding<ContentDisplay> checkForUpdatesButtonStateProperty() {
@@ -77,6 +123,46 @@ public class UpdatesPreferencesController implements FxController {
return currentVersion;
}
public StringBinding lastUpdateCheckMessageProperty() {
return lastUpdateCheckMessage;
}
public String getLastUpdateCheckMessage() {
Instant lastCheck = lastSuccessfulUpdateCheck.getValue();
if (lastCheck != null && !lastCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
return formatter.format(LocalDateTime.ofInstant(lastCheck, ZoneId.systemDefault()));
} else {
return "-";
}
}
public ObservableValue<String> timeDifferenceMessageProperty() {
return timeDifferenceMessage;
}
public String getTimeDifferenceMessage() {
var lastSuccessCheck = lastSuccessfulUpdateCheck.getValue();
var duration = Duration.between(lastSuccessCheck, Instant.now());
var hours = duration.toHours();
if (lastSuccessCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
return resourceBundle.getString("preferences.updates.lastUpdateCheck.never");
} else if (hours < 1) {
return resourceBundle.getString("preferences.updates.lastUpdateCheck.recently");
} else if (hours < 24) {
return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.hoursAgo"), hours);
} else {
return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.daysAgo"), duration.toDays());
}
}
public BooleanProperty upToDateLabelVisibleProperty() {
return upToDateLabelVisible;
}
public boolean isUpToDateLabelVisible() {
return upToDateLabelVisible.get();
}
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
@@ -84,4 +170,13 @@ public class UpdatesPreferencesController implements FxController {
public boolean isUpdateAvailable() {
return updateAvailable.get();
}
public BooleanBinding checkFailedProperty() {
return checkFailed;
}
public boolean isCheckFailed() {
return checkFailed.getValue();
}
}

View File

@@ -2,17 +2,14 @@ package org.cryptomator.ui.preferences;
import dagger.Lazy;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.mount.MountModule;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.MountCapability;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
@@ -21,24 +18,22 @@ import javafx.util.StringConverter;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicReference;
@PreferencesScoped
public class VolumePreferencesController implements FxController {
private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
private static final int MIN_PORT = 1024;
private static final int MAX_PORT = 65535;
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
private final Settings settings;
private final ObservableValue<MountService> selectedMountService;
private final ResourceBundle resourceBundle;
private final BooleanExpression loopbackPortSupported;
private final ObservableValue<Boolean> loopbackPortSupported;
private final ObservableValue<Boolean> mountToDirSupported;
private final ObservableValue<Boolean> mountToDriveLetterSupported;
private final ObservableValue<Boolean> mountFlagsSupported;
private final ObservableValue<Boolean> readonlySupported;
private final ObservableValue<Boolean> fuseRestartRequired;
private final Lazy<Application> application;
private final List<MountService> mountProviders;
public ChoiceBox<MountService> volumeTypeChoiceBox;
@@ -46,7 +41,10 @@ public class VolumePreferencesController implements FxController {
public Button loopbackPortApplyButton;
@Inject
VolumePreferencesController(Settings settings, Lazy<Application> application, List<MountService> mountProviders, @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) {
VolumePreferencesController(Settings settings, //
Lazy<Application> application, //
List<MountService> mountProviders, //
ResourceBundle resourceBundle) {
this.settings = settings;
this.application = application;
this.mountProviders = mountProviders;
@@ -54,17 +52,11 @@ public class VolumePreferencesController implements FxController {
var fallbackProvider = mountProviders.stream().findFirst().orElse(null);
this.selectedMountService = ObservableUtil.mapWithDefault(settings.mountService, serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), fallbackProvider);
this.loopbackPortSupported = BooleanExpression.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT)));
this.loopbackPortSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT));
this.mountToDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT) || s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR));
this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
this.fuseRestartRequired = selectedMountService.map(s -> {//
return firstUsedProblematicFuseMountService.get() != null //
&& MountModule.isProblematicFuseService(s) //
&& !firstUsedProblematicFuseMountService.get().equals(s);
});
}
public void initialize() {
@@ -101,12 +93,12 @@ public class VolumePreferencesController implements FxController {
/* Property Getters */
public BooleanExpression loopbackPortSupportedProperty() {
public ObservableValue<Boolean> loopbackPortSupportedProperty() {
return loopbackPortSupported;
}
public boolean isLoopbackPortSupported() {
return loopbackPortSupported.get();
return loopbackPortSupported.getValue();
}
public ObservableValue<Boolean> readonlySupportedProperty() {
@@ -141,14 +133,6 @@ public class VolumePreferencesController implements FxController {
return mountFlagsSupported.getValue();
}
public ObservableValue<Boolean> fuseRestartRequiredProperty() {
return fuseRestartRequired;
}
public boolean getFuseRestartRequired() {
return fuseRestartRequired.getValue();
}
/* Helpers */
private class MountServiceConverter extends StringConverter<MountService> {

View File

@@ -0,0 +1,34 @@
package org.cryptomator.ui.sharevault;
import dagger.BindsInstance;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
@ShareVaultScoped
@Subcomponent(modules = {ShareVaultModule.class})
public interface ShareVaultComponent {
@ShareVaultWindow
Stage window();
@FxmlScene(FxmlFile.SHARE_VAULT)
Lazy<Scene> scene();
default void showShareVaultWindow(){
Stage stage = window();
stage.setScene(scene().get());
stage.show();
}
@Subcomponent.Factory
interface Factory {
ShareVaultComponent create(@BindsInstance @ShareVaultWindow Vault vault);
}
}

View File

@@ -0,0 +1,76 @@
package org.cryptomator.ui.sharevault;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
@ShareVaultScoped
public class ShareVaultController implements FxController {
private static final String SCHEME_PREFIX = "hub+";
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/en/latest/security/best-practices/#sharing-of-vaults";
private final Stage window;
private final Lazy<Application> application;
private final Vault vault;
private final Boolean hubVault;
@Inject
ShareVaultController(@ShareVaultWindow Stage window, //
Lazy<Application> application, //
@ShareVaultWindow Vault vault) {
this.window = window;
this.application = application;
this.vault = vault;
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
}
@FXML
public void close() {
window.close();
}
@FXML
public void visitHub() {
application.get().getHostServices().showDocument(VISIT_HUB_URL);
}
@FXML
public void openHub() {
application.get().getHostServices().showDocument(getHubUri(vault).toString());
}
@FXML
public void visitBestPractices() {
application.get().getHostServices().showDocument(BEST_PRACTICES_URL);
}
private static URI getHubUri(Vault vault) {
try {
var keyID = new URI(vault.getVaultConfigCache().get().getKeyId().toString());
assert keyID.getScheme().startsWith(SCHEME_PREFIX);
return new URI(keyID.getScheme().substring(SCHEME_PREFIX.length()) + "://" + keyID.getHost() + "/app/vaults");
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (URISyntaxException e) {
throw new IllegalStateException("URI constructed from params known to be valid", e);
}
}
public boolean isHubVault() {
return hubVault;
}
}

View File

@@ -0,0 +1,54 @@
package org.cryptomator.ui.sharevault;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class ShareVaultModule {
@Provides
@ShareVaultWindow
@ShareVaultScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@ShareVaultWindow
@ShareVaultScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setResizable(false);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle(resourceBundle.getString("shareVault.title"));
return stage;
}
@Provides
@FxmlScene(FxmlFile.SHARE_VAULT)
@ShareVaultScoped
static Scene provideShareVaultScene(@ShareVaultWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.SHARE_VAULT);
}
@Binds
@IntoMap
@FxControllerKey(ShareVaultController.class)
abstract FxController bindShareVaultController(ShareVaultController controller);
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.sharevault;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface ShareVaultScoped {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.sharevault;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface ShareVaultWindow {
}

View File

@@ -11,7 +11,7 @@ public class TrayMenuModule {
@Provides
@TrayMenuScoped
static Optional<TrayMenuController> provideSupportedKeychainAccessProviders() {
static Optional<TrayMenuController> provideFirstSupportedTrayMenuController() {
return TrayMenuController.get();
}

View File

@@ -19,16 +19,8 @@ import java.util.concurrent.Future;
@Subcomponent(modules = {UnlockModule.class})
public interface UnlockComponent {
ExecutorService defaultExecutorService();
UnlockWorkflow unlockWorkflow();
default Future<Boolean> startUnlockWorkflow() {
UnlockWorkflow workflow = unlockWorkflow();
defaultExecutorService().submit(workflow);
return workflow;
}
@Subcomponent.Factory
interface Factory {
UnlockComponent create(@BindsInstance @UnlockWindow Vault vault, @BindsInstance @Named("unlockWindowOwner") @Nullable Stage owner);

View File

@@ -81,6 +81,13 @@ abstract class UnlockModule {
return fxmlLoaders.createScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT);
}
@Provides
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART)
@UnlockScoped
static Scene provideRestartRequiredScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.UNLOCK_REQUIRES_RESTART);
}
// ------------------
@Binds
@@ -93,4 +100,9 @@ abstract class UnlockModule {
@FxControllerKey(UnlockInvalidMountPointController.class)
abstract FxController bindUnlockInvalidMountPointController(UnlockInvalidMountPointController controller);
@Binds
@IntoMap
@FxControllerKey(UnlockRequiresRestartController.class)
abstract FxController bindUnlockRequiresRestartController(UnlockRequiresRestartController controller);
}

View File

@@ -0,0 +1,47 @@
package org.cryptomator.ui.unlock;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.ResourceBundle;
@UnlockScoped
public class UnlockRequiresRestartController implements FxController {
private final Stage window;
private final ResourceBundle resourceBundle;
private final FxApplicationWindows appWindows;
private final Vault vault;
@Inject
UnlockRequiresRestartController(@UnlockWindow Stage window, //
ResourceBundle resourceBundle, //
FxApplicationWindows appWindows, //
@UnlockWindow Vault vault) {
this.window = window;
this.resourceBundle = resourceBundle;
this.appWindows = appWindows;
this.vault = vault;
}
public void initialize() {
window.setTitle(String.format(resourceBundle.getString("unlock.error.title"), vault.getDisplayName()));
}
@FXML
public void close() {
window.close();
}
@FXML
public void closeAndOpenVaultOptions() {
appWindows.showVaultOptionsWindow(vault, SelectedVaultOptionsTab.MOUNT);
window.close();
}
}

View File

@@ -1,7 +1,7 @@
package org.cryptomator.ui.unlock;
import com.google.common.base.Throwables;
import dagger.Lazy;
import org.cryptomator.common.mount.ConflictingMountServiceException;
import org.cryptomator.common.mount.IllegalMountPointException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
@@ -11,6 +11,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,7 +20,9 @@ import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.concurrent.Task;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.io.IOException;
@@ -29,51 +32,56 @@ import java.io.IOException;
* This class runs the unlock process and controls when to display which UI.
*/
@UnlockScoped
public class UnlockWorkflow extends Task<Boolean> {
public class UnlockWorkflow extends Task<Void> {
private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class);
private final Stage mainWindow;
private final Stage window;
private final Vault vault;
private final VaultService vaultService;
private final Lazy<Scene> successScene;
private final Lazy<Scene> invalidMountPointScene;
private final Lazy<Scene> restartRequiredScene;
private final FxApplicationWindows appWindows;
private final KeyLoadingStrategy keyLoadingStrategy;
private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
UnlockWorkflow(@PrimaryStage Stage mainWindow, //
@UnlockWindow Stage window, //
@UnlockWindow Vault vault, //
VaultService vaultService, //
@FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, //
@FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, //
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
FxApplicationWindows appWindows, //
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
this.mainWindow = mainWindow;
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
this.successScene = successScene;
this.invalidMountPointScene = invalidMountPointScene;
this.restartRequiredScene = restartRequiredScene;
this.appWindows = appWindows;
this.keyLoadingStrategy = keyLoadingStrategy;
this.illegalMountPointException = illegalMountPointException;
}
@Override
protected Boolean call() throws InterruptedException, IOException, CryptoException, MountFailedException {
try {
attemptUnlock();
return true;
} catch (UnlockCancelledException e) {
cancel(false); // set Tasks state to cancelled
return false;
}
}
private void attemptUnlock() throws IOException, CryptoException, MountFailedException {
protected Void call() throws InterruptedException, IOException, CryptoException, MountFailedException {
try {
keyLoadingStrategy.use(vault::unlock);
return null;
} catch (UnlockCancelledException e) {
cancel(false); // set Tasks state to cancelled
return null;
} catch (IOException | RuntimeException | MountFailedException e) {
throw e;
} catch (Exception e) {
Throwables.propagateIfPossible(e, IOException.class);
Throwables.propagateIfPossible(e, CryptoException.class);
Throwables.propagateIfPossible(e, IllegalMountPointException.class);
Throwables.propagateIfPossible(e, MountFailedException.class);
throw new IllegalStateException("unexpected exception type", e);
throw new IllegalStateException("Unexpected exception type", e);
}
}
@@ -85,6 +93,13 @@ public class UnlockWorkflow extends Task<Boolean> {
});
}
private void handleConflictingMountServiceException() {
Platform.runLater(() -> {
window.setScene(restartRequiredScene.get());
window.show();
});
}
private void handleGenericError(Throwable e) {
LOG.error("Unlock failed for technical reasons.", e);
appWindows.showErrorWindow(e, window, null);
@@ -98,6 +113,19 @@ public class UnlockWorkflow extends Task<Boolean> {
case ASK -> Platform.runLater(() -> {
window.setScene(successScene.get());
window.show();
double x = mainWindow.getX() + (mainWindow.getWidth() - window.getWidth()) / 2;
double y = mainWindow.getY() + (mainWindow.getHeight() - window.getHeight()) / 2;
if(!mainWindow.isShowing()) {
Screen screen = Screen.getScreensForRectangle(mainWindow.getX(), mainWindow.getY(), mainWindow.getWidth(), mainWindow.getHeight())
.stream()
.findFirst()
.orElse(Screen.getPrimary());
Rectangle2D bounds = screen.getVisualBounds();
x = bounds.getMinX() + (bounds.getWidth() - window.getWidth()) / 2;
y = bounds.getMinY() + (bounds.getHeight() - window.getHeight()) / 2;
}
window.setX(x);
window.setY(y);
});
case REVEAL -> {
Platform.runLater(window::close);
@@ -113,10 +141,10 @@ public class UnlockWorkflow extends Task<Boolean> {
protected void failed() {
LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
Throwable throwable = super.getException();
if(throwable instanceof IllegalMountPointException impe) {
handleIllegalMountPointError(impe);
} else {
handleGenericError(throwable);
switch (throwable) {
case IllegalMountPointException e -> handleIllegalMountPointError(e);
case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
default -> handleGenericError(throwable);
}
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}

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