Compare commits

..
549 Commits
Author SHA1 Message Date
Armin Schrenk 8f42c1c9ce Merge branch 'develop' into release/1.7.0 2022-11-18 10:47:33 +01:00
Armin SchrenkandGitHub 37f89fd235 Move release precondition checks to own workflow (#2535) 2022-11-18 10:46:22 +01:00
Armin Schrenk 668feb73f9 Merge branch 'feature/mount-provider' into release/1.7.0 2022-11-17 09:59:46 +01:00
Armin Schrenk ba0569deb1 preparing 1.7.0 2022-11-17 09:59:38 +01:00
Armin Schrenk a70d8e1567 update nonModularGroupIds 2022-11-17 09:50:39 +01:00
Armin Schrenk 96b7a8df3f bump dokany-nio-adapter 2022-11-17 09:24:51 +01:00
Armin Schrenk 5143fdccbb Refactor mount point setting and controller :
* remove CustomMountPath, winDriveLetter and usesCustomMountPath
* new property mountPoint (can be null)
* differentiation between driveLetter and directory happens in controller
2022-11-11 19:42:59 +01:00
Armin Schrenk 3c5502955a bump version of dokany-nio-adapter 2022-11-11 15:20:13 +01:00
Armin Schrenk 2d3a035d9e renamed mount option controller members 2022-11-11 11:44:50 +01:00
Sebastian Stenzel 4029f86a0d reveal OR copy mount point depending on chosen mounter 2022-11-10 14:11:06 +01:00
Sebastian Stenzel 6dac00625c updated mount API 2022-11-09 15:33:20 +01:00
Sebastian Stenzel 41b7bd54f1 use vault id as a volume id 2022-11-08 16:38:45 +01:00
Sebastian Stenzel f236614bd0 allow repeated mount & unmount of the same vault 2022-11-08 16:38:31 +01:00
Sebastian Stenzel 31c69f145e use a fallback mount point absed on -Dcryptomator.mountPointsDir=... vm param 2022-11-07 18:17:20 +01:00
Sebastian Stenzel b5a00f5ebe added --enable-preview --enable-native-access to run config 2022-11-07 18:07:45 +01:00
Sebastian Stenzel 40c4300012 fixed json syntax error 2022-11-07 16:20:19 +01:00
Sebastian Stenzel 5369ddb1f9 adjust suppression.xml to jfuse 2022-11-07 16:17:50 +01:00
Sebastian Stenzel 1b4117de38 volume settings → change tcp port 2022-11-07 16:16:10 +01:00
Sebastian Stenzel 3e5a7fadd0 only set the mount point when a mount point is explicitly required
(didn't work for the webdav fallback provider, which doesn't _not_ MOUNT_TO_SYSTEM_CHOSEN_PATH, as it does not mount at all)
2022-11-07 15:55:26 +01:00
Sebastian Stenzel eff2530e70 pre-select chosen mount service 2022-11-07 15:52:48 +01:00
Armin Schrenk 0f84d0c990 adapt to new APIs 2022-11-07 14:49:56 +01:00
Armin Schrenk fbe33e21fb Fixes #2498
Bump winfsp for windows bundle distribution
2022-11-03 10:16:53 +01:00
Sebastian StenzelandGitHub 7091839f08 Merge pull request #2505 from cryptomator/feature/javafx-19 2022-10-28 22:09:29 +02:00
Sebastian StenzelandGitHub 79bf1759c7 Merge pull request #2509 from cryptomator/feature/jdk19 2022-10-28 22:09:06 +02:00
Sebastian Stenzel 70e5f53009 Merge branch 'develop' into feature/jdk19 2022-10-27 18:13:42 +02:00
Sebastian Stenzel c79cfe1c9d Merge branch 'develop' into feature/javafx-19
# Conflicts:
#	pom.xml
2022-10-26 11:43:07 +02:00
Sebastian Stenzel d619a0cbf4 bump jwt version 2022-10-26 11:40:09 +02:00
Sebastian StenzelandGitHub 517b5958b0 Merge pull request #2487 from cryptomator/feature/ppa-jdk-19
Use `coffeelibs-jdk-19` during PPA build
2022-10-18 16:48:28 +02:00
Armin Schrenk 01a6475d5f replace deprecated method call 2022-10-17 17:13:12 +02:00
Sebastian Stenzel 69641ed3b6 Merge branch 'develop' into feature/jdk19
[ci skip]
2022-10-14 13:55:15 +02:00
Sebastian Stenzel 1f01923169 build on Ubuntu Focal 2022-10-14 13:44:50 +02:00
Sebastian Stenzel 687bed6984 use the correct name of required build-dependency 2022-10-14 13:24:08 +02:00
Sebastian Stenzel 01698d1393 install required build-dependency 2022-10-14 13:21:28 +02:00
Sebastian Stenzel 6367cc2dfb depend on coffeelibs-jdk-19 2022-10-14 13:10:37 +02:00
Armin Schrenk bcfda68bef bump used gh actions 2022-10-13 12:48:48 +02:00
Sebastian Stenzel 2399e29d75 update logback 2022-10-12 13:42:11 +02:00
322779ee88 Refactor winget release to own workflow file (#2471)
... and call it on release windows build

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-10-11 20:16:24 +02:00
Julian Raufelder fd6eb2139d Minor cleanup in build.yml 2022-10-11 17:45:59 +02:00
Julian Raufelder 26ecd67a79 Update linux common metainfo.xml file 2022-10-11 15:33:44 +02:00
Julian Raufelder e06240cbb9 Merge pull request #2475 from cryptomator/feature/add-release-check-precondition
Check POM version and linux metainfo.xml file during release steps
2022-10-11 15:11:54 +02:00
Julian Raufelder b10d892b12 Skip release tests too when ci-skip provided in commit message 2022-10-11 15:09:19 +02:00
Julian Raufelder 45c2dd0358 Validate release check precondition in separate job 2022-10-11 12:45:49 +02:00
Julian Raufelder 34d6e8c419 Validate POM version and linux metainfo.xml file during build 2022-10-10 15:28:16 +02:00
Julian Raufelder ae6a15b391 Check POM version and linux metainfo.xml file during release steps 2022-10-10 14:49:36 +02:00
Sebastian Stenzel 8040b71a51 simplified some more bindings 2022-10-10 12:09:30 +02:00
Sebastian Stenzel 76e7a0a7b8 bump OpenJFX to version 19
replaced EasyBind with `ObservableValue.map()` where possible
2022-10-10 10:24:42 +02:00
Sebastian Stenzel 910efc0b7b prepare macOS builds for diverging Xcode versions
[ci skip]
2022-10-07 12:03:00 +02:00
Sebastian Stenzel c805a820a4 Merge branch 'main' into develop
[ci skip]
2022-10-06 17:42:00 +02:00
Sebastian Stenzel 3ed8316b60 Merge branch 'release/1.6.15' 2022-10-06 17:40:22 +02:00
Sebastian Stenzel b6e381f0bf apply cryptofs hotfix 2022-10-06 17:37:03 +02:00
Sebastian Stenzel aef3f4c6e2 prepare 1.6.15 2022-10-06 16:49:12 +02:00
Sebastian Stenzel 7bc5a336e4 dependency bump 2022-10-06 16:48:12 +02:00
Sebastian Stenzel 672b4aee2d added Belgian, Danish, Farsi, Sinhala, Vietnamese to language chooser 2022-10-06 16:19:16 +02:00
CryptobotandGitHub d4ab17b38c New Crowdin updates (#2427)
[ci skip]
2022-10-06 16:11:29 +02:00
Sebastian Stenzel 91f49c5642 update cryptolib + cryptofs
fixes #2451
2022-10-06 15:20:21 +02:00
Julian Raufelder da2f97b1c4 Fix communitcation with hub if it supports only HTTP version 1.1 2022-10-06 09:38:02 +02:00
Tobias HagemannandGitHub 5d647d20a5 Update README.md 2022-09-22 15:00:22 +02:00
Armin SchrenkandGitHub ce96d1c65e Merge pull request #2452 from chewitt1/chewitt-next-button
Fixes #2447
2022-09-21 21:31:33 +02:00
Carrima Hewitt f02a8e14ff Fixed Trim and Bidirectional Bind 2022-09-21 11:11:00 -04:00
Armin Schrenk dd31908f3a Use for windows exe and msi build same JDKs 2022-09-21 15:50:08 +02:00
Sebastian Stenzel 495a1884c3 Merge branch 'develop' into feature/jdk19
[ci skip]
2022-09-21 13:21:18 +02:00
Sebastian Stenzel 49c3b1fa43 fix font loading warning visible in console
see https://stackoverflow.com/a/65127949/4014509
2022-09-21 13:20:34 +02:00
Sebastian Stenzel cde67966da fix test 2022-09-21 09:49:57 +02:00
Sebastian Stenzel d19e77432e build with jdk 19 2022-09-21 09:28:37 +02:00
Sebastian StenzelandGitHub cc24a4c9ae Merge pull request #2445 from purejava/disable-keyrings
fixes #2047, fixes #2219
2022-09-21 09:12:40 +02:00
Ralph Plawetzki 1101a7574c Beautify the code 2022-09-20 20:12:44 +02:00
Ralph Plawetzki 30de04e291 Move CheckBox (injected from JavaFX) below the final fields (injected by Dagger) 2022-09-20 20:12:43 +02:00
Ralph Plawetzki 2e3d2e86e2 Cut off the application from accessing keychains at the earliest point possible
as suggested in the discussion to #2445
2022-09-20 20:12:43 +02:00
Ralph Plawetzki 78cf082096 Rename properties as suggested in the discussion to #2445 2022-09-20 20:12:43 +02:00
Ralph Plawetzki 64c90f8107 Move checkbox to the front and rename members 2022-09-20 20:12:43 +02:00
Ralph Plawetzki b34bf6f161 Handle further queries of the backend for disabled keyrings 2022-09-20 20:12:43 +02:00
Ralph Plawetzki 17d3d7307d Wire disable all keyrings setting checkbox to the KeychainManager 2022-09-20 20:12:43 +02:00
Ralph Plawetzki e6c7fed662 Add disable all keyrings setting checkbox
and connect it to the settings file
2022-09-20 20:12:42 +02:00
Ralph Plawetzki 23fad16742 Revert "Add disable all keyrings setting checkbox"
This reverts commit d037c6bc140495b5469c2a25ae2a7a2752bc35dc.
2022-09-20 20:12:42 +02:00
Ralph Plawetzki ec794cdca2 Add disable all keyrings setting checkbox
and connect it to the settings file
2022-09-20 20:12:42 +02:00
Carrima Hewitt 2def9216ec Trimmed Vault Name StringProperty 2022-09-20 10:33:26 -04:00
Carrima Hewitt 4f9759af58 Merge branch 'chewitt-next-button' of https://github.com/chewitt1/cryptomator into chewitt-next-button 2022-09-20 10:20:10 -04:00
Carrima Hewitt 382f6b7851 Reverted Misc 2022-09-20 10:19:38 -04:00
Carrima HewittandGitHub d60eb470ea Merge branch 'cryptomator:develop' into chewitt-next-button 2022-09-20 08:41:38 -04:00
Carrima Hewitt 97a61ef717 Removed Comments 2022-09-20 08:37:38 -04:00
Armin Schrenk 08fc5e9a77 Fixes #2454 2022-09-20 10:58:48 +02:00
Carrima Hewitt 4c96c285bf Fixed Next button for Invalid Vault Name 2022-09-19 21:17:48 -04:00
Edward ChowandGitHub 0021f2d37c Use 2x icon on Mac menu bar (#2345) (#2346)
* Use 2x icon on Mac menu bar

Use 2x icon on Mac menu bar

* Fix #2357

Changed Windows tray icon to 2x
2022-09-15 17:38:42 +02:00
Tobias HagemannandGitHub 31d32f2018 Added macOS ARM64 workflow (#2432) 2022-09-10 08:30:18 +02:00
Armin SchrenkandGitHub b9c087b48e Merge pull request #2430 from youscam/patch-1
Update README.md
2022-09-07 09:53:52 +02:00
KAMAL AZZOUZIandGitHub 797dbc0a57 Update README.md
Update Windows profile name
2022-09-07 00:52:40 +01:00
Armin Schrenk 46745d030b [ci skip] fix winget release action 2022-08-31 18:12:59 +02:00
Armin Schrenk 3cdf124e54 Merge branch 'main' into develop
[ci skip]
2022-08-31 16:02:28 +02:00
Armin Schrenk d37147c73e Merge branch 'release/1.6.14' 2022-08-31 15:59:56 +02:00
Armin Schrenk 67dd1548cc finalize release 2022-08-31 15:47:23 +02:00
Armin Schrenk 008bb38e30 preparing 1.6.14 2022-08-31 15:41:18 +02:00
CryptobotandGitHub 1be6128523 New Crowdin updates (#2350)
New translations strings.properties

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

[ci skip]
2022-08-31 15:17:19 +02:00
Armin Schrenk b4f95c465a Fixes #2408 2022-08-31 13:35:27 +02:00
Sebastian Stenzel 40fa961b9b clean up module-info 2022-08-31 11:40:18 +02:00
Sebastian Stenzel 659123fe7a regenerate keypair if .p12 _or_ keychain item is missing
it used to depend only on the keychain item
2022-08-31 11:25:25 +02:00
Sebastian Stenzel 2448963161 class does not need to be generic 2022-08-31 10:57:41 +02:00
Sebastian Stenzel 16c0431f6f use release versions of slf4j + logback 2022-08-31 10:52:53 +02:00
Sebastian Stenzel 2d99d1bdd4 Adding EaseUS as a sponsor
[ci skip]
2022-08-31 10:46:11 +02:00
Sebastian StenzelandGitHub 86ed0d3c0e Merge pull request #2390 from cryptomator/feature/slf4j-2
Upgrade to SLF4J 2.x and Logback 1.3.x
2022-08-12 09:53:27 +02:00
Sebastian Stenzel 0501b0ef39 fixed test 2022-08-11 18:23:33 +02:00
Sebastian Stenzel 2c2b208de7 no need to reset anything, since there is no previous config 2022-08-11 18:18:48 +02:00
Sebastian Stenzel ec7ad0f479 use new ServiceLoader based log configurator 2022-08-11 18:14:54 +02:00
Sebastian Stenzel 5267a04491 make org.cryptomator.desktop an open module 2022-08-11 17:36:10 +02:00
Sebastian Stenzel 04714cd0de make singleton Environment accessible outside of Dagger graph 2022-08-11 17:35:35 +02:00
Sebastian Stenzel 46dbe15e4a update dependencies 2022-08-11 17:34:34 +02:00
Armin SchrenkandGitHub 7abf167ba2 Merge pull request #2381 from cryptomator/feature/hub-handle-402
handle 402 error when authenticating with Cryptomator Hub
2022-08-09 10:55:41 +02:00
44d19bdefe Apply suggestions from code review
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2022-08-09 10:37:22 +02:00
Armin Schrenk f84eef5639 actually use the scene in ui flow 2022-08-08 16:37:30 +02:00
Armin Schrenk 281f6f727f show info for 402 response on unlock 2022-08-08 16:33:07 +02:00
Sebastian Stenzel bf83df3cd3 Merge branch 'main' into develop
# Conflicts:
#	pom.xml

[ci skip]
2022-08-05 14:31:59 +02:00
Sebastian Stenzel be4f6f2b9b Merge branch 'hotfix/1.6.13' 2022-08-05 14:31:25 +02:00
Sebastian Stenzel bf84598131 set version to 1.6.13 2022-08-05 14:31:10 +02:00
Sebastian Stenzel 943be4557f fixes #2374 2022-08-05 14:28:47 +02:00
Sebastian Stenzel 649e029d4f added cryptomator.integrationsMac.keychainServiceName system property 2022-08-05 11:09:49 +02:00
Sebastian StenzelandGitHub 1cd3d1d67f Merge pull request #2365 from purejava/fix-2301
Pass on CLI parameters from launch scripts to executables
2022-08-02 08:44:04 +02:00
Ralph Plawetzki 8290df9ea6 Pass on CLI parameters for Cryptomator started as an AppImage or installed from PPA
Fixes #2301
2022-08-01 18:17:41 +02:00
Armin Schrenk 47f2d423b9 Remove code smells 2022-07-28 11:16:59 +02:00
Armin Schrenk 80b5b6af00 simplify app version code 2022-07-28 10:58:50 +02:00
Armin Schrenk 414c4e20b8 remove code smell 2022-07-28 10:47:46 +02:00
Armin Schrenk 11a55f382a fix winget release 2022-07-27 17:16:33 +02:00
Armin Schrenk 0c5021913c Merge branch 'main' into develop 2022-07-27 12:11:13 +02:00
Armin Schrenk 8bc0b3a700 Merge branch 'hotfix/1.6.12' 2022-07-27 12:10:01 +02:00
Armin Schrenk 57110ff417 add release to metainfo 2022-07-27 11:57:45 +02:00
Armin Schrenk d5bdea4a75 fix debian build script
Fixes #2352
2022-07-27 11:57:00 +02:00
Armin Schrenk 72c8ca3368 preparing 1.6.12 2022-07-27 11:55:24 +02:00
Armin Schrenk 2ca824696c Merge branch 'main' into develop 2022-07-26 13:24:11 +02:00
Armin Schrenk 43d8f7c47f Merge branch 'release/1.6.11' 2022-07-26 13:23:54 +02:00
Armin Schrenk 9209e7698c add release info to linux metainfo file 2022-07-26 13:23:31 +02:00
Armin Schrenk 6e75f1fea8 Merge branch 'main' into develop 2022-07-26 13:11:13 +02:00
Armin Schrenk 2bdb0c3e6a Merge branch 'release/1.6.11' 2022-07-26 13:08:59 +02:00
Armin Schrenk 677634930d Fix windows run config [ci skip] 2022-07-26 13:03:43 +02:00
Armin Schrenk 546da7d11f preparing 1.6.11 2022-07-26 12:39:45 +02:00
Armin Schrenk 8662abae5a Merge branch 'feature/hub' into develop
# Conflicts:
#	.idea/runConfigurations/Cryptomator_Windows.xml
#	.idea/runConfigurations/Cryptomator_Windows_Dev.xml
#	dist/win/build.ps1
#	src/main/java/org/cryptomator/common/Environment.java
2022-07-26 12:29:33 +02:00
CryptobotandGitHub f98782ba4e New Crowdin updates (#2313)
New translations strings.properties

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

[ci skip]
2022-07-26 12:14:32 +02:00
Armin Schrenk 422077ac08 adjust pom 2022-07-20 17:01:29 +02:00
Armin Schrenk 10b9e75972 degenerify button description and rename things in resetPasswordController 2022-07-19 16:36:32 +02:00
Armin Schrenk 00e420a248 Improve add vault wizard:
* adjust ok/not-ok-design to other textfields
* add info, what characters to use
2022-07-19 15:51:58 +02:00
Armin Schrenk f7ddb6e714 Update and deduplicate winfsp download link
Fixes #2117
2022-07-18 14:56:39 +02:00
Armin Schrenk f1c8e3d3f2 Change CatchingExecutor:
* ignore cancellation exception
* remove assertion (Closes #2270)
2022-07-18 12:22:57 +02:00
Armin Schrenk cfc8c7bed7 remove unused instance var 2022-07-18 12:15:48 +02:00
Armin Schrenk 71b876b32d Fixes #1900 2022-07-18 11:42:12 +02:00
Armin Schrenk cdc60abf66 Fixes #2292 2022-07-18 10:23:12 +02:00
Armin Schrenk b73ab4645b only publish to winget, if workflow triggered by release or explicitly stated 2022-07-15 11:14:21 +02:00
Sebastian Stenzel c70961405c updated code sign cert
[ci skip]
2022-07-15 09:52:25 +02:00
d814c1cfca Update integrations-win lib (#2331)
* update to integrations-win 1.1.2

* refactor environment class to reduce maintenance

* fix test

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-07-11 15:59:54 +02:00
Armin SchrenkandGitHub 50413ca9d8 Merge pull request #2329 from barimess/update-link
updated links to the latest version.
2022-07-11 13:37:58 +02:00
Sebastian Stenzel 2a91403f2f bumped webdav-nio-adapter to version 1.2.8, containing vulnerability fixes in jetty 2022-07-11 10:09:22 +02:00
barimess 5ec499ff01 updated links to the latest version.
updated links from 1.5 to 1.6
2022-07-08 18:30:49 +01:00
Armin SchrenkandGitHub 86cf0d4d4e Merge pull request #2328 from cryptomator/feature/hub-device-name-taken
Display message when device name is already in use
2022-07-08 13:44:04 +02:00
Sebastian StenzelandGitHub f2af6f355c remove debug step
[ci skip]
2022-07-08 13:09:26 +02:00
Sebastian StenzelandGitHub 58d01b5bf5 fixed errors
[ci skip]
2022-07-08 13:07:30 +02:00
Sebastian StenzelandGitHub 5e19cd409f use different action for getting the gist
[ci skip]
2022-07-08 12:56:02 +02:00
Sebastian StenzelandGitHub 4dd58a664e Update error-db.yml
add discussion data to errorcodes.json gist

[ci skip]
2022-07-08 12:48:50 +02:00
Armin Schrenk a7fc8d6fc4 hide alreadyExistingLabel when deviceName changed by user 2022-07-08 12:03:38 +02:00
Armin Schrenk 5c31a34d5f Show message in register dialog if device name is already in use 2022-07-08 11:33:59 +02:00
Armin SchrenkandGitHub 67264c0d8c Merge pull request #2319 from cryptomator/feature/hub-better-dialogs
Improve Hub dialogs
2022-07-07 20:54:11 +02:00
mindmonkandGitHub 153ccef8a2 Merge pull request #2325 from cryptomator/feature/recoverykey-success-notification
implemented recoverykey reset password success notification screen
2022-07-07 18:12:12 +02:00
Armin Schrenk 0e853b25f7 simplify the prefill of device registration name textbox 2022-07-07 17:54:53 +02:00
Sebastian StenzelandGitHub 12302162b1 Update error-db.yml
[ci skip]
2022-07-07 17:35:48 +02:00
Jan-Peter Klein 7d14b55bcb Merge branch 'develop' into feature/recoverykey-success-notification 2022-07-07 17:23:32 +02:00
Jan-Peter Klein 69fe5b8c27 changed buttonOrder and buttonData in recovery_reset_password.fxml and removed blank line in string.properties 2022-07-07 17:23:10 +02:00
Sebastian StenzelandGitHub df099df334 Create error-db.yml
Eventually append discussion data to error database
2022-07-07 17:13:27 +02:00
Jan-Peter Klein 6bbdb4baa0 Merge branch 'develop' into feature/recoverykey-success-notification 2022-07-07 16:18:26 +02:00
Jan-Peter Klein ea6318dfc4 comments of the PR were taken into account as well as a few small changes in the recoverykey recover dialog 2022-07-07 16:17:41 +02:00
Armin Schrenk 93500a4efd use try-with-resource 2022-07-07 12:27:20 +02:00
Jan-Peter Klein b132bb6412 implemented recoverykey reset password success notification screen 2022-07-07 12:16:04 +02:00
Armin Schrenk cf38a10284 Change text from registration to "naming" and prefill device name 2022-07-07 12:01:34 +02:00
Sebastian Stenzel 15781b685d use new github "inputs" context, where booleans are supposed to actually be booleans
see https://github.com/actions/runner/issues/1483
see https://docs.github.com/en/actions/learn-github-actions/contexts#inputs-context

[ci skip]
2022-07-06 17:09:02 +02:00
Armin Schrenk 73442cddc8 Change register device dialog text to make intent clear 2022-07-06 10:41:13 +02:00
mindmonkandGitHub bce9833929 Merge pull request #2273 from cryptomator/feature/lock-and-quit-without-asking
implemented functionality of feature request issue #1713 "On closing …
2022-07-05 16:46:45 +02:00
Jan-Peter Klein 29d0e258f1 deleted unused and unnecessary code in QuitController 2022-07-05 16:30:32 +02:00
Jan-Peter Klein 1305e45564 Merge branch 'develop' into feature/lock-and-quit-without-asking 2022-07-05 16:18:06 +02:00
Jan-Peter Klein 9e18215d10 removed unused method, del .idea/uiDesigner.xml and code cleanup 2022-07-05 16:17:42 +02:00
Jan-Peter Klein 683276a6c6 switch scene in quitDialog to quitForcedDialog instead of init new window 2022-07-05 14:22:05 +02:00
Sebastian Stenzel f16a30ef29 Merge branch 'develop' into feature/hub
# Conflicts:
#	pom.xml
2022-07-05 13:28:41 +02:00
Sebastian Stenzel 82db3b59fb bump jwt dependency 2022-07-05 13:22:43 +02:00
Armin Schrenk c191df9ee3 add device registration failed controller 2022-07-05 11:20:52 +02:00
Armin Schrenk a2d6db0415 Add device registrations success screen 2022-07-05 11:09:29 +02:00
Armin Schrenk 077825f98a Add desgin to recieve key dialog 2022-07-05 09:29:49 +02:00
Armin Schrenk 381c1cd8d3 Bump tinyoauth2 dependecy and adjust to new api 2022-07-04 17:15:00 +02:00
Armin Schrenk ae2c67a88b fit unauthorized device dialog to new design 2022-07-04 11:50:07 +02:00
Armin Schrenk 3ce0270cde Adjust hub register device to new design 2022-07-04 11:37:49 +02:00
Armin Schrenk e011a98fa0 add window title to hub keyloading 2022-07-04 10:36:15 +02:00
Armin Schrenk 9386804216 redesign auth processing dialog:
* adjust to new design
* add translation keys
2022-07-04 10:33:58 +02:00
Jan-Peter Klein b791df01f7 removed quitforced package, moved QuitForcedController into quit package and deleted not required files 2022-06-30 15:41:33 +02:00
Jan-Peter Klein 376733106b Merge branch 'develop' into feature/lock-and-quit-without-asking
# Conflicts (solved):
#	src/main/resources/i18n/strings.properties
2022-06-30 15:22:57 +02:00
Jan-Peter Klein 8e6d2cf45a Merge branch 'develop' into feature/lock-and-quit-without-asking 2022-06-30 15:13:06 +02:00
Armin Schrenk 63cd3a02de Merge branch 'develop' into feature/hub 2022-06-30 12:13:29 +02:00
Armin SchrenkandGitHub 1795a7aaa4 Merge pull request #2311 from cryptomator/feature/1430-embark-on-unity
Embark on unity.

Closes #1430
2022-06-30 11:07:25 +02:00
Armin Schrenk c8e4ed1ce4 Merge branch 'feature/translations' into feature/1430-embark-on-unity
# Conflicts:
#	src/main/resources/i18n/strings_de.properties
#	src/main/resources/i18n/strings_pl.properties
#	src/main/resources/i18n/strings_pt_BR.properties
#	src/main/resources/i18n/strings_zh_TW.properties
2022-06-30 10:20:44 +02:00
Armin Schrenk 99b25d0792 migrate translations 2022-06-30 10:00:22 +02:00
Jan-Peter Klein e640e36744 show forced dialog if normal lock and quit fails in quit dialog 2022-06-29 15:27:19 +02:00
Jan-Peter Klein d10d8fb208 implemented force quit dialog 2022-06-29 15:12:43 +02:00
Tobias Hagemann 43e6ca41aa more consistency 2022-06-29 11:34:07 +02:00
e2b4566174 Update src/main/resources/i18n/strings.properties
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2022-06-29 11:15:26 +02:00
d203db8075 Apply suggestions from code review
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2022-06-29 11:14:14 +02:00
Armin Schrenk 7c4f5b28a7 use correct translation key 2022-06-29 11:10:55 +02:00
Armin Schrenk 7edd1dfa40 Reintegrate forget password dialog into application 2022-06-29 11:10:15 +02:00
Armin Schrenk 09550b77dc Merge branch 'feature/1430-embark-on-unity' of https://github.com/cryptomator/cryptomator into feature/1430-embark-on-unity 2022-06-29 10:54:59 +02:00
3bd43908ac Apply suggestions from code review
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2022-06-29 10:54:42 +02:00
Armin Schrenk f69efb85a5 more translation adjustments 2022-06-29 10:54:17 +02:00
Armin Schrenk bc5091a0d7 adjust forget password dialog 2022-06-29 10:52:39 +02:00
Armin Schrenk a199a2a06b add ux todo 2022-06-29 10:08:18 +02:00
Armin Schrenk dffd4d9dd9 change translation keys to use unified dialog scheme 2022-06-29 10:07:03 +02:00
Armin Schrenk 737d98775b Revert "rename recovery_create.fxml file"
This reverts commit 78e458f01a.
2022-06-28 16:41:53 +02:00
Armin Schrenk bee9c9f452 closes #1713
quit application by locking open vaults, if user wished so.
2022-06-28 15:39:04 +02:00
Armin Schrenk 2b19fd6852 adjust lock failed dialog 2022-06-28 10:32:47 +02:00
Armin Schrenk a5e9837c99 Adjust design of unlock invalid mp dialog 2022-06-28 10:15:39 +02:00
Armin Schrenk 81a1052a76 cleanup 2022-06-27 19:08:00 +02:00
Armin Schrenk ab2d740722 adjust choose masterkeyfile dialog 2022-06-27 19:04:14 +02:00
Armin Schrenk bf3a62a6b4 Rename translation key for unlock window title 2022-06-27 18:56:53 +02:00
Armin Schrenk d163d02077 Change window title for removeVault dialog 2022-06-27 18:49:26 +02:00
Armin Schrenk 677607d210 adjust remove vault dialog 2022-06-27 18:24:54 +02:00
Armin Schrenk fd3c8d7f78 Adjust design of lock_forced dialog 2022-06-27 18:07:41 +02:00
Armin Schrenk cce74ef5e4 add TODO 2022-06-27 17:52:49 +02:00
Armin Schrenk 74058238ec adjust design of wrong file alert 2022-06-27 17:52:34 +02:00
Armin Schrenk 78e458f01a rename recovery_create.fxml file 2022-06-27 17:26:21 +02:00
Armin Schrenk 07cfef2e41 adjust design of auth for recovery key 2022-06-27 17:21:45 +02:00
Armin Schrenk 35c5bd03de Changed design for unlock success dialog 2022-06-27 16:26:35 +02:00
Jan-Peter Klein 7cc1a1728c removed unused parameter 2022-06-27 14:40:20 +02:00
Cryptobot a79bf1952d New translations strings.properties (Vietnamese)
[ci skip]
2022-06-25 18:41:43 +02:00
Armin Schrenk c7ad587aa2 Changed design for quit screen as template for other dialogs 2022-06-24 15:34:01 +02:00
Armin Schrenk 22b842e6c2 Rename styleclass 2022-06-24 12:27:38 +02:00
Armin Schrenk f96c52cdb1 Disable Password tab if vault.config.keyid.scheme is not masterkeyfile 2022-06-24 10:26:34 +02:00
Cryptobot ca426f6535 New translations strings.properties (Vietnamese)
[ci skip]
2022-06-22 17:10:33 +02:00
Armin Schrenk d6f489ea98 bump dependencies 2022-06-22 12:36:00 +02:00
Tobias HagemannandGitHub 8043a4ea10 Update README.md 2022-06-22 10:42:59 +02:00
Tobias HagemannandGitHub e5cb271e28 Update README.md 2022-06-22 10:42:13 +02:00
Armin Schrenk 45f038bae2 Merge branch 'develop' into feature/hub
# Conflicts:
#	dist/mac/dmg/build.sh
#	dist/win/build.ps1
#	pom.xml
2022-06-22 09:55:02 +02:00
Cryptobot 3580728a5a New translations strings.properties (Hindi)
[ci skip]
2022-06-21 12:42:13 +02:00
Cryptobot 749c5d9f24 New translations strings.properties (Vietnamese)
[ci skip]
2022-06-21 00:39:49 +02:00
Cryptobot 6ae872fa85 New translations strings.properties (Vietnamese)
[ci skip]
2022-06-20 21:27:37 +02:00
Cryptobot 773fa9804a New translations strings.properties (Hindi)
[ci skip]
2022-06-20 14:07:39 +02:00
Cryptobot cfa9beec8b New translations strings.properties (Hindi)
[ci skip]
2022-06-20 11:11:16 +02:00
Cryptobot 5f4d13f662 New translations strings.properties (Japanese)
[ci skip]
2022-06-20 04:29:20 +02:00
Cryptobot f0d69b645b New translations strings.properties (Hindi)
[ci skip]
2022-06-19 13:23:39 +02:00
Cryptobot 797724e640 New translations strings.properties (Portuguese, Brazilian)
[ci skip]
2022-06-17 01:12:50 +02:00
Cryptobot 3488db8c0e New translations strings.properties (Vietnamese)
[ci skip]
2022-06-15 21:47:37 +02:00
Cryptobot d53e0b24a4 New translations strings.properties (German)
[ci skip]
2022-06-15 15:40:48 +02:00
Cryptobot 347a7b3ef7 New translations strings.properties (Vietnamese)
[ci skip]
2022-06-15 12:35:10 +02:00
Cryptobot ff80585a2f New translations strings.properties (Portuguese, Brazilian)
[ci skip]
2022-06-15 09:29:10 +02:00
Armin Schrenk 8c65bd19c2 Make FUSE the default volume impl 2022-06-13 10:00:14 +02:00
Cryptobot ad7a8835b3 New translations strings.properties (German)
[ci skip]
2022-06-07 20:28:24 +02:00
Cryptobot 9f2b5b6ee3 New translations strings.properties (Croatian)
[ci skip]
2022-06-06 11:29:54 +02:00
Cryptobot 15726c6b21 New translations strings.properties (Indonesian)
[ci skip]
2022-06-03 05:53:25 +02:00
Cryptobot 1c824a37b0 New translations strings.properties (French)
[ci skip]
2022-06-01 12:36:32 +02:00
Cryptobot 7c04ba48a8 New translations strings.properties (Turkish)
[ci skip]
2022-06-01 12:36:24 +02:00
Jan-Peter Klein 6f33cf8df7 removed checkbox in quit prompt 2022-05-31 14:09:12 +02:00
Jan-Peter Klein 8f85777887 implemented functionality of feature request issue #1713 "On closing of the application: Always lock and quit – without asking" 2022-05-31 11:18:20 +02:00
Cryptobot 931d6f0048 New translations strings.properties (Polish)
[ci skip]
2022-05-30 22:16:25 +02:00
Cryptobot 692b3828b8 New translations strings.properties (Sinhala)
[ci skip]
2022-05-28 19:53:25 +02:00
Cryptobot 320be05a66 New translations strings.properties (Sinhala)
[ci skip]
2022-05-28 10:53:36 +02:00
Cryptobot 2c7b407e94 New translations strings.properties (Sinhala)
[ci skip]
2022-05-28 07:54:01 +02:00
Cryptobot 20c66a6005 New translations strings.properties (Sinhala)
[ci skip]
2022-05-27 21:40:27 +02:00
JaniruTECandGitHub e6ff7a6d88 Merge pull request #2259 from cryptomator/fix/2258-catching-executors
Added catching executors
2022-05-27 18:24:55 +02:00
Sebastian StenzelandGitHub aca55734b0 Use Xcode Notarization Action (#2267)
[ci skip]
2022-05-27 12:21:13 +02:00
JaniruTEC f740a93b6c Applied suggestions from code review
Changed signature of afterExecuteTask
See: https://github.com/cryptomator/cryptomator/pull/2259/files#r875548549

--

Changed name of "afterExecute0" to "afterExecuteInternal"
See: https://github.com/cryptomator/cryptomator/pull/2259/files#r875551994
2022-05-27 01:48:11 +02:00
e9a71827ed Apply suggestions from code review
Establishing symmetry with `afterExecuteTask`

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-27 01:36:10 +02:00
Sebastian Stenzel dc3f80ffc9 fix build 2022-05-26 12:04:33 +02:00
Cryptobot 75633a10bd New translations strings.properties (Czech)
[ci skip]
2022-05-25 13:41:37 +02:00
Cryptobot dda526b83e New translations strings.properties (Ukrainian)
[ci skip]
2022-05-24 00:49:03 +02:00
Tobias Hagemann 47f7c437fe added separate definition for green tones used for password strength indicator 2022-05-23 13:39:07 +02:00
Tobias Hagemann 15e46f8ca5 Merge branch 'feature/optimize-logo' into develop 2022-05-23 13:17:38 +02:00
Tobias Hagemann 5b13553866 cleanup
[ci skip]
2022-05-23 13:17:02 +02:00
Tobias Hagemann e14d1f4028 cleaned up css files
[ci skip]
2022-05-23 13:15:24 +02:00
Cryptobot a7405b4fca New translations strings.properties (Dutch)
[ci skip]
2022-05-21 11:41:58 +02:00
Jan-Peter Klein 8c52c740d3 removed /bot/bot.png 2022-05-20 11:30:23 +02:00
Jan-Peter Klein 1d70eecbb7 added title-logo file and rearranged files 2022-05-20 11:24:51 +02:00
3d95276a7f Feature/2200 illegal use of builders (#2262)
* Replaced Builder in VaultComponent with Factory
* migrate VaultOptionsComponent

Co-authored-by: JaniruTEC <JaniruTEC@gmail.com>
2022-05-19 22:07:56 +02:00
Cryptobot 88020fcaf4 New translations strings.properties (Ukrainian)
[ci skip]
2022-05-19 15:51:15 +02:00
Cryptobot 4e89b6365a New translations strings.properties (Chinese Traditional, Hong Kong)
[ci skip]
2022-05-17 19:47:14 +02:00
Cryptobot 52396f68f5 New translations strings.properties (Chinese Traditional)
[ci skip]
2022-05-17 19:47:13 +02:00
Tobias Hagemann 5233e6b690 improved cleanup in manual build script for macos
[ci skip]
2022-05-17 18:33:02 +02:00
Cryptobot 3c469c80be New translations strings.properties (Chinese Traditional)
[ci skip]
2022-05-17 16:44:35 +02:00
JaniruTEC 0040c8a5f8 Added catching executors 2022-05-17 16:30:23 +02:00
Cryptobot e8751d2116 New translations strings.properties (Swahili, Tanzania)
[ci skip]
2022-05-17 10:51:44 +02:00
Cryptobot 9bc439a913 New translations strings.properties (Indonesian)
[ci skip]
2022-05-16 21:08:12 +02:00
Armin Schrenk 4989eacaf9 Fixes #2256 2022-05-16 12:58:36 +02:00
Cryptobot c7b257eb66 New translations strings.properties (Polish)
[ci skip]
2022-05-15 23:47:27 +02:00
Cryptobot 2f76b724a7 New translations strings.properties (Swedish)
[ci skip]
2022-05-14 18:55:21 +02:00
Sebastian Stenzel cd709260ce ignore autogenerated file
[ci skip]
2022-05-13 15:48:15 +02:00
Sebastian Stenzel 87202453b1 added Swahili to language picker
[ci skip]
2022-05-13 15:43:43 +02:00
4786193731 New Crowdin updates (#2212)
[ci skip]

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-13 15:28:33 +02:00
90f523bee8 parameterize patchWebDAV.ps1 and add default values to .bat file (#2251)
Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2022-05-13 15:11:41 +02:00
Tobias Hagemann 78bb996072 updated integrations-mac dependency 2022-05-13 13:42:40 +02:00
Armin Schrenk 822963e1a9 bump cryptofs
Fixes #2238
2022-05-13 13:17:11 +02:00
Tobias HagemannandGitHub 773a5d3570 Merge pull request #2244 from cryptomator/feature/parameterize-mac-build-script
Parameterized macOS build script
2022-05-12 16:19:14 +02:00
Armin SchrenkandGitHub acc5277258 Merge pull request #2242 from cryptomator/feature/parameterize-win-build-script
Parameterize local windows build script
2022-05-12 16:17:34 +02:00
Armin SchrenkandGitHub 4ecb98a5e6 Merge pull request #2245 from cryptomator/feature/location-preset-enum
Enumeenize well-known local cloud storage paths
2022-05-12 16:14:43 +02:00
Armin Schrenk 75093f1c09 Make lauchner class configurable 2022-05-12 16:12:20 +02:00
Tobias Hagemann 0351595243 parameterized MODULE_AND_MAIN_CLASS 2022-05-12 16:07:47 +02:00
Armin SchrenkandSebastian Stenzel 5ab74affb5 Refactor LocationPreset enum:
* use static inner class
* add getDisplayName method
* make instance variables final

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-12 13:47:57 +02:00
Sebastian Stenzel 8230b74c78 moved file picker filter glob to Constants.java 2022-05-12 13:17:33 +02:00
Armin Schrenk 5832e8cd49 Apply suggestions from code review
Co-authored by: Tobias Hagemann <tobihagemann@users.noreply.github.com>
2022-05-12 12:15:16 +02:00
Tobias Hagemann 0220af24d0 actually use vault config filename from constants when creating vault 2022-05-12 10:51:00 +02:00
Armin Schrenk 3e6d81ef1b Use parameter for main jar glob pattern 2022-05-12 09:15:04 +02:00
Tobias Hagemann 852daaa88e parameterized mac build script 2022-05-12 09:14:10 +02:00
Armin Schrenk 7dfc4d9ded allow custom name for c9r/c9s icon file 2022-05-12 09:14:03 +02:00
Armin Schrenk 98002f63dc Use enum for well-known local cloud storage paths and move class to commons package 2022-05-12 08:45:03 +02:00
Armin Schrenk 80696972cb allow upgrade-uuid to be customized in build script 2022-05-11 13:00:32 +02:00
Sebastian Stenzel a3d4eb6048 adjust build to integrations-win 1.1.1 2022-05-11 12:51:48 +02:00
Armin Schrenk 0f88e6c2fe fix errors 2022-05-11 12:48:56 +02:00
Armin Schrenk 3524f273de Merge branch 'develop' into feature/parameterize-win-build-script
# Conflicts:
#	dist/win/build.ps1
2022-05-11 12:48:31 +02:00
Armin Schrenk fb3d64c43f update integrations-win dependency 2022-05-11 12:41:33 +02:00
Armin Schrenk 90d43f28d9 eradicate last hard coded app name occurences in build script 2022-05-11 12:25:53 +02:00
Armin Schrenk a29f10a504 refactor build scipt to be easiert customizable 2022-05-11 12:22:29 +02:00
Armin Schrenk 6702747652 separate cryptomator labels and asset paths from installer file 2022-05-10 18:35:04 +02:00
Armin Schrenk a8b920698d Generify error message in installer 2022-05-10 16:32:59 +02:00
Sebastian Stenzel 0b2685f271 adjusted .gitignore to latest IntelliJ IDEA standards
see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems

[ci skip]
2022-05-10 15:23:02 +02:00
Sebastian Stenzel 120be431ee prepare Cryptomator to eventually change the update check URL 2022-05-10 11:36:40 +02:00
Armin Schrenk f50eb1f047 Revert "Reset webdavport field if changes are not applied and focus lost"
This reverts commit c6e9e33feb.
2022-05-09 13:06:15 +02:00
Armin SchrenkandGitHub 32d6661a72 Merge pull request #2235 from cryptomator/feature/automate-winget
Add automatic release to winget to msi/exe ci job
2022-05-06 17:44:33 +02:00
Armin Schrenk f57d8dab6d instead of defining pwsh as default for winget job, only use it in specific step 2022-05-06 17:34:04 +02:00
Armin Schrenk 5ba0e674f2 Add automatic release to winget to msi/exe ci job 2022-05-06 13:13:45 +02:00
Sebastian Stenzel 55f67785cd don't cause the job to fail if the upload fails
[ci skip]
2022-05-05 09:05:05 +02:00
Sebastian Stenzel 6d082330e6 replace failing curl with http-request-action
[ci skip]
2022-05-05 08:00:21 +02:00
Sebastian Stenzel 0fdc5b2826 fix duplicate arm64 dmg and duplicate appimage
[ci skip]
2022-05-05 07:50:06 +02:00
Sebastian Stenzel 31f77e62ea configurable time interval 2022-05-05 07:44:28 +02:00
Sebastian Stenzel 3435a6701a round timestamp to latest 15min mark 2022-05-05 07:41:36 +02:00
Sebastian Stenzel d4367b97a6 adjust interval to 900s, when reporting stats every 15min 2022-05-05 07:39:05 +02:00
Sebastian StenzelandGitHub 1151157dff Report Download Stats (#2230)
[ci skip]
2022-05-05 07:32:19 +02:00
Julian Raufelder e9c58ac3d5 Merge branch 'hotfix/1.6.10' into develop 2022-05-03 16:38:51 +02:00
Julian Raufelder 2833015ff1 Merge branch 'hotfix/1.6.10' 2022-05-03 16:18:05 +02:00
Julian Raufelder 12ef32835c Prepare 1.6.10
[ci skip]
2022-05-03 16:15:33 +02:00
Julian Raufelder eae5f4d870 Fix truncation if new size is larger than the current file size when FUSE is used to mount the vault
Fixes #2218
2022-05-03 16:10:41 +02:00
JaniruTECandGitHub 30e1c989d0 Fixed typo 2022-05-01 00:46:36 +02:00
Sebastian Stenzel d4b3eff42f restored success/error redirects 2022-04-28 12:59:27 +02:00
Sebastian Stenzel 9f0e269baf Merge branch 'develop' into feature/hub
# Conflicts:
#	pom.xml
#	src/main/resources/license/THIRD-PARTY.txt
2022-04-28 12:55:55 +02:00
Armin Schrenk c6e9e33feb Reset webdavport field if changes are not applied and focus lost 2022-04-28 12:00:06 +02:00
Armin Schrenk 62c7f95f7c removed logging of unused property 2022-04-28 11:54:51 +02:00
Armin Schrenk a304427973 Remove last reference of "donationkey" 2022-04-28 11:53:54 +02:00
Sebastian Stenzel c04597eca5 Merge branch 'main' into develop
[ci skip]
2022-04-27 15:52:15 +02:00
Sebastian Stenzel ba1945ebb8 Merge branch 'release/1.6.9' 2022-04-27 15:51:50 +02:00
Sebastian Stenzel e9551a076d Merge branch 'develop' into release/1.6.9 2022-04-27 15:30:59 +02:00
Sebastian StenzelandGitHub ccd3da3b09 Merge pull request #2096 from cryptomator/feature/integrations-api-1.1.0
Integrations API 1.1.0
2022-04-27 15:29:47 +02:00
Sebastian Stenzel 5e06fa7385 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	pom.xml
2022-04-27 15:23:38 +02:00
Sebastian Stenzel c3969f2f33 bumped integrations version 2022-04-27 15:19:54 +02:00
Sebastian Stenzel 0a8cc7ed1e Merge branch 'develop' into release/1.6.9 2022-04-27 13:19:41 +02:00
CryptobotandGitHub 74f5be1605 New Crowdin updates (#2146)
[ci skip]
2022-04-27 13:19:03 +02:00
Sebastian Stenzel 3c376c4bf6 add -Dsun.java2d.metal=true, which fixes #2194 2022-04-27 13:18:25 +02:00
Armin SchrenkandGitHub fedb62d99a Merge pull request #2175 from cryptomator/feature/binary-stylesheets
Prefer binary stylesheets
2022-04-27 13:06:18 +02:00
Armin SchrenkandGitHub 8f97235b9a Merge pull request #2189 from cryptomator/feature/on-the-fly-license
Generate app license on release/ installer build and remove commited third party file
2022-04-27 13:05:53 +02:00
Julian Raufelder d127121007 Update metainfo.xml to 1.6.9
[ci skip]
2022-04-26 19:51:43 +02:00
Armin Schrenk d6a55d3006 preparing 1.6.9 2022-04-26 18:14:40 +02:00
Armin Schrenk d3d0cef2bb bumping dependencies 2022-04-26 18:07:37 +02:00
Armin Schrenk c514dc6235 fixes #2205 2022-04-26 17:58:37 +02:00
Armin Schrenk 98b3b14e6a Reverted bundle license to show links again and adjusted license template
* only those are depicted as clickable http links
2022-04-26 17:48:05 +02:00
Sebastian Stenzel 9d4f9c12b9 outsourced authorization flow to https://github.com/coffeelibs/tiny-oauth2-client 2022-04-25 16:28:54 +02:00
Sebastian Stenzel d1c4eda072 Merge branch 'develop' into feature/hub
# Conflicts:
#	.github/workflows/appimage.yml
#	dist/linux/appimage/build.sh
#	dist/linux/debian/rules
#	dist/mac/dmg/build.sh
#	dist/win/build.ps1
2022-04-25 15:30:56 +02:00
Armin Schrenk 835a5592fc use pwsh for license generation to circumvent slash problem in url 2022-04-22 17:18:28 +02:00
Armin Schrenk c429de6a66 third slash's the charm 2022-04-22 16:52:49 +02:00
Armin Schrenk afe7737129 wrong usage of github workspace var 2022-04-22 16:25:00 +02:00
Armin Schrenk 94b3ea37d6 use absolute path in workflow 2022-04-22 16:19:09 +02:00
Tobias Hagemann 4d7e16d58e updated license templates 2022-04-22 12:34:25 +02:00
Armin Schrenk 2f17d80fe6 Revert "removed license dir, renamed merge file and adjusted paths"
This reverts commit d053367f83.
2022-04-22 11:51:22 +02:00
Armin Schrenk fbc598fed7 Reordered steps in vault creation and renamed methods 2022-04-22 10:40:18 +02:00
Armin Schrenk d053367f83 removed license dir, renamed merge file and adjusted paths 2022-04-22 09:49:04 +02:00
Armin Schrenk 9f5c31e4e7 Move app specific license template to resources directory 2022-04-21 14:21:40 +02:00
Armin Schrenk 68d4b440e1 use spaces instead of tabs in app license file 2022-04-20 17:47:50 +02:00
Armin Schrenk bcfba21900 Create third-party-license file in target directory, use plugin defaults to let it placed inside jar 2022-04-20 17:46:58 +02:00
Armin Schrenk c1801e3d22 fixed typo 2022-04-20 12:12:25 +02:00
Armin Schrenk bff0194f3e Fixed wrong file format and url of lcense merge file 2022-04-20 12:12:00 +02:00
Armin Schrenk 75f96eb08f removing static third-party-licenses file 2022-04-20 10:37:13 +02:00
Armin SchrenkandSebastian Stenzel 4d1d6f449e Apply suggestions from code review
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-04-20 10:31:25 +02:00
Armin Schrenk eb0a2c1267 On macOS show example in dark mode when adding existing vault 2022-04-19 17:34:12 +02:00
Armin Schrenk 51d715caa1 apply same config to license generation 2022-04-19 16:47:33 +02:00
Sebastian Stenzel b55bd8075c relax stale bot
[ci skip]
2022-04-18 09:18:51 +02:00
Sebastian StenzelandGitHub d368b01038 Merge pull request #2105 from cryptomator/feature/gtk2-launcher
Add additional GTK2 launcher to AppImage, fixes #1432
2022-04-18 08:24:13 +02:00
Tobias Hagemann dc3894290b updated license template for macos, fixed manual build script 2022-04-14 13:52:10 +02:00
Armin Schrenk 597bbdbdb8 for mac build generate license on the fly
References 7172462b4b
2022-04-13 17:53:44 +02:00
Sebastian Stenzel 2b255ed101 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
2022-04-13 07:21:59 +02:00
Sebastian Stenzel 9ece1f66a1 bumped API to 1.1.0-rc1 2022-04-13 07:16:19 +02:00
JaniruTECandGitHub e71b375437 Added "--version" (short: "-v")
Merge pull request #2064 from cryptomator/1996-cli-version
2022-04-13 00:57:08 +02:00
JaniruTEC bcb2b088ba Applied suggestions from code review 2022-04-13 00:46:11 +02:00
JaniruTEC f123dff0cd Merge remote-tracking branch 'origin/develop' into 1996-cli-version
# Conflicts:
#	src/main/java/org/cryptomator/launcher/Cryptomator.java
2022-04-12 17:44:13 +02:00
Sebastian Stenzel 0ace5bb42b fix copy-paste error in log message 2022-04-11 12:34:50 +02:00
Sebastian Stenzel b656b591ed prefer .bss over .css 2022-04-11 12:32:07 +02:00
Sebastian Stenzel 564370ec7a compile .css into .bss file 2022-04-11 12:31:57 +02:00
Tobias HagemannandGitHub 7dd0f0cb31 Merge pull request #2173 from cryptomator/feature/preferences-interface
Moved interface preferences to its own tab
2022-04-11 10:49:14 +02:00
Tobias Hagemann 79e1285b38 increased margins and spacings even further 2022-04-11 10:48:47 +02:00
Sebastian Stenzel 9c26d17733 log durations of application start 2022-04-11 09:36:34 +02:00
Sebastian Stenzel 69f3a2bd5a log errors when showing main window 2022-04-11 09:35:35 +02:00
Sebastian Stenzel 9a9ef6c583 updated license file
[ci skip]
2022-04-11 09:23:53 +02:00
Sebastian Stenzel a8e73350a6 renamed translation keys 2022-04-09 15:30:16 +02:00
Sebastian Stenzel c8e131c49f reordered controls 2022-04-09 15:25:27 +02:00
Sebastian Stenzel 3b8f2adedf increased margins and spacings 2022-04-09 15:18:44 +02:00
Sebastian Stenzel 9b00cd923c moved interface settings to separate preferences tab 2022-04-09 15:13:53 +02:00
Sebastian Stenzel 12d38335d8 increase width of preferences window, which should fix #2107 2022-04-09 15:05:05 +02:00
Sebastian Stenzel 2108a77e12 adjusted build scripts for Cryptomator Hub 2022-04-08 20:45:22 +02:00
Sebastian Stenzel f3bfe7b64c Merge branch 'develop' into feature/hub
# Conflicts:
#	.github/workflows/release.yml
#	.idea/runConfigurations/Cryptomator_Linux.xml
#	.idea/runConfigurations/Cryptomator_Linux_Dev.xml
#	.idea/runConfigurations/Cryptomator_Windows.xml
#	.idea/runConfigurations/Cryptomator_Windows_Dev.xml
#	.idea/runConfigurations/Cryptomator_macOS.xml
#	.idea/runConfigurations/Cryptomator_macOS_Dev.xml
#	pom.xml
#	src/main/java/module-info.java
#	src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/SelectMasterkeyFileController.java
#	src/main/resources/license/THIRD-PARTY.txt
2022-04-08 20:27:02 +02:00
Sebastian StenzelandGitHub 752e61219c Merge pull request #2161 from cryptomator/feature/check-vaultconfig-sig-during-key-recovery
Prevent key recovery for foreign vault
2022-04-05 12:03:12 +02:00
Sebastian Stenzel d308ee626a more descriptive name
[ci skip]
2022-04-05 09:27:44 +02:00
Sebastian Stenzel d861ef0d22 Make use of extended validation to prevent applying a recovery key that didn't sign the vault config 2022-04-05 09:21:33 +02:00
Sebastian Stenzel 78a50548ab optionally allow extended key validation in RecoveryKeyFactory#validateRecoveryKey 2022-04-05 09:19:58 +02:00
Sebastian Stenzel 4c363a9abc dealing with deprecation 2022-04-05 08:14:14 +02:00
Sebastian Stenzel 9ed7438f05 bump zxcvbn4j version, fixes #979 2022-04-05 08:00:05 +02:00
Sebastian Stenzel 5a4f714c93 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
2022-04-05 07:49:25 +02:00
Sebastian Stenzel c03bc9ed0b removed unused imports
[ci skip]
2022-04-05 07:47:54 +02:00
Sebastian Stenzel 7c772e2767 removed user.language=en and jdk.gtk.version=2 flags from run configurations
[ci skip]
2022-04-05 07:46:17 +02:00
Sebastian StenzelandGitHub 3136e22414 Merge pull request #2150 from cryptomator/feature/language-switcher 2022-04-04 20:59:23 +02:00
Sebastian Stenzel 508b9f5c64 test if bundle for supported locales exist and are not empty 2022-04-04 15:57:01 +02:00
Sebastian Stenzel d10c8fcf17 amend settings deserialization test 2022-04-04 15:13:27 +02:00
Sebastian Stenzel ceb3cbc43f remove unused imports 2022-04-04 15:10:35 +02:00
Sebastian Stenzel ec909ce723 rethrow AWTException as TrayMenuException 2022-04-04 15:10:24 +02:00
Sebastian Stenzel 2ce7e84e3c Merge branch 'develop' into feature/gtk2-launcher 2022-04-04 06:58:07 +02:00
Sebastian Stenzel d7da78fc07 adjust to latest API 2022-04-03 15:26:04 +02:00
Sebastian Stenzel e93e31e745 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	pom.xml
#	src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
#	src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
#	src/main/resources/license/THIRD-PARTY.txt
2022-04-03 12:53:08 +02:00
Sebastian Stenzel 5dc8fd2582 made constant final 2022-04-03 12:25:15 +02:00
Sebastian Stenzel 77d81acb1e added "English" as first option 2022-04-02 15:54:37 +02:00
Sebastian Stenzel 71d346eddd apply chosen language at application start 2022-04-02 12:17:10 +02:00
Sebastian Stenzel d9aa6ae91a display "language" chooser in general preferences 2022-04-02 12:16:57 +02:00
Sebastian Stenzel c130441700 add setting for user-chosen "language" 2022-04-02 12:15:09 +02:00
Sebastian Stenzel 6af016f1fe log errors occuring in async "show window" tasks 2022-04-02 11:51:34 +02:00
Sebastian Stenzel ec2524f6ff use correct StartupWMClass (fixes #1955)
[ci skip]
2022-04-02 10:06:15 +02:00
Sebastian Stenzel 27d4e00210 Merge branch 'develop' into feature/gtk2-launcher 2022-04-02 10:01:25 +02:00
Sebastian StenzelandGitHub 0cd2ecb0b3 Merge pull request #2139 from cryptomator/feature/javafx-launcher
Refactored Launcher Component Graph
2022-04-02 09:59:45 +02:00
Sebastian Stenzel a143ecdcf8 Refactored use of StageFactory 2022-04-01 17:42:29 +02:00
Sebastian Stenzel 2d830cdb31 easier-to-understand if/else for combinations of "startHidden" and "hasTrayIcon"
fixes bug when startHidden was false and hasTrayIcon was true
2022-04-01 15:36:20 +02:00
Sebastian Stenzel 887e0332da changed misleading comment
[ci skip]
2022-04-01 14:50:27 +02:00
Sebastian Stenzel c1504e29c1 added documentation
[ci skip]
2022-04-01 14:48:14 +02:00
Sebastian Stenzel 0bb6e64d83 when "starting hidden", only minimize when there is no tray icon 2022-04-01 14:01:58 +02:00
Sebastian Stenzel c7d1b9dbd6 don't prompt user if quitting directly after app start 2022-03-31 16:32:17 +02:00
Armin Schrenk a4c50da222 replicate naming scheme of win exe installer in CI job to local build 2022-03-31 11:12:50 +02:00
Sebastian StenzelandGitHub a558135fec Rename .exe Installer Bundle (#2145) 2022-03-31 11:03:15 +02:00
Sebastian Stenzel 7df449524a fixed lock workflow 2022-03-31 10:03:02 +02:00
Sebastian Stenzel d475444829 fixed some code smells 2022-03-31 09:59:16 +02:00
Sebastian Stenzel 828f32d3f6 Merge branch 'develop' into feature/gtk2-launcher 2022-03-31 08:57:27 +02:00
Sebastian Stenzel dc6b5774b8 Merge branch 'develop' into feature/javafx-launcher
# Conflicts:
#	pom.xml
2022-03-31 08:55:49 +02:00
Sebastian Stenzel 28db04e621 arm av allowlisting
[ci skip]
2022-03-31 08:37:00 +02:00
Sebastian Stenzel e2184ec009 upload .msi and .exe to AV allowlisting servers 2022-03-31 08:20:49 +02:00
Sebastian Stenzel 5f1f95da74 Merge branch 'main' into develop
[ci skip]
2022-03-31 08:05:53 +02:00
Sebastian Stenzel 4b29f0b4f3 Merge branch 'release/1.6.8' 2022-03-30 18:03:54 +02:00
Sebastian Stenzel f8bcbda8d7 add safeguard to prevent release in case of missing signature 2022-03-30 18:03:15 +02:00
Sebastian Stenzel e368087c3e Merge branch 'develop' into release/1.6.8 2022-03-30 18:02:13 +02:00
Sebastian Stenzel ddb5da1d79 reintroduce condition for signing the tarball
[ci skip]
2022-03-30 17:56:01 +02:00
Sebastian Stenzel d2374fb8cd deleted yet another use of github.ref 2022-03-30 17:50:45 +02:00
Sebastian Stenzel c73aa49b59 always run the tarball signature step (for testing ${{ github.ref_name }}) 2022-03-30 17:46:31 +02:00
Armin Schrenk de86760a75 Merge branch 'release/1.6.8' 2022-03-30 17:23:49 +02:00
Armin Schrenk bfd8a62015 preparing 1.6.8 2022-03-30 17:22:25 +02:00
9292a3c89c Sign again tarball in release using GPG (#2140)
Co-authored-by: infeo <armin.schrenk@skymatic.de>
Co-authored-by: overheadhunter <sebastian.stenzel@skymatic.de>
2022-03-30 17:19:27 +02:00
Julian Raufelder 732fc2f3dd Update metainfo.xml to 1.6.8
[ci skip]
2022-03-30 17:17:48 +02:00
CryptobotandGitHub 9fb00fcc38 New Crowdin updates (#2119)
New translations strings.properties

Chinese Traditional; German; 

[ci skip]
2022-03-30 17:16:49 +02:00
Armin Schrenk 12b58a8a32 Update third-party license file 2022-03-30 17:10:10 +02:00
Armin Schrenk 338eb45e24 bump dependencies 2022-03-30 16:17:06 +02:00
Armin Schrenk e8c63dd3f3 Fixes #2138, closes #2127
Return code 0 when running application found
2022-03-30 16:00:05 +02:00
Armin Schrenk 2de23f8fff bump jwt lib to fix possible vulnerability 2022-03-30 15:56:22 +02:00
Armin Schrenk 1a0624cc81 Closes #2130
Prevent Windows installation if app is running
2022-03-29 23:47:03 +02:00
Sebastian Stenzel e907360419 refactored launcher, deleted UiLauncherModule
component graph is now: Main → FxApplicationComponent → Tray, MainWindow, etc
2022-03-29 20:39:46 +02:00
Armin Schrenk ef6f87bb19 Fixes #2126
Remove deprecated env variable
2022-03-29 17:24:01 +02:00
Sebastian Stenzel 73bbcdcca1 start javafx via Application.launch(...) again 2022-03-28 17:52:39 +02:00
Sebastian Stenzel 6da8fc1f70 optionally specify version in manually dispatched builds 2022-03-25 08:20:18 +01:00
Armin Schrenk 49e90515a1 Fixes #2125 2022-03-24 17:49:12 +01:00
Armin Schrenk 983c35eb37 Remove Codacy badge and ci action 2022-03-24 17:35:36 +01:00
Armin Schrenk 1f0c12ef7a Fix wrong debug message 2022-03-24 12:28:21 +01:00
Sebastian Stenzel ba514c8896 Merge branch 'main' into develop
[ci skip]
2022-03-23 16:38:40 +01:00
Sebastian Stenzel 76c310f1fc Merge branch 'release/1.6.7'
# Conflicts:
#	pom.xml
2022-03-23 16:36:59 +01:00
Sebastian Stenzel 874f0ee611 preparing 1.6.7 2022-03-23 15:19:11 +01:00
Sebastian Stenzel 957f067e9b update license info
[ci skip]
2022-03-23 14:41:29 +01:00
CryptobotandGitHub b1931f2c13 New Crowdin updates (#1966) 2022-03-23 14:40:21 +01:00
Sebastian Stenzel 156e7bbb91 also run PR tests in virtual x environment (allowing UI tests) 2022-03-23 11:03:59 +01:00
Sebastian Stenzel 4abb41aebd Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
2022-03-23 11:02:48 +01:00
Sebastian Stenzel 67e1626de0 Merge branch 'develop' into feature/gtk2-launcher 2022-03-23 10:56:01 +01:00
Sebastian Stenzel aebe848bef run tests in virtual x environment (allowing UI tests) 2022-03-23 10:50:30 +01:00
Sebastian Stenzel c40ef2f0f2 bump webdav dependency, which fixes #1273 2022-03-23 10:42:58 +01:00
Sebastian Stenzel 8fa562aa60 bumped dependencies 2022-03-23 10:42:20 +01:00
Armin SchrenkandGitHub 303f84166f Merge pull request #2101 from kevinstsauveur/1583-vault-state-in-title-tray
Show vault locked/unlocked state in the vault title in the tray menu

Closes #1583
2022-03-22 12:45:21 +01:00
Armin Schrenk 9e594ea127 Fix not showing dir picker dialog when path does not exist 2022-03-22 12:44:06 +01:00
Sebastian StenzelandGitHub 88977ecf20 Merge pull request #1983 from cryptomator/feature/simplified-workflow-results 2022-03-21 21:19:32 +01:00
Kevin St-Sauveur 538cac3749 Changing prefix marker in unlocked vault name in tray menu 2022-03-21 15:37:32 -04:00
Armin Schrenk 30196f6a68 Display correct name during in uac dialog during install 2022-03-21 16:40:57 +01:00
Sebastian Stenzel 8bc79e684d fix syntax errors and switch back to bash 2022-03-21 15:28:49 +01:00
Sebastian Stenzel 7183efa073 attempt to fix broken CI build 2022-03-21 15:22:33 +01:00
Sebastian Stenzel 92f621cd25 Merge branch 'develop' into feature/simplified-workflow-results 2022-03-21 14:08:45 +01:00
snyk-botandSebastian Stenzel 19d26de4ec fix: pom.xml to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-2421244
2022-03-21 14:06:43 +01:00
Sebastian Stenzel d39c3969df share charset configuration
[ci skip]
2022-03-21 14:05:29 +01:00
Armin Schrenk c781755c6e use special sign process for windows exe bundle 2022-03-21 14:02:02 +01:00
Sebastian Stenzel 4a3d579b76 changed button title as suggested in review 2022-03-21 14:01:35 +01:00
Sebastian Stenzel 6bad74720f Merge branch 'develop' into feature/simplified-workflow-results 2022-03-21 13:56:24 +01:00
Sebastian Stenzel 77e2be22de updated .gitignore
[ci skip]
2022-03-18 18:06:31 +01:00
Sebastian Stenzel 7b4a3e1313 make sure not to upload unrelated artifacts to a release 2022-03-18 18:06:08 +01:00
Sebastian Stenzel 504a384225 fix envsubst 2022-03-18 17:58:21 +01:00
Sebastian Stenzel 550546c4b7 not required in yml multiline strings 2022-03-18 17:42:28 +01:00
Sebastian Stenzel 8dce21ea40 run envsubst as an independent step 2022-03-18 17:31:49 +01:00
Sebastian Stenzel b565f1d0c0 remove unused java binaries from jlinked runtime image 2022-03-18 17:26:28 +01:00
Sebastian Stenzel fab70ef8c9 run Cryptomator binaries instead of java from AppImage launcher .sh 2022-03-18 17:25:04 +01:00
Armin Schrenk 1c76c50a4b Merge commit 'c29cc9ab85f582c458ce981559d3b6f7bfd82803' into 1583-vault-state-in-title-tray 2022-03-16 16:12:39 +01:00
Kevin St-Sauveur 79e6a4cd48 Modify vault title when unlocked 2022-03-14 02:29:25 -04:00
Sebastian Stenzel ad6d5bfae9 resolved class name conflict 2022-03-07 17:42:56 +01:00
Sebastian Stenzel a404740ceb Updated to integrations-api 1.1.0-beta2 2022-03-07 17:35:48 +01:00
Sebastian Stenzel 9f7442c1c0 read clientId from vault config 2022-02-23 12:49:39 +01:00
Tobias Hagemann 64845f0ffa Merge branch 'hotfix/1.6.6' 2022-02-21 14:45:19 +01:00
Tobias Hagemann ee68f9d47b preparing 1.6.6 2022-02-21 14:45:01 +01:00
Tobias Hagemann 65bb6773cb Merge branch 'hotfix/1.6.6' 2022-02-21 14:25:05 +01:00
Tobias Hagemann 4bd4cd671b removed unnecessary entitlement in mac build
(cherry picked from commit 3e8690ca11)
2022-02-21 14:24:04 +01:00
Sebastian Stenzel 64abbc8205 Merge branch 'develop' into feature/hub
# Conflicts:
#	.github/workflows/release.yml
#	pom.xml
#	src/main/resources/license/THIRD-PARTY.txt
2022-02-18 09:38:25 +01:00
Sebastian Stenzel 165f190004 read device registration url from vault config 2022-02-18 09:13:33 +01:00
JaniruTEC 12a21bbb71 Added "--version" (short: "-v") 2022-02-17 02:39:48 +01:00
Sebastian Stenzel e1a72c41a5 remove unused imports 2022-01-19 20:01:48 +01:00
Sebastian Stenzel 4d4098e0e0 cleanup 2022-01-19 19:49:33 +01:00
Sebastian Stenzel d52e59d7a4 dedup by setting title when setting the scene 2022-01-19 19:08:54 +01:00
Sebastian Stenzel 9440fb186f Merge branch 'develop' into feature/simplified-workflow-results,
solving conflict with 5509033

# Conflicts:
#	src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingModule.java
2022-01-19 17:35:04 +01:00
Sebastian Stenzel 9856792921 replaced UserInteractionLock with CompletableFuture in LockWorkflow 2022-01-19 17:21:32 +01:00
Sebastian StenzelandGitHub b142904cc3 Merge pull request #1980 from cryptomator/feature/hub-integrated-device-registration
Register device from within client application
2021-12-16 18:02:00 +01:00
Sebastian Stenzel 806ffe704c convert Dagger modules to interfaces 2021-12-16 17:09:25 +01:00
Sebastian Stenzel 6ca87d13f5 renamed class 2021-12-16 17:01:56 +01:00
Sebastian Stenzel 983a4d0b0f move masterkey selection to subcomponent
and use CompletableFuture instead of UserInteractionLock + AtomicReference
2021-12-16 17:00:49 +01:00
Sebastian Stenzel e95853deac make test public, fixing InaccessibleObject error in maven build 2021-12-16 16:46:57 +01:00
Sebastian Stenzel d72d9f533c Replace CharBuffer and char[] instances by destroyable Passphrase 2021-12-16 16:41:07 +01:00
Sebastian Stenzel 85a5146d4b wipe pw before losing reference
[ci skip]
2021-12-16 13:59:10 +01:00
Sebastian Stenzel 0bece0f591 move passphrase entry to subcomponent
and use CompletableFuture instead of UserInteractionLock + AtomicReference
2021-12-16 13:56:59 +01:00
Sebastian Stenzel abc0f952e0 closing the window will cancel implicitly 2021-12-15 17:32:11 +01:00
Sebastian Stenzel e6f43d1f05 no need to run code in ui thread 2021-12-15 17:31:08 +01:00
Sebastian Stenzel 25b9722019 Use CompletableFuture instead of UserInteractionLock + AtomicReference 2021-12-15 17:19:59 +01:00
Sebastian Stenzel 157bef5df6 close dialog after successful device registration (to be improved lol) 2021-12-15 17:19:00 +01:00
Sebastian Stenzel ef281f810f register device via rest api 2021-12-14 17:35:24 +01:00
921dd8fe67 Add query to redirection to provide more context in the frontend (#1973)
Co-authored-by: Sebastian Stenzel <sebastian.stenzel@gmail.com>
2021-12-10 13:42:37 +01:00
Sebastian Stenzel fb580ff79d read masterkey from JWE 2021-12-09 14:03:39 +01:00
Sebastian Stenzel 0110e5bedd added JWE decryption helper 2021-12-09 14:02:39 +01:00
Sebastian Stenzel 3cd99f680a Merge branch 'develop' into feature/hub 2021-11-24 16:23:10 +01:00
Sebastian Stenzel 34e4383c1e adjust release build to support hub features 2021-11-24 14:46:21 +01:00
Tobias Hagemann d4c3f02d8a beautified hub_register_device 2021-11-18 17:02:58 +01:00
Sebastian Stenzel 839175a5e6 Merge branch 'develop' into feature/hub
# Conflicts:
#	pom.xml
2021-11-11 16:04:26 +01:00
Sebastian Stenzel 5924e443fb Merge branch 'develop' into feature/hub
# Conflicts:
#	.idea/runConfigurations/Cryptomator_Linux.xml
#	.idea/runConfigurations/Cryptomator_Linux_Dev.xml
#	.idea/runConfigurations/Cryptomator_Windows.xml
#	.idea/runConfigurations/Cryptomator_Windows_Dev.xml
#	.idea/runConfigurations/Cryptomator_macOS.xml
#	.idea/runConfigurations/Cryptomator_macOS_Dev.xml
#	pom.xml
2021-11-02 15:53:23 +01:00
Sebastian Stenzel 346ce67bc4 add "unauthorized device" scene 2021-08-24 14:43:26 +02:00
Sebastian Stenzel 1477bf07a9 use public key hash as device id 2021-08-24 13:59:51 +02:00
Sebastian Stenzel 056990151a adjusted vault config 2021-08-24 12:39:29 +02:00
Sebastian Stenzel fa86d890fe use application-global device key that requires a system keychain 2021-08-20 17:16:07 +02:00
Sebastian Stenzel 5922743f19 removed dead code
[ci skip]
2021-08-17 16:52:28 +02:00
Sebastian Stenzel afc853f5f5 append device registration params to hub url 2021-08-13 21:41:43 +02:00
Sebastian Stenzel be8243d9d1 cleanup 2021-08-13 21:41:10 +02:00
Sebastian Stenzel 1322b872b6 adjusted to new vault config format and unlock status codes 2021-08-13 19:59:31 +02:00
Sebastian Stenzel e46072c726 Merge branch 'develop' into feature/hub
[ci skip]
2021-08-13 12:13:49 +02:00
Sebastian Stenzel 33ed41307e Merge branch 'develop' into feature/hub 2021-08-12 17:39:25 +02:00
Sebastian Stenzel e865eaf412 added quick & dirty device registration 2021-08-12 17:38:50 +02:00
Sebastian Stenzel e866b64352 fix build 2021-08-12 17:05:59 +02:00
Sebastian Stenzel a3a96496b6 use new auth flow
talking directly to Authorization Server and Resource Server instead of web frontend
2021-08-12 16:18:22 +02:00
Sebastian Stenzel 75644a35ec cleanup
[ci skip]
2021-08-12 12:19:09 +02:00
Sebastian Stenzel ec09413575 renamed controller 2021-08-12 12:18:36 +02:00
Sebastian Stenzel f9c2807ce1 added basic OAuth 2.0 Authorization Code Flow + PKCE impl 2021-08-12 10:54:58 +02:00
Sebastian Stenzel 8075d33d39 fix key/iv extraction from sharedSecret 2021-08-11 16:13:14 +02:00
Sebastian Stenzel d7dcc46988 spec correction
[ci skip]
2021-08-10 13:23:29 +02:00
Sebastian Stenzel 01b2b47823 Switching to P-384 + X9.63 KDF SHA-256 + AES-GCM 2021-08-10 12:36:22 +02:00
Sebastian Stenzel d087a5fdde derive masterkey from received ECIES params 2021-08-09 23:03:36 +02:00
Sebastian Stenzel 43dbdb3e8f prepare local webserver for cross-origin requests 2021-08-09 18:11:44 +02:00
Sebastian Stenzel d938b1c3f7 keep window open while waiting for http callback 2021-07-30 16:55:05 +02:00
Sebastian Stenzel 59eda3159b Merge branch 'develop' into feature/hub 2021-07-30 14:04:07 +02:00
Sebastian Stenzel 7fabc6f52d spawn server listening on localhost, used for oauth redirect_uri 2021-07-29 16:57:28 +02:00
Sebastian Stenzel b21ea61342 add first draft for hub+http / hub+https keyloading scheme 2021-07-28 17:29:44 +02:00
Sebastian Stenzel 2952733a11 add PKCS12 support for on-demand creation and storage of an EC keypair 2021-07-28 17:04:12 +02:00
363 changed files with 13033 additions and 6898 deletions
+4 -2
View File
@@ -1,11 +1,13 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 180
daysUntilStale: 365
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 30
daysUntilClose: 90
# Issues with these labels will never be considered stale
exemptLabels:
- type:security-issue # never close automatically
- type:feature-request # never close automatically
- type:enhancement # never close automatically
- type:upstream-bug # never close automatically
- state:awaiting-response # handled by different bot
- state:blocked
- state:confirmed
+23 -12
View File
@@ -4,22 +4,26 @@ on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env:
JAVA_VERSION: 17
JAVA_VERSION: 19
jobs:
build:
name: Build AppImage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- id: versions
@@ -28,6 +32,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -53,11 +60,17 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--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
--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: ${{ steps.versions.outputs.semVerStr }}
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
- name: Run jpackage
run: >
${JAVA_HOME}/bin/jpackage
@@ -79,16 +92,17 @@ jobs:
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
run: |
mv appdir/Cryptomator Cryptomator.AppDir
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
envsubst '${REVISION_NO} ${SEMVER_STR}' < dist/linux/appimage/resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
@@ -100,9 +114,6 @@ jobs:
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
env:
REVISION_NO: ${{ steps.versions.outputs.revNum }}
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
run: |
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
@@ -146,6 +157,6 @@ jobs:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
*.AppImage
*.zsync
*.asc
cryptomator-*.AppImage
cryptomator-*.zsync
cryptomator-*.asc
+18 -13
View File
@@ -6,32 +6,32 @@ on:
types: [labeled]
env:
JAVA_VERSION: 17
JAVA_VERSION: 19
defaults:
run:
shell: bash
jobs:
jobs:
test:
name: Compile and Test
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Build and Test
run: >
xvfb-run
mvn -B verify
jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
@@ -42,13 +42,15 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Upload code coverage report
id: codacyCoverageReporter
if: "github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'pr:safe')"
run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
- name: Sign source tarball with key 615D449FE6E6A235
if: startsWith(github.ref, 'refs/tags/')
run: |
git archive --prefix="cryptomator-${{ github.ref_name }}/" -o "cryptomator-${{ github.ref_name }}.tar.gz" ${{ github.ref }}
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
continue-on-error: true
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Draft a release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
@@ -57,6 +59,9 @@ jobs:
discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true
files: |
cryptomator-*.tar.gz.asc
fail_on_unmatched_files: true
body: |-
:construction: Work in Progress
+14 -7
View File
@@ -10,26 +10,30 @@ on:
required: true
default: false
type: boolean
version:
description: 'Version'
required: false
env:
JAVA_VERSION: 17
JAVA_VERSION: 19
jobs:
build:
name: Build Debian Package
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install build tools
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19
- name: Setup Java
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- id: versions
@@ -38,6 +42,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -105,7 +112,7 @@ jobs:
cryptomator_*_amd64.deb
cryptomator_*.asc
- name: Publish on PPA
if: startsWith(github.ref, 'refs/tags/') || github.event.inputs.dput == 'true'
if: startsWith(github.ref, 'refs/tags/') || inputs.dput
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
- name: Publish Debian package on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
+63
View File
@@ -0,0 +1,63 @@
name: Report Download Stats
on:
schedule:
- cron: '0/15 * * * *' # run every 15 min - don't forget to adjust the "interval" in the json sent to the metrics endpoint
jobs:
report-download-stats:
runs-on: ubuntu-latest
steps:
- name: Get download count of latest releases
id: get-stats
uses: actions/github-script@v6
with:
script: |
const query = `query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name){
releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
isPrerelease
tagName
releaseAssets(first: 20) {
nodes {
name
downloadCount
}
}
}
}
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo
}
return await github.graphql(query, variables)
- name: Transform Results
id: transform-stats
run: |
TIME=$(($(date +%s) / $INTERVAL * $INTERVAL))
echo ${JSON_DATA} | jq --arg TIME "$TIME" --arg INTERVAL "$INTERVAL" -c '.repository.releases.nodes[] | select(.isPrerelease == false) | .tagName as $tagName | .releaseAssets.nodes[] | {filename: .name, downloads: .downloadCount, release: $tagName, time: ($TIME|tonumber), interval: ($INTERVAL|tonumber)}' > input.json
jq -c 'select(.filename|endswith("-x86_64.AppImage")) | {name: "github.releases.downloads", tags: ["file=AppImage", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("_amd64.deb")) | {name: "github.releases.downloads", tags: ["file=deb", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-x64.msi")) | {name: "github.releases.downloads", tags: ["file=msi", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-x64.exe")) | {name: "github.releases.downloads", tags: ["file=exe", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-arm64.dmg")) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=arm64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith(".dmg")) | select(.filename|endswith("-arm64.dmg")|not) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
RESULT=$(jq -s -c "." output.json)
echo "::set-output name=result::${RESULT}"
env:
INTERVAL: 900
JSON_DATA: ${{ steps.get-stats.outputs.result }}
- name: Upload Results
uses: fjogeleit/http-request-action@v1
with:
url: 'https://graphite-us-central1.grafana.net/metrics'
method: 'POST'
contentType: 'application/json'
bearerToken: ${{ secrets.GRAFANA_GRAPHITE_TOKEN }}
data: ${{ steps.transform-stats.outputs.result }}
continue-on-error: true # currently there seems to be a problem with the metrics endpoint, failing every now and then
+60
View File
@@ -0,0 +1,60 @@
name: Update Error Database
on:
discussion:
types: [created, edited, category_changed, answered, unanswered]
discussion_comment:
types: [created, edited, deleted]
jobs:
update-error-db:
runs-on: ubuntu-latest
if: github.event.discussion.category.name == 'Errors'
steps:
- name: Query Discussion Data
id: query-data
uses: actions/github-script@v6
with:
script: |
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
repository(owner: $owner, name: $name) {
discussion(number: $discussionNumber) {
id
upvoteCount
title
url
answer {
url
upvoteCount
}
comments {
totalCount
}
}
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo,
discussionNumber: context.payload.discussion.number
}
return await github.graphql(query, variables)
- name: Get Gist
id: get-gist
uses: andymckay/get-gist-action@master
with:
gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230
- name: Merge Error Code Data
run: |
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
jq -s '.[0] * .[1]' original.json new.json > merged.json
env:
DISCUSSION: ${{ steps.query-data.outputs.result }}
- name: Patch Gist
uses: exuanbo/actions-deploy-gist@v1
with:
token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }}
gist_id: accba9fb9555e7192271b85606f97230
gist_file_name: errorcodes.json
file_path: merged.json
+54 -45
View File
@@ -4,30 +4,50 @@ on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env:
JAVA_VERSION: 17
JAVA_VERSION: 19
jobs:
build:
name: Build Cryptomator.app
runs-on: macos-11
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-11
architecture: x64
output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app'
- os: [self-hosted, macOS, ARM64]
architecture: aarch64
output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
architecture: ${{ matrix.architecture }}
cache: 'maven'
- id: versions
- id: versions
name: Apply version information
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -53,7 +73,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--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
--strip-native-commands
--no-header-files
--no-man-pages
@@ -75,13 +95,16 @@ jobs:
--app-version "${{ steps.versions.outputs.semVerNum }}"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
@@ -95,6 +118,16 @@ jobs:
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
REVISION_NO: ${{ steps.versions.outputs.revNum }}
- name: Generate license for dmg
run: >
mvn -B license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
-Dlicense.includedScopes=compile
-Dlicense.excludedGroups=^org\.cryptomator
-Dlicense.failOnMissing=true
-Dlicense.licenseMergesUrl=file://${{ github.workspace }}/license/merges
- name: Install codesign certificate
run: |
# create variables
@@ -166,38 +199,20 @@ jobs:
--icon ".background" 128 758
--icon ".fseventsd" 320 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}.dmg dmg
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
- name: Install notarization credentials
if: startsWith(github.ref, 'refs/tags/')
run: |
# create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
security create-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
security set-keychain-settings -lut 900 ${KEYCHAIN_PATH}
security unlock-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
# import credentials from secrets
sudo xcode-select -s /Applications/Xcode_13.0.app
xcrun notarytool store-credentials "${NOTARIZATION_KEYCHAIN_PROFILE}" --apple-id "${NOTARIZATION_APPLE_ID}" --password "${NOTARIZATION_PW}" --team-id "${NOTARIZATION_TEAM_ID}" --keychain "${KEYCHAIN_PATH}"
env:
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
NOTARIZATION_PW: ${{ secrets.MACOS_NOTARIZATION_PW }}
NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
NOTARIZATION_TMP_KEYCHAIN_PW: ${{ secrets.MACOS_NOTARIZATION_TMP_KEYCHAIN_PW }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/')
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
sudo xcode-select -s /Applications/Xcode_13.0.app
xcrun notarytool submit Cryptomator-*.dmg --keychain-profile "${NOTARIZATION_KEYCHAIN_PROFILE}" --keychain "${KEYCHAIN_PATH}" --wait
xcrun stapler staple Cryptomator-*.dmg
env:
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
uses: cocoalibs/xcode-notarization-action@v1
with:
app-path: 'Cryptomator-*.dmg'
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
xcode-path: ${{ matrix.xcode-path }}
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ steps.versions.outputs.semVerStr }}.dmg
run: mv Cryptomator-*.dmg Cryptomator-${{ steps.versions.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -209,14 +224,10 @@ jobs:
if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Clean up notarization credentials
if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/notarization.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: dmg
name: dmg-${{ matrix.output-suffix }}
path: Cryptomator-*.dmg
if-no-files-found: error
- name: Publish dmg on GitHub Releases
@@ -226,7 +237,5 @@ jobs:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
*.dmg
*.asc
Cryptomator-*.dmg
Cryptomator-*.asc
+5 -5
View File
@@ -4,7 +4,7 @@ on:
pull_request:
env:
JAVA_VERSION: 17
JAVA_VERSION: 19
defaults:
run:
@@ -16,11 +16,11 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
+43
View File
@@ -0,0 +1,43 @@
name: Release Check
on:
push:
branches:
- 'release/**'
- 'hotfix/**'
env:
JAVA_VERSION: 19
defaults:
run:
shell: bash
jobs:
release-check-precondition:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: validate-pom-version
name: Validate POM version
run: |
if [[ $GITHUB_REF =~ refs/heads/(hotfix|release)/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
else
echo "Failed to parse version"
exit 1
fi
if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
echo "::set-output name=semVerStr::${SEM_VER_STR}"
else
echo "Version not set in POM"
exit 1
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
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
exit 1
fi
+115 -25
View File
@@ -4,10 +4,15 @@ on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env:
JAVA_VERSION: 17
WINFSP_MSI: https://github.com/winfsp/winfsp/releases/download/v1.10/winfsp-1.10.22006.msi
JAVA_VERSION: 19
JAVA_DIST: 'zulu'
JAVA_CACHE: 'maven'
defaults:
run:
@@ -18,21 +23,24 @@ jobs:
name: Build .msi Installer
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
cache: ${{ env.JAVA_CACHE }}
- id: versions
name: Apply version information
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
@@ -58,7 +66,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--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
--strip-native-commands
--no-header-files
--no-man-pages
@@ -80,15 +88,18 @@ jobs:
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.keychainPath=\"~/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
--resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico
- name: Patch Application Directory
@@ -102,17 +113,22 @@ jobs:
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator
timestampUrl: 'http://timestamp.digicert.com'
folder: appdir/Cryptomator
recursive: true
- name: Generate license
- name: Generate license for MSI
run: >
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
"-Dlicense.includedScopes=compile"
"-Dlicense.excludedGroups=^org\.cryptomator"
"-Dlicense.failOnMissing=true"
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
shell: pwsh
- name: Create MSI
run: >
${JAVA_HOME}/bin/jpackage
@@ -140,7 +156,7 @@ jobs:
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator Installer
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
@@ -175,33 +191,48 @@ jobs:
semVerStr: ${{ steps.versions.outputs.semVerStr }}
revNum: ${{ steps.versions.outputs.revNum }}
call-winget-flow:
needs: [build-msi]
if: github.event.action == 'published'
uses: ./.github/workflows/winget.yml
with:
releaseTag: ${{ github.event.release.tag_name }}
build-exe:
name: Build .exe installer
runs-on: windows-latest
needs: [build-msi]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Download .msi
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
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@v2
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Generate license
cache: ${{ env.JAVA_CACHE }}
- name: Generate license for exe
run: >
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources"
"-Dlicense.includedScopes=compile"
"-Dlicense.excludedGroups=^org\.cryptomator"
"-Dlicense.failOnMissing=true"
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
shell: pwsh
- name: Download WinFsp
run:
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
run: |
$winfspUrl= (Select-String -Path ".\dist\win\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
shell: pwsh
- name: Compile to wixObj file
run: >
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
@@ -217,18 +248,37 @@ jobs:
run: >
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
-ext WixBalExtension
-out installer/Cryptomator.exe
-out installer/unsigned/Cryptomator-Installer.exe
- name: Detach burn engine in preparation to sign
run: >
"${WIX}/bin/insignia.exe"
-ib installer/unsigned/Cryptomator-Installer.exe
-o tmp/engine.exe
- name: Codesign burn engine
uses: skymatic/code-sign-action@v1
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator Installer
timestampUrl: 'http://timestamp.digicert.com'
folder: tmp
- name: Reattach signed burn engine to installer
run : >
"${WIX}/bin/insignia.exe"
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
-o installer/Cryptomator-Installer.exe
- name: Codesign EXE
uses: skymatic/code-sign-action@v1
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator Installer
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -251,5 +301,45 @@ jobs:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
*.exe
*.asc
Cryptomator-*.exe
Cryptomator-*.asc
allowlist:
name: Anti Virus Allowlisting
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: [build-msi, build-exe]
steps:
- name: Download .msi
uses: actions/download-artifact@v2
with:
name: msi
path: msi
- name: Download .exe
uses: actions/download-artifact@v2
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@4.3.0
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@4.3.0
with:
protocol: ftp
server: whitelisting.avast.com
port: 21
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
local-dir: files/
+49
View File
@@ -0,0 +1,49 @@
name: Release to Winget
on:
workflow_call:
inputs:
releaseTag:
required: true
type: string
workflow_dispatch:
inputs:
releaseTag:
description: 'Release tag name'
required: true
type: string
jobs:
publish-winget:
name: Publish on winget repo
runs-on: windows-latest
steps:
- name: Get download url for msi artifacts
id: get-release-assets
uses: actions/github-script@v6
with:
script: |
const query =`query($tag:String!) {
repository(owner:"cryptomator", name:"cryptomator"){
release(tagName: $tag) {
releaseAssets(first:20) {
nodes {
name
downloadUrl
}
}
}
}
}`;
const variables = {
tag: "${{ inputs.releaseTag }}"
}
return await github.graphql(query, variables)
- name: Submit package to Windows Package Manager Community Repository
id: submit-winget
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$releaseAssets = (ConvertFrom-Json '${{ steps.get-release-assets.outputs.result }}').repository.release.releaseAssets.nodes
$installerUrl = $releaseAssets | Where-Object -Property name -match '^Cryptomator-.*\.msi$' | Select -ExpandProperty downloadUrl -First 1
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v "${{ inputs.releaseTag }}" -u "$installerUrl" -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
shell: pwsh
+1 -18
View File
@@ -5,26 +5,9 @@
*.war
*.ear
# Eclipse Settings Files #
.settings
.project
.classpath
# Maven #
target/
pom.xml.versionsBackup
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/shelf
.idea/dictionaries/**
!.idea/dictionaries/dict_*
.idea/compiler.xml
.idea/encodings.xml
.idea/jarRepositories.xml
.idea/uiDesigner.xml
.idea/**/libraries/
*.iml
# Java Crash Logs
hs_err_pid*.log
+13
View File
@@ -0,0 +1,13 @@
# see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems
# Default ignored files
/shelf/
/workspace.xml
/usage.statistics.xml
/dictionaries/
# generated from Maven
/jarRepositories.xml
/modules.xml
/*.iml
/libraries/*.xml
+51
View File
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
</profile>
<profile name="Annotation profile for Cryptomator Desktop App" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.44/dagger-compiler-2.44.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.44/dagger-2.44.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.44/dagger-producers-2.44.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.44/dagger-spi-2.44.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.7.0-1.0.6/symbol-processing-api-1.7.0-1.0.6.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.7.0/kotlin-stdlib-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.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$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.0/kotlin-stdlib-jdk8-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.0/kotlin-stdlib-jdk7-1.7.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$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.5.0/kotlinx-metadata-jvm-0.5.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled --enable-preview" />
</option>
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
+1 -1
View File
@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&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>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&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>
+8 -4
View File
@@ -2,7 +2,7 @@
[![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
[![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/2a0adf3cec6a4143b91035d3924178f1)](https://www.codacy.com/gh/cryptomator/cryptomator/dashboard)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cryptomator_cryptomator&metric=alert_status)](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
[![Twitter](https://img.shields.io/badge/twitter-@Cryptomator-blue.svg?style=flat)](http://twitter.com/Cryptomator)
[![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/)
[![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest)
@@ -32,11 +32,16 @@ Cryptomator is provided free of charge as an open-source project despite the hig
<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://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
</tr>
</tbody>
</table>
- [Jameson Lopp](https://www.lopp.net/)
### Special Shoutout
Continuous integration hosting for ARM64 builds is provided by [MacStadium](https://www.macstadium.com/opensource).
<a href="https://www.macstadium.com/opensource"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="MacStadium" height="100"></a>
---
@@ -82,13 +87,12 @@ For more information on the security details visit [cryptomator.org](https://doc
* JDK 17 (e.g. temurin)
* Maven 3
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
### Run Maven
```
mvn clean install
# or mvn clean install -Pwindows
# or mvn clean install -Pwin
# or mvn clean install -Pmac
# or mvn clean install -Plinux
```
+4 -1
View File
@@ -19,13 +19,15 @@ cp ../../../target/cryptomator-*.jar ../../../target/mods
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--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 \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=1
# create app dir
envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
${JAVA_HOME}/bin/jpackage \
--verbose \
--type app-image \
@@ -48,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources
# transform AppDir
+5 -20
View File
@@ -15,26 +15,11 @@ elif command -v pacman &> /dev/null; then # don't forget arch
GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
fi
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
GTK_FLAG="-Djdk.gtk.version=2"
fi
# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
export LD_PRELOAD=lib/app/libjffi.so
# start Cryptomator
./lib/runtime/bin/java \
-p "lib/app/mods" \
-cp "lib/app/*" \
-Dfile.encoding="utf-8" \
-Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
-Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" \
-Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json" \
-Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
-Dcryptomator.buildNumber="appimage-${REVISION_NO}" \
-Dcryptomator.appVersion="${SEMVER_STR}" \
$GTK_FLAG \
-Xss5m \
-Xmx256m \
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
bin/Cryptomator-gtk2 $@
else
bin/Cryptomator $@
fi
+1 -1
View File
@@ -7,5 +7,5 @@ Terminal=false
Type=Application
Categories=Utility;Security;FileTools;
StartupNotify=true
StartupWMClass=org.cryptomator.launcher.Cryptomator
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
@@ -66,6 +66,13 @@
</content_rating>
<releases>
<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"/>
</releases>
</component>
+1 -1
View File
@@ -1,4 +1,4 @@
cryptomator (${PPA_VERSION}) bionic; urgency=low
cryptomator (${PPA_VERSION}) focal; urgency=low
* Full changelog can be found on https://github.com/cryptomator/cryptomator/releases
+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), openjdk-17-jdk
Build-Depends: debhelper (>=10), coffeelibs-jdk-19
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git
+1 -1
View File
@@ -3,4 +3,4 @@
# fix for https://github.com/cryptomator/cryptomator/issues/1370
export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/jni/libjffi-1.2.so
/usr/lib/cryptomator/bin/cryptomator
/usr/lib/cryptomator/bin/cryptomator $@
+7 -3
View File
@@ -4,6 +4,8 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
%:
dh $@
@@ -16,14 +18,15 @@ override_dh_auto_clean:
override_dh_auto_build:
mkdir resources
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
jlink \
$(JAVA_HOME)/bin/jlink \
--output runtime \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--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 \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=2
jpackage \
$(JAVA_HOME)/bin/jpackage \
--type app-image \
--runtime-image runtime \
--input libs \
@@ -39,6 +42,7 @@ override_dh_auto_build:
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \
+12
View File
@@ -0,0 +1,12 @@
java-options=-Xss5m \
-Xmx256m \
-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
+2
View File
@@ -1,4 +1,6 @@
# created during build
Cryptomator.app/
runtime/
dmg/
*.dmg
license.rtf
+51 -29
View File
@@ -14,9 +14,17 @@ while getopts ":s:" o; do
done
shift "$((OPTIND-1))"
# prepare working dir and variables
# prepare working dir
cd $(dirname $0)
rm -rf runtime dmg
rm -rf runtime dmg *.app *.dmg
# set variables
APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2022"
PACKAGE_IDENTIFIER="org.cryptomator"
MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
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'`
@@ -31,13 +39,14 @@ fi
# compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../target/cryptomator-*.jar ../../../target/mods
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--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 \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
@@ -50,35 +59,48 @@ ${JAVA_HOME}/bin/jpackage \
--runtime-image runtime \
--input ../../../target/libs \
--module-path ../../../target/mods \
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
--module ${MODULE_AND_MAIN_CLASS} \
--dest . \
--name Cryptomator \
--vendor "Skymatic GmbH" \
--copyright "(C) 2016 - 2022 Skymatic GmbH" \
--name ${APP_NAME} \
--vendor "${VENDOR}" \
--copyright "(C) ${COPYRIGHT_YEARS} ${VENDOR}" \
--app-version "${VERSION_NO}" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--app-version "${VERSION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\"" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" \
--java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/${APP_NAME}/key.p12\"" \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier org.cryptomator \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
# transform app dir
cp ../resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
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 \
-Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
-Dlicense.includedScopes=compile \
-Dlicense.excludedGroups=^org\.cryptomator \
-Dlicense.failOnMissing=true \
-Dlicense.licenseMergesUrl=file://$(pwd)/../../../license/merges
# codesign
if [ -n "${CODESIGN_IDENTITY}" ]; then
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
find ${APP_NAME}.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
for JAR_PATH in `find ${APP_NAME}.app -name "*.jar"`; do
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
JAR_FILENAME=$(basename ${JAR_PATH})
OUTPUT_PATH=${JAR_PATH%.*}
@@ -93,25 +115,25 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
rm -r ${OUTPUT_PATH}
fi
done
echo "Codesigning Cryptomator.app..."
codesign --force --deep --entitlements ../Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
echo "Codesigning ${APP_NAME}.app..."
codesign --force --deep --entitlements ../${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
fi
# prepare dmg contents
mkdir dmg
mv Cryptomator.app dmg
mv ${APP_NAME}.app dmg
cp resources/macFUSE.webloc dmg
# create dmg
create-dmg \
--volname Cryptomator \
--volicon "resources/Cryptomator-Volume.icns" \
--background "resources/Cryptomator-background.tiff" \
--volname ${APP_NAME} \
--volicon "resources/${APP_NAME}-Volume.icns" \
--background "resources/${APP_NAME}-background.tiff" \
--window-pos 400 100 \
--window-size 640 694 \
--icon-size 128 \
--icon "Cryptomator.app" 128 245 \
--hide-extension "Cryptomator.app" \
--icon "${APP_NAME}.app" 128 245 \
--hide-extension "${APP_NAME}.app" \
--icon "macFUSE.webloc" 320 501 \
--hide-extension "macFUSE.webloc" \
--app-drop-link 512 245 \
@@ -119,4 +141,4 @@ create-dmg \
--icon ".background" 128 758 \
--icon ".fseventsd" 320 758 \
--icon ".VolumeIcon.icns" 512 758 \
Cryptomator-${VERSION_NO}.dmg dmg
${APP_NAME}-${VERSION_NO}.dmg dmg
-100
View File
@@ -1,100 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2512
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
\deftab720
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
\f0\b\fs24 \cf0 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.
\f1\b0 \
\
\f0\b \'a9 2016 \'96 2022 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.\
\
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
\
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
\
\f0\b Cryptomator uses 49 third-party dependencies under the following licenses:
\f1\b0 \
Apache License v2.0:\
- jffi (com.github.jnr:jffi:1.2.23 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jffi"}}{\fldrslt http://github.com/jnr/jffi}})\
- jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm}})\
- jnr-constants (com.github.jnr:jnr-constants:0.9.15 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-constants"}}{\fldrslt http://github.com/jnr/jnr-constants}})\
- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-ffi"}}{\fldrslt http://github.com/jnr/jnr-ffi}})\
- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - {\field{\*\fldinst{HYPERLINK "http://findbugs.sourceforge.net/"}}{\fldrslt http://findbugs.sourceforge.net/}})\
- Gson (com.google.code.gson:gson:2.8.6 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/gson/gson"}}{\fldrslt https://github.com/google/gson/gson}})\
- Dagger (com.google.dagger:dagger:2.29.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/dagger"}}{\fldrslt https://github.com/google/dagger}})\
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation}} )\
- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/failureaccess"}}{\fldrslt https://github.com/google/guava/failureaccess}})\
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/guava"}}{\fldrslt https://github.com/google/guava/guava}})\
- Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/listenablefuture"}}{\fldrslt https://github.com/google/guava/listenablefuture}})\
- J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/j2objc/"}}{\fldrslt https://github.com/google/j2objc/}})\
- Apache Commons CLI (commons-cli:commons-cli:1.4 - {\field{\*\fldinst{HYPERLINK "http://commons.apache.org/proper/commons-cli/"}}{\fldrslt http://commons.apache.org/proper/commons-cli/}})\
- javax.inject (javax.inject:javax.inject:1 - {\field{\*\fldinst{HYPERLINK "http://code.google.com/p/atinject/"}}{\fldrslt http://code.google.com/p/atinject/}})\
- Java Native Access (net.java.dev.jna:jna:5.6.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - {\field{\*\fldinst{HYPERLINK "https://commons.apache.org/proper/commons-lang/"}}{\fldrslt https://commons.apache.org/proper/commons-lang/}})\
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - {\field{\*\fldinst{HYPERLINK "http://hc.apache.org/httpcomponents-core-ga"}}{\fldrslt http://hc.apache.org/httpcomponents-core-ga}})\
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - {\field{\*\fldinst{HYPERLINK "http://jackrabbit.apache.org/jackrabbit-webdav/"}}{\fldrslt http://jackrabbit.apache.org/jackrabbit-webdav/}})\
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
BSD:\
- asm (org.ow2.asm:asm:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-commons (org.ow2.asm:asm-commons:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-tree (org.ow2.asm:asm-tree:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-util (org.ow2.asm:asm-util:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
Eclipse Public License - Version 1.0:\
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
Eclipse Public License - v 2.0:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
GPLv2:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
GPLv2+CE:\
- Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - {\field{\*\fldinst{HYPERLINK "http://servlet-spec.java.net"}}{\fldrslt http://servlet-spec.java.net}})\
- javafx-base (org.openjfx:javafx-base:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-base/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-base/}})\
- javafx-controls (org.openjfx:javafx-controls:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-controls/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-controls/}})\
- javafx-fxml (org.openjfx:javafx-fxml:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-fxml/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-fxml/}})\
- javafx-graphics (org.openjfx:javafx-graphics:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-graphics/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-graphics/}})\
LGPL 2.1:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
- Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)\
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
MIT License:\
- java jwt (com.auth0:java-jwt:3.12.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/auth0/java-jwt"}}{\fldrslt https://github.com/auth0/java-jwt}})\
- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-x86asm"}}{\fldrslt http://github.com/jnr/jnr-x86asm}})\
- jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined)\
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/nulab/zxcvbn4j"}}{\fldrslt https://github.com/nulab/zxcvbn4j}})\
- Checker Qual (org.checkerframework:checker-qual:3.5.0 - {\field{\*\fldinst{HYPERLINK "https://checkerframework.org"}}{\fldrslt https://checkerframework.org}})\
- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - {\field{\*\fldinst{HYPERLINK "http://www.slf4j.org"}}{\fldrslt http://www.slf4j.org}})\
The BSD 2-Clause License:\
- EasyBind (com.tobiasdiez:easybind:2.1.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/tobiasdiez/EasyBind"}}{\fldrslt https://github.com/tobiasdiez/EasyBind}})\
\
\f0\b Cryptomator uses other third-party assets under the following licenses:
\f1\b0 \
SIL OFL 1.1 License:\
- Font Awesome 5.12.0 ({\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt https://fontawesome.com/}})\
\
}
+49
View File
@@ -0,0 +1,49 @@
<#function artifactFormat p>
<#if p.name?index_of('Unnamed') &gt; -1>
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.artifactId + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
<#else>
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.name + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\cocoartf2512
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
\deftab720
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
\f0\b\fs24 \cf0 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.
\f1\b0 \
\
\f0\b \'a9 2016 \'96 2022 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.\
\
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
\
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
\
\f0\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:
\f1\b0 \
<#list licenseMap as e>
<#assign license = e.getKey()/>
<#assign projects = e.getValue()/>
<#if projects?size &gt; 0>
${license}:\
<#list projects as project>
- ${artifactFormat(project)}\
</#list>
</#if>
</#list>
\
\f0\b Cryptomator uses other third-party assets under the following licenses:
\f1\b0 \
SIL OFL 1.1 License:\
- {\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt Font Awesome}} (5.12.0)\
\
}
+22 -1
View File
@@ -1,2 +1,23 @@
@echo off
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1
:: Default values for Cryptomator builds
SET APPNAME="Cryptomator"
SET MAIN_JAR_GLOB="cryptomator-*"
SET UPGRADE_UUID="bda45523-42b1-4cae-9354-a45475ed4775"
SET VENDOR="Skymatic GmbH"
SET FIRST_COPYRIGHT_YEAR=2016
SET ABOUT_URL="https://cryptomator.org"
SET UPDATE_URL="https://cryptomator.org/downloads/"
SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
-UpgradeUUID "%UPGRADE_UUID%"^
-Vendor ""%VENDOR%""^
-CopyrightStartYear %FIRST_COPYRIGHT_YEAR%^
-AboutUrl "%ABOUT_URL%"^
-HelpUrl "%HELP_URL%"^
-UpdateUrl "%UPDATE_URL%"^
-Clean 1
+55 -39
View File
@@ -1,5 +1,15 @@
# check parameters
$clean = $args[0] -eq "fresh"
Param(
[Parameter(Mandatory, HelpMessage="Please provide a name for the app")][string] $AppName,
[Parameter(Mandatory, HelpMessage="Please provide the glob pattern to identify the main jar")][string] $MainJarGlob,
[Parameter(Mandatory, HelpMessage="Please provide the module- and main class path to start the app")][string] $ModuleAndMainClass,
[Parameter(Mandatory, HelpMessage="Please provide the windows upgrade uuid for the installer")][string] $UpgradeUUID,
[Parameter(Mandatory, HelpMessage="Please provide the name of the vendor")][string] $Vendor,
[Parameter(Mandatory, HelpMessage="Please provide the starting year for the copyright notice")][int] $CopyrightStartYear,
[Parameter(Mandatory, HelpMessage="Please provide a help url")][string] $HelpUrl,
[Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
[Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
[bool] $clean
)
# check preconditions
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
@@ -24,12 +34,11 @@ Write-Output "`$revisionNo=$revisionNo"
Write-Output "`$buildDir=$buildDir"
Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$vendor = "Skymatic GmbH"
$copyright = "(C) 2016 - 2022 Skymatic GmbH"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
Copy-Item "$buildDir\..\..\target\cryptomator-*.jar" -Destination "$buildDir\..\..\target\mods"
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime
$runtimeImagePath = '.\runtime'
@@ -41,13 +50,14 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
--verbose `
--output runtime `
--module-path "$Env:JAVA_HOME/jmods" `
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
--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 `
--strip-native-commands `
--no-header-files `
--no-man-pages `
--strip-debug `
--compress=1
$appPath = '.\Cryptomator'
$appPath = ".\$AppName"
if ($clean -and (Test-Path -Path $appPath)) {
Remove-Item -Path $appPath -Force -Recurse
}
@@ -59,85 +69,91 @@ if ($clean -and (Test-Path -Path $appPath)) {
--runtime-image runtime `
--input ../../target/libs `
--module-path ../../target/mods `
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator `
--module $ModuleAndMainClass `
--dest . `
--name Cryptomator `
--vendor $vendor `
--name $AppName `
--vendor $Vendor `
--copyright $copyright `
--java-options "-Xss5m" `
--java-options "-Xmx256m" `
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/Cryptomator`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/Cryptomator/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/Cryptomator/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/Cryptomator/ipc.socket`"" `
--java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/Cryptomator/keychain.json`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/Cryptomator`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"~/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources `
--icon resources/Cryptomator.ico
--icon resources/$AppName.ico
#Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\resources\"
"-Dlicense.outputDirectory=$buildDir\resources\" `
"-Dlicense.includedScopes=compile" `
"-Dlicense.excludedGroups=^org\.cryptomator" `
"-Dlicense.failOnMissing=true" `
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# patch app dir
Copy-Item "contrib\*" -Destination "Cryptomator"
attrib -r "Cryptomator\Cryptomator.exe"
$aboutUrl="https://cryptomator.org"
$updateUrl="https://cryptomator.org/downloads/"
$helpUrl="https://cryptomator.org/contact/"
Copy-Item "contrib\*" -Destination "$AppName"
attrib -r "$AppName\$AppName.exe"
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
& "$Env:JAVA_HOME\bin\jpackage" `
--verbose `
--type msi `
--win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 `
--app-image Cryptomator `
--win-upgrade-uuid $UpgradeUUID `
--app-image $AppName `
--dest installer `
--name Cryptomator `
--vendor $vendor `
--name $AppName `
--vendor $Vendor `
--copyright $copyright `
--app-version "$semVerNo" `
--win-menu `
--win-dir-chooser `
--win-shortcut-prompt `
--win-update-url $updateUrl `
--win-menu-group Cryptomator `
--win-menu-group $AppName `
--resource-dir resources `
--about-url $aboutUrl `
--license-file resources/license.rtf `
--win-update-url $UpdateUrl `
--about-url $AboutUrl `
--file-associations resources/FAvaultFile.properties
#Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\bundle\resources\"
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
"-Dlicense.includedScopes=compile" `
"-Dlicense.excludedGroups=^org\.cryptomator" `
"-Dlicense.failOnMissing=true" `
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
$winfspMsiUrl = "https://github.com/winfsp/winfsp/releases/download/v1.10/winfsp-1.10.22006.msi"
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value
Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
# copy MSI to bundle resources
Copy-Item ".\installer\Cryptomator-*.msi" -Destination ".\bundle\resources\Cryptomator.msi"
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
# create bundle including winfsp
& "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ `
-dBundleVersion="$semVerNo.$revisionNo" `
-dBundleVendor="$vendor" `
-dBundleVendor="$Vendor" `
-dBundleCopyright="$copyright" `
-dAboutUrl="$aboutUrl" `
-dHelpUrl="$helpUrl" `
-dUpdateUrl="$updateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\CryptomatorBundle.exe
-dAboutUrl="$AboutUrl" `
-dHelpUrl="$HelpUrl" `
-dUpdateUrl="$UpdateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\$AppName-Installer.exe
+7 -7
View File
@@ -5,18 +5,18 @@
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1
\pard\tx566\tx1133\tx1700\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
\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 \endash 2022 Skymatic GmbH\b0\par
\b\'a9 2016 \'96 2022 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
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
\par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
@@ -26,7 +26,7 @@ You should have received a copy of the GNU General Public License along with thi
<#if projects?size &gt; 0>
\tab ${license}:\par
<#list projects as project>
\tab\tab- ${artifactFormat(project)}\par
\tab\tab - ${artifactFormat(project)}\par
</#list>
</#if>
</#list>
@@ -38,4 +38,4 @@ You should have received a copy of the GNU General Public License along with thi
\b Cryptomator dynamically links to third-party libraries under the following license:\b0\par
\tab Uncategorized License:\par
\tab\tab - WinFsp - Windows File System Proxy, Copyright (C) Bill Zissimopoulos ({{\field{\*\fldinst{HYPERLINK https://github.com/billziss-gh/winfsp }}{\fldrslt{https://github.com/billziss-gh/winfsp\ul0\cf0}}}}\f0\fs16 )\b\par
}
}
+2
View File
@@ -0,0 +1,2 @@
[InternetShortcut]
URL=https://github.com/winfsp/winfsp/releases/download/v1.12/winfsp-1.12.22301.msi
+5 -1
View File
@@ -1,3 +1,7 @@
@echo off
:: Default values for Cryptomator builds
SET LOOPBACK_ALIAS="cryptomator-vault"
cd %~dp0
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS%
+9 -3
View File
@@ -1,10 +1,16 @@
#Requires -RunAsAdministrator
Param(
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
)
# Adds for address 127.0.0.1 the 'cryptomator-vault' alias to the hosts file
# Adds an alias for 127.0.0.1 to the hosts file
function Add-AliasToHost {
param (
[string]$LoopbackAlias
)
$sysdir = [Environment]::SystemDirectory
$hostsFile = "$sysdir\drivers\etc\hosts"
$aliasLine = '127.0.0.1 cryptomator-vault'
$aliasLine = "127.0.0.1 $LoopbackAlias"
foreach ($line in Get-Content $hostsFile) {
if ($line -eq $aliasLine){
@@ -49,7 +55,7 @@ function Edit-ProviderOrder {
}
Add-AliasToHost
Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file'
Set-WebDAVFileSizeLimit
+25 -2
View File
@@ -13,7 +13,6 @@
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
@@ -24,8 +23,9 @@
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
<!-- custom end dialog -->
<!-- custom end dialogs -->
<Publish Dialog="MyExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
<Publish Dialog="MyFatalErrorDlg" Control="Finish" Event="EndDialog" Value="Return" Order="998">1</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
@@ -75,11 +75,34 @@
</Control>
</Dialog>
<!-- copy pasta from https://github.com/wixtoolset/wix3/blob/develop/src/ext/UIExtension/wixlib/FatalError.wxs with adjustments-->
<Dialog Id="MyFatalErrorDlg" Width="370" Height="270" Title="!(loc.FatalError_Title)">
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
<Publish Event="EndDialog" Value="Exit">1</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUICancel)" />
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="!(loc.FatalErrorBitmap)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Title" Type="Text" X="135" Y="20" Width="220" Height="60" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorTitle)" />
<Control Id="Description" Type="Text" X="135" Y="70" Width="220" Height="80" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorDescription1) !(loc.FatalErrorDescription2)" />
<Control Id="DescriptionReason1" Type="Text" X="135" Y="160" Width="220" Height="20" Transparent="yes" NoPrefix="yes" Hidden="yes" >
<Text>Reason:</Text>
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
</Control>
<Control Id="DescriptionReason2" Type="Text" X="135" Y="170" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Hidden="yes" >
<Text>Application to update was still running during installation.</Text>
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
</Control>
</Dialog>
<InstallUISequence>
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
</InstallUISequence>
<AdminUISequence>
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
</AdminUISequence>
</UI>
+7 -7
View File
@@ -5,18 +5,18 @@
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1
\pard\tx566\tx1133\tx1700\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
\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 \endash 2022 Skymatic GmbH\b0\par
\b\'a9 2016 \'96 2022 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
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
\par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
@@ -26,12 +26,12 @@ You should have received a copy of the GNU General Public License along with thi
<#if projects?size &gt; 0>
\tab ${license}:\par
<#list projects as project>
\tab\tab- ${artifactFormat(project)}\par
\tab\tab - ${artifactFormat(project)}\par
</#list>
</#if>
</#list>
\par
\b Cryptomator uses other third-party assets under the following licenses:\b0\par
\tab SIL OFL 1.1 License:\par
\tab\tab - Font Awesome 5.12.0 ({{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
\tab\tab - Font Awesome (5.12.0 - {{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
}
+25 -6
View File
@@ -86,12 +86,12 @@
<!-- Non-Opening ProgID -->
<DirectoryRef Id="INSTALLDIR">
<Component Win64="yes" Id="nonStartingProgID" >
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\Cryptomator-Vault.ico" Name="Cryptomator-Vault.ico"></File>
<ProgId Id="Cryptomator.Encrypted.1" Description="Cryptomator Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="application/vnd.cryptomator.encrypted">
<MIME ContentType="application/vnd.cryptomator.encrypted" Default="yes"></MIME>
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileC9rC9s)" Name="$(var.IconFileC9rC9s)"></File>
<ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
<MIME ContentType="$(var.ProgIdContentType)" Default="yes"></MIME>
</Extension>
<Extension Id="c9s" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"/>
<Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
</ProgId>
</Component>
</DirectoryRef>
@@ -127,6 +127,20 @@
<!-- WebDAV patches -->
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
<!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
<util:CloseApplication
Target="$(var.CloseApplicationTarget)"
Id="Close$(var.JpAppName)"
CloseMessage="no"
RebootPrompt="no"
PromptToContinue="yes"
Description="A running instance of $(var.JpAppName) is found. Please close it to continue."
Property="FOUNDRUNNINGAPP"
>
</util:CloseApplication>
<CustomAction Id="FailOnRunningApp" Impersonate="no" ExeCommand="[SystemFolder]\cmd.exe /c &quot;exit 1&quot;" Directory="INSTALLDIR" Execute="immediate" Return="check" />
<?ifdef JpIcon ?>
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
@@ -155,7 +169,12 @@
<?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?>
<RemoveExistingProducts Before="CostInitialize"/>
<!-- Check and fail if Cryptomator is running -->
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
<Custom Action="FailOnRunningApp" After="WixCloseApplications" >FOUNDRUNNINGAPP</Custom>
<RemoveExistingProducts After="InstallValidate"/>
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Stub by design -->
<!--
overrides.wxi is a placeholder to set/alter WiX variables referenced from default
main.wxs file.
Put custom overrides.wxi in resource directory to replace this default file.
Override default overrides.wxi if configuring of msi installers through jpackage
command line is not sufficient.
WiX variables referenced from default main.wxs that can be altered in custom overrides.wxi:
- JpProductLanguage
Value of `Language` attribute of `Product` WiX element. Default value is 1033.
- JpInstallerVersion
Value of `InstallerVersion` attribute of `Package` WiX element. Default value is 200.
- JpCompressedMsi
Value of `Compressed` attribute of `Package` WiX element. Default value is `yes`.
- JpAllowDowngrades
Should be defined to enable downgrades and undefined to disable downgrades.
Default value is `yes`.
- JpAllowUpgrades
Should be defined to enable upgrades and undefined to disable upgrades.
Default value is `yes`.
-->
<!-- Non-opening ProgID settings-->
<?define IconFileC9rC9s= "Cryptomator-Vault.ico" ?>
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
<!-- Close Application util -->
<?define CloseApplicationTarget= "cryptomator.exe" ?>
<Include/>
+7
View File
@@ -0,0 +1,7 @@
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
GPLv2|GNU General Public License Version 2
GPLv2+CE|CDDL + GPLv2 with classpath exception
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
Eclipse Public License - Version 2.0|Eclipse Public License - v 2.0
+101 -37
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.7.0-SNAPSHOT</version>
<version>1.7.0</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -21,45 +21,55 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>17</project.jdk.version>
<project.jdk.version>19</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<nonModularGroupIds>com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- Check progress on https://github.com/swiesend/secret-service/issues/31 to remove hypfvieh, swiesend and jnr -->
<nonModularGroupIds>com.github.jnr,org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.3.1</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.1.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.6</cryptomator.webdav.version>
<cryptomator.cryptolib.version>2.1.0-rc1</cryptomator.cryptolib.version>
<cryptomator.cryptofs.version>2.4.5</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.2.0-beta2</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.1.2</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.1.2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.1.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>2.0.0-beta1</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0-beta1</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.0-beta1</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>17.0.2</javafx.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jwt.version>3.18.3</jwt.version>
<dagger.version>2.44</dagger.version>
<easybind.version>2.2</easybind.version>
<guava.version>31.0-jre</guava.version>
<dagger.version>2.40.3</dagger.version>
<gson.version>2.8.9</gson.version>
<zxcvbn.version>1.5.2</zxcvbn.version>
<slf4j.version>1.7.32</slf4j.version>
<logback.version>1.2.9</logback.version>
<guava.version>31.1-jre</guava.version>
<gson.version>2.9.1</gson.version>
<javafx.version>19</javafx.version>
<jwt.version>4.2.1</jwt.version>
<nimbus-jose.version>9.25.4</nimbus-jose.version>
<logback.version>1.4.4</logback.version>
<slf4j.version>2.0.3</slf4j.version>
<tinyoauth2.version>0.5.1</tinyoauth2.version>
<zxcvbn.version>1.7.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.8.1</junit.jupiter.version>
<mockito.version>3.12.4</mockito.version>
<junit.jupiter.version>5.9.1</junit.jupiter.version>
<mockito.version>4.8.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
<!-- build plugin dependencies -->
<dependency-check.version>7.0.0</dependency-check.version>
<jacoco.version>0.8.7</jacoco.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>7.2.1</dependency-check.version>
<jacoco.version>0.8.8</jacoco.version>
</properties>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>${cryptomator.cryptolib.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
@@ -132,12 +142,22 @@
<version>${commons-lang3.version}</version>
</dependency>
<!-- JWT -->
<!-- OAuth/JWT -->
<dependency>
<groupId>io.github.coffeelibs</groupId>
<artifactId>tiny-oauth2-client</artifactId>
<version>${tinyoauth2.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>
@@ -224,6 +244,13 @@
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>${jetbrains.annotations.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@@ -237,7 +264,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -247,7 +274,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.0.0-M7</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -257,7 +284,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
@@ -287,9 +314,45 @@
<compilerArgs>
<arg>-Adagger.fastInit=enabled</arg>
<arg>-Adagger.formatGeneratedSource=enabled</arg>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>compile-light-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/light_theme.css</arg>
<arg>${project.build.outputDirectory}/css/light_theme.bss</arg>
</arguments>
</configuration>
</execution>
<execution>
<id>compile-dark-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/dark_theme.css</arg>
<arg>${project.build.outputDirectory}/css/dark_theme.bss</arg>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@@ -347,23 +410,24 @@
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
<includedScopes>compile</includedScopes>
<excludedGroups>org\.cryptomator</excludedGroups>
<licenseMerges>
<licenseMerge>Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0</licenseMerge>
<licenseMerge>MIT License|The MIT License (MIT)|The MIT License|MIT license</licenseMerge>
<licenseMerge>LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1</licenseMerge>
<licenseMerge>GPLv2|GNU General Public License Version 2</licenseMerge>
<licenseMerge>GPLv2+CE|CDDL + GPLv2 with classpath exception</licenseMerge>
</licenseMerges>
<fileTemplate>${project.basedir}/src/license/template.ftl</fileTemplate>
<licenseMergesUrl>file:///${project.basedir}/license/merges</licenseMergesUrl>
<fileTemplate>${project.basedir}/src/main/resources/license/template.ftl</fileTemplate>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>license/*</exclude>
</excludes>
</resource>
</resources>
</build>
<profiles>
-1
View File
@@ -1 +0,0 @@
com.github.serceman--jnr-fuse--0.5.4=MIT License
+20 -38
View File
@@ -1,60 +1,42 @@
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
module org.cryptomator.desktop {
open module org.cryptomator.desktop {
requires static org.jetbrains.annotations;
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;
// jdk:
requires java.desktop;
requires java.net.http;
requires javafx.base;
requires javafx.graphics;
requires javafx.controls;
requires javafx.fxml;
requires com.tobiasdiez.easybind;
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.google.gson;
requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind;
requires dagger;
requires io.github.coffeelibs.tinyoauth2client;
requires org.slf4j;
requires org.apache.commons.lang3;
requires dagger;
requires com.auth0.jwt;
/* TODO: filename-based modules: */
requires static javax.inject; /* ugly dagger/guava crap */
requires logback.classic;
requires logback.core;
uses AutoStartProvider;
uses KeychainAccessProvider;
uses TrayIntegrationProvider;
uses UiAppearanceProvider;
opens org.cryptomator.common.settings to com.google.gson;
opens org.cryptomator.common to javafx.fxml;
opens org.cryptomator.common.vaults to javafx.fxml;
opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
opens org.cryptomator.ui.changepassword to javafx.fxml;
opens org.cryptomator.ui.common to javafx.fxml;
opens org.cryptomator.ui.controls to javafx.fxml;
opens org.cryptomator.ui.forgetPassword to javafx.fxml;
opens org.cryptomator.ui.fxapp to javafx.fxml;
opens org.cryptomator.ui.health to javafx.fxml;
opens org.cryptomator.ui.keyloading.masterkeyfile to javafx.fxml;
opens org.cryptomator.ui.lock to javafx.fxml;
opens org.cryptomator.ui.mainwindow to javafx.fxml;
opens org.cryptomator.ui.migration to javafx.fxml;
opens org.cryptomator.ui.preferences to javafx.fxml;
opens org.cryptomator.ui.quit to javafx.fxml;
opens org.cryptomator.ui.recoverykey to javafx.fxml;
opens org.cryptomator.ui.removevault to javafx.fxml;
opens org.cryptomator.ui.stats to javafx.fxml;
opens org.cryptomator.ui.unlock to javafx.fxml;
opens org.cryptomator.ui.vaultoptions to javafx.fxml;
opens org.cryptomator.ui.wrongfilealert to javafx.fxml;
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
}
@@ -0,0 +1,110 @@
package org.cryptomator.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.concurrent.Task;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//Inspired by: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)
public final class CatchingExecutors {
private static final Logger LOG = LoggerFactory.getLogger(CatchingExecutors.class);
private CatchingExecutors() { /* NO-OP */ }
public static class CatchingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
public CatchingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
Runnable oneShot = () -> this.execute(command);
return super.scheduleAtFixedRate(oneShot, initialDelay, period, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
Runnable oneShot = () -> this.execute(command);
return super.scheduleWithFixedDelay(oneShot, initialDelay, delay, unit);
}
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
afterExecuteInternal(runnable, throwable);
}
}
public static class CatchingThreadPoolExecutor extends ThreadPoolExecutor {
public CatchingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
afterExecuteInternal(runnable, throwable);
}
}
private static void afterExecuteInternal(Runnable runnable, Throwable throwable) {
if (throwable != null) {
callHandler(Thread.currentThread(), throwable);
} else if (runnable instanceof Task<?> t) {
afterExecuteTask(t);
} else if (runnable instanceof Future<?> f) {
afterExecuteFuture(f);
}
//Errors in this method are delegated to the UncaughtExceptionHandler of the current thread
}
private static void callHandler(Thread thread, Throwable throwable) {
Objects.requireNonNullElseGet(thread.getUncaughtExceptionHandler(), CatchingExecutors::fallbackHandler).uncaughtException(thread, throwable);
}
private static Thread.UncaughtExceptionHandler fallbackHandler() {
return (thread, throwable) -> LOG.error("FALLBACK: Uncaught exception in " + thread.getName(), throwable);
}
private static void afterExecuteTask(Task<?> task) {
var caller = Thread.currentThread();
Platform.runLater(() -> {
if (task.getOnFailed() == null) {
callHandler(caller, task.getException());
}
});
}
private static void afterExecuteFuture(Future<?> future) {
if (future instanceof ScheduledFuture<?> && !future.isDone()) {
//we assume that this must be a repeated ScheduledFutureTask, where the done-status is only set when not executed anymore
//see also https://github.com/cryptomator/cryptomator/pull/2422
return;
}
try {
future.get();
} catch (CancellationException ce) {
//Ignore
} catch (ExecutionException ee) {
callHandler(Thread.currentThread(), ee.getCause());
} catch (InterruptedException ie) {
//Ignore/Reset
Thread.currentThread().interrupt();
}
}
}
@@ -10,35 +10,29 @@ 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;
import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultListModule;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import javafx.beans.value.ObservableValue;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class})
@Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class, MountModule.class})
public abstract class CommonsModule {
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
@@ -46,6 +40,12 @@ public abstract class CommonsModule {
private static final int NUM_CORE_BG_THREADS = 6;
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
@Provides
@Singleton
static Environment provideEnvironment() {
return Environment.getInstance();
}
@SuppressWarnings("SpellCheckingInspection")
@Provides
@Singleton
@@ -93,7 +93,7 @@ public abstract class CommonsModule {
@Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
ScheduledExecutorService executorService = new CatchingExecutors.CatchingScheduledThreadPoolExecutor(NUM_SCHEDULER_THREADS, r -> {
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
Thread t = new Thread(r);
t.setName(name);
@@ -110,7 +110,7 @@ public abstract class CommonsModule {
@Singleton
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
ExecutorService executorService = new CatchingExecutors.CatchingThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
Thread t = new Thread(r);
t.setName(name);
@@ -129,20 +129,11 @@ public abstract class CommonsModule {
@Provides
@Singleton
static Binding<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
return Bindings.createObjectBinding(() -> {
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());
}, settings.port());
}
@Provides
@Singleton
static WebDavServer provideWebDavServer(Binding<InetSocketAddress> serverSocketAddressBinding) {
WebDavServer server = WebDavServer.create();
// no need to unsubscribe eventually, because server is a singleton
EasyBind.subscribe(serverSocketAddressBinding, server::bind);
return server;
});
}
}
@@ -6,6 +6,7 @@ public interface Constants {
String MASTERKEY_BACKUP_SUFFIX = ".bkup";
String VAULTCONFIG_FILENAME = "vault.cryptomator";
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
byte[] PEPPER = new byte[0];
}
@@ -5,8 +5,6 @@ import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -17,32 +15,55 @@ import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@Singleton
public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
private static final char PATH_LIST_SEP = ':';
private static final int DEFAULT_MIN_PW_LENGTH = 8;
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
@Inject
public Environment() {
LOG.debug("user.home: {}", System.getProperty("user.home"));
LOG.debug("java.library.path: {}", System.getProperty("java.library.path"));
LOG.debug("user.language: {}", System.getProperty("user.language"));
LOG.debug("user.region: {}", System.getProperty("user.region"));
LOG.debug("logback.configurationFile: {}", System.getProperty("logback.configurationFile"));
LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath"));
LOG.debug("cryptomator.ipcSocketPath: {}", System.getProperty("cryptomator.ipcSocketPath"));
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
LOG.debug("cryptomator.pluginDir: {}", System.getProperty("cryptomator.pluginDir"));
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
private Environment() {}
public void log() {
LOG.info("user.home: {}", System.getProperty("user.home"));
LOG.info("java.library.path: {}", System.getProperty("java.library.path"));
LOG.info("user.language: {}", System.getProperty("user.language"));
LOG.info("user.region: {}", System.getProperty("user.region"));
LOG.info("logback.configurationFile: {}", System.getProperty("logback.configurationFile"));
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
}
public static Environment getInstance() {
final class Holder {
private static final Environment INSTANCE = new Environment();
}
return Holder.INSTANCE;
}
private void logCryptomatorSystemProperty(String propertyName) {
LOG.info("{}: {}", propertyName, System.getProperty(propertyName));
}
public boolean useCustomLogbackConfig() {
@@ -50,48 +71,52 @@ public class Environment {
}
public Stream<Path> getSettingsPath() {
return getPaths("cryptomator.settingsPath");
return getPaths(SETTINGS_PATH_PROP_NAME);
}
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Stream<Path> ipcSocketPath() {
return getPaths("cryptomator.ipcSocketPath");
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
}
public Stream<Path> getKeychainPath() {
return getPaths("cryptomator.keychainPath");
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
}
public Optional<Path> getLogDir() {
return getPath("cryptomator.logDir").map(this::replaceHomeDir);
return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public Optional<Path> getPluginDir() {
return getPath("cryptomator.pluginDir").map(this::replaceHomeDir);
return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public Optional<Path> getMountPointsDir() {
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
return getPath(MOUNTPOINT_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public Optional<String> getAppVersion() {
return Optional.ofNullable(System.getProperty("cryptomator.appVersion"));
/**
* Returns the app version defined in the {@value APP_VERSION_PROP_NAME} property or returns "SNAPSHOT".
*
* @return App version or "SNAPSHOT", if undefined
*/
public String getAppVersion() {
return System.getProperty(APP_VERSION_PROP_NAME, "SNAPSHOT");
}
public Optional<String> getBuildNumber() {
return Optional.ofNullable(System.getProperty("cryptomator.buildNumber"));
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
}
public int getMinPwLength() {
return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
return getInt(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
}
public boolean showTrayIcon() {
return Boolean.getBoolean("cryptomator.showTrayIcon");
}
@Deprecated // TODO: remove as soon as custom mount path works properly on Win+Fuse
public boolean useExperimentalFuse() {
return Boolean.getBoolean("fuse.experimental");
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
}
private int getInt(String propertyName, int defaultValue) {
@@ -10,6 +10,7 @@ import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import java.util.Optional;
@Singleton
@@ -18,7 +19,7 @@ public class LicenseHolder {
private final Settings settings;
private final LicenseChecker licenseChecker;
private final ObjectProperty<DecodedJWT> validJwtClaims;
private final StringBinding licenseSubject;
private final ObservableValue<String> licenseSubject;
private final BooleanBinding validLicenseProperty;
@Inject
@@ -26,7 +27,7 @@ public class LicenseHolder {
this.settings = settings;
this.licenseChecker = licenseChecker;
this.validJwtClaims = new SimpleObjectProperty<>();
this.licenseSubject = Bindings.createStringBinding(this::getLicenseSubject, validJwtClaims);
this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject);
this.validLicenseProperty = validJwtClaims.isNotNull();
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
@@ -55,17 +56,12 @@ public class LicenseHolder {
}
}
public StringBinding licenseSubjectProperty() {
public ObservableValue<String> licenseSubjectProperty() {
return licenseSubject;
}
public String getLicenseSubject() {
DecodedJWT claims = validJwtClaims.get();
if (claims != null) {
return claims.getSubject();
} else {
return null;
}
return licenseSubject.getValue();
}
public BooleanBinding validLicenseProperty() {
@@ -0,0 +1,64 @@
package org.cryptomator.common;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
/**
* Enum of common cloud providers and their default local storage location path.
*/
public enum LocationPreset {
DROPBOX("Dropbox", "~/Dropbox"),
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
MEGA("MEGA", "~/MEGA"),
ONEDRIVE("OneDrive", "~/OneDrive"),
PCLOUD("pCloud", "~/pCloudDrive"),
LOCAL("local");
private final String name;
private final List<Path> candidates;
LocationPreset(String name, String... candidates) {
this.name = name;
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
}
/**
* Checks for this LocationPreset if any of the associated paths exist.
*
* @return the first existing path or null, if none exists.
*/
public Path existingPath() {
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
}
public String getDisplayName() {
return name;
}
@Override
public String toString() {
return getDisplayName();
}
//this contruct is needed, since static members are initialized after every enum member is initialized
//TODO: refactor this to normal class and use this also in different parts of the project
private static class UserHome {
private static final String USER_HOME = System.getProperty("user.home");
private static String resolve(String path) {
if (path.startsWith("~/")) {
return UserHome.USER_HOME + path.substring(1);
} else {
return path;
}
}
}
}
@@ -0,0 +1,108 @@
package org.cryptomator.common;
import javax.security.auth.Destroyable;
import java.util.Arrays;
/**
* A destroyable CharSequence.
*/
public class Passphrase implements Destroyable, CharSequence {
private final char[] data;
private final int offset;
private final int length;
private boolean destroyed;
/**
* Wraps (doesn't copy) the given data.
*
* @param data The wrapped data. Any changes to this will be reflected in this passphrase
*/
public Passphrase(char[] data) {
this(data, 0, data.length);
}
/**
* Wraps (doesn't copy) a subarray of the given data.
*
* @param data The wrapped data. Any changes to this will be reflected in this passphrase
* @param offset The subarray offset, i.e. the first character of this passphrase
* @param length The subarray length, i.e. the length of this passphrase
*/
public Passphrase(char[] data, int offset, int length) {
if (offset < 0 || length < 0 || offset + length > data.length) {
throw new IndexOutOfBoundsException("[%1$d %1$d + %2$d[ not within [0, %3$d[".formatted(offset, length, data.length));
}
this.data = data;
this.offset = offset;
this.length = length;
}
public static Passphrase copyOf(CharSequence cs) {
char[] result = new char[cs.length()];
for (int i = 0; i < cs.length(); i++) {
result[i] = cs.charAt(i);
}
return new Passphrase(result);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Passphrase that = (Passphrase) o;
// time-constant comparison
int diff = 0;
for (int i = 0; i < length; i++) {
diff |= charAt(i) ^ that.charAt(i);
}
return diff == 0;
}
@Override
public int hashCode() {
// basically Arrays.hashCode, but only for a certain subarray
int result = 1;
for (int i = 0; i < length; i++) {
result = 31 * result + charAt(i);
}
return result;
}
@Override
public String toString() {
return new String(data, offset, length);
}
@Override
public int length() {
return length;
}
@Override
public char charAt(int index) {
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("%d not within [0, %d[".formatted(index, length));
}
return data[offset + index];
}
@Override
public Passphrase subSequence(int start, int end) {
if (start < 0 || end < 0 || end > length || start > end) {
throw new IndexOutOfBoundsException("[%d, %d[ not within [0, %d[".formatted(start, end, length));
}
return new Passphrase(Arrays.copyOfRange(data, offset + start, offset + end));
}
@Override
public boolean isDestroyed() {
return destroyed;
}
@Override
public void destroy() {
Arrays.fill(data, offset, offset + length, '\0');
destroyed = true;
}
}
@@ -1,66 +0,0 @@
package org.cryptomator.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@Singleton
public class PluginClassLoader extends URLClassLoader {
private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
private static final String NAME = "PluginClassLoader";
private static final String JAR_SUFFIX = ".jar";
@Inject
public PluginClassLoader(Environment env) {
super(NAME, env.getPluginDir().map(PluginClassLoader::findJars).orElse(new URL[0]), PluginClassLoader.class.getClassLoader());
}
private static URL[] findJars(Path path) {
if (!Files.isDirectory(path)) {
return new URL[0];
} else {
try {
var visitor = new JarVisitor();
Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
return visitor.urls.toArray(URL[]::new);
} catch (IOException e) {
LOG.warn("Failed to scan plugin dir " + path, e);
return new URL[0];
}
}
}
private static final class JarVisitor extends SimpleFileVisitor<Path> {
private final List<URL> urls = new ArrayList<>();
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (attrs.isRegularFile() && file.getFileName().toString().toLowerCase().endsWith(JAR_SUFFIX)) {
try {
urls.add(file.toUri().toURL());
} catch (MalformedURLException e) {
LOG.warn("Failed to create URL for jar file {}", file);
}
}
return FileVisitResult.CONTINUE;
}
}
}
@@ -43,12 +43,6 @@ public class KeychainManager implements KeychainAccessProvider {
return getClass().getName();
}
@Override
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
@@ -68,14 +62,6 @@ public class KeychainManager implements KeychainAccessProvider {
setPassphraseStored(key, false);
}
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) {
getKeychainOrFail().changePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
}
@Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) {
@@ -2,44 +2,35 @@ package org.cryptomator.common.keychain;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import javax.inject.Singleton;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectExpression;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.List;
@Module
public class KeychainModule {
@Provides
@Singleton
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
static List<KeychainAccessProvider> provideSupportedKeychainAccessProviders() {
return KeychainAccessProvider.get().toList();
}
@Provides
@Singleton
static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) {
return availableFactories.stream() //
.map(ServiceLoader.Provider::get) //
.filter(KeychainAccessProvider::isSupported) //
.collect(Collectors.toUnmodifiableSet());
}
@Provides
@Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> {
if (!settings.useKeychain().get()) {
return null;
}
var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findAny().orElse(null);
var fallbackProvider = providers.stream().findFirst().orElse(null);
return selectedProvider.orElse(fallbackProvider);
}, settings.keychainProvider());
}, settings.keychainProvider(), settings.useKeychain());
}
}
@@ -0,0 +1,30 @@
package org.cryptomator.common.mount;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.MountService;
import javax.inject.Singleton;
import javafx.beans.value.ObservableValue;
import java.util.List;
@Module
public class MountModule {
@Provides
@Singleton
static List<MountService> provideSupportedMountServices() {
return MountService.get().toList();
}
@Provides
@Singleton
static ObservableValue<MountService> provideMountService(Settings settings, List<MountService> serviceImpls) {
return settings.mountService().map(desiredServiceImpl -> {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
return serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny().orElse(fallbackProvider);
});
}
}
@@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.mount;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.SystemUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
@Singleton
public final class WindowsDriveLetters {
private static final Set<Path> A_TO_Z;
static {
var sortedSet = new TreeSet<Path>();
IntStream.rangeClosed('A', 'Z').mapToObj(i -> Path.of((char) i + ":\\")).forEach(sortedSet::add);
A_TO_Z = Collections.unmodifiableSet(sortedSet);
}
@Inject
public WindowsDriveLetters() {
}
public Set<Path> getAll() {
return A_TO_Z;
}
public Set<Path> getOccupied() {
if (!SystemUtils.IS_OS_WINDOWS) {
return Set.of();
} else {
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
return StreamSupport.stream(rootDirs.spliterator(), false).collect(Collectors.toUnmodifiableSet());
}
}
public Set<Path> getAvailable() {
return Sets.difference(getAll(), getOccupied());
}
/**
* Skips A and B and only returns them if all others are occupied.
*
* @return an Optional containing either the letter of a free drive letter or empty, if none is available
*/
public Optional<Path> getFirstDesiredAvailable() {
var availableDriveLetters = getAvailable();
var optString = availableDriveLetters.stream().filter(this::notAOrB).findFirst();
return optString.or(() -> availableDriveLetters.stream().findFirst());
}
private boolean notAOrB(Path driveLetter) {
return !(Path.of("A:\\").equals(driveLetter) || Path.of("B:\\").equals(driveLetter));
}
}
@@ -1,29 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.common.vaults.WindowsDriveLetters;
import javax.inject.Inject;
import java.nio.file.Path;
import java.util.Optional;
class AvailableDriveLetterChooser implements MountPointChooser {
private final WindowsDriveLetters windowsDriveLetters;
@Inject
public AvailableDriveLetterChooser(WindowsDriveLetters windowsDriveLetters) {
this.windowsDriveLetters = windowsDriveLetters;
}
@Override
public boolean isApplicable(Volume caller) {
return SystemUtils.IS_OS_WINDOWS;
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return this.windowsDriveLetters.getDesiredAvailableDriveLetterPath();
}
}
@@ -1,42 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
import javax.inject.Inject;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
class CustomDriveLetterChooser implements MountPointChooser {
private final VaultSettings vaultSettings;
@Inject
public CustomDriveLetterChooser(VaultSettings vaultSettings) {
this.vaultSettings = vaultSettings;
}
@Override
public boolean isApplicable(Volume caller) {
return SystemUtils.IS_OS_WINDOWS;
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return this.vaultSettings.getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get);
}
@Override
public boolean prepare(Volume caller, Path driveLetter) throws InvalidMountPointException {
if (!Files.notExists(driveLetter, LinkOption.NOFOLLOW_LINKS)) {
//Drive already exists OR can't be determined
throw new InvalidMountPointException(new FileAlreadyExistsException(driveLetter.toString()));
}
return false;
}
}
@@ -1,151 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.common.vaults.MountPointRequirement;
import org.cryptomator.common.vaults.Volume;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
class CustomMountPointChooser implements MountPointChooser {
private static final String HIDEAWAY_PREFIX = ".~$";
private static final String HIDEAWAY_SUFFIX = ".tmp";
private static final String WIN_HIDDEN = "dos:hidden";
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
private final VaultSettings vaultSettings;
private final Environment environment;
@Inject
public CustomMountPointChooser(VaultSettings vaultSettings, Environment environment) {
this.vaultSettings = vaultSettings;
this.environment = environment;
}
@Override
public boolean isApplicable(Volume caller) {
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse();
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
//VaultSettings#getCustomMountPath already checks whether the saved custom mountpoint should be used
return this.vaultSettings.getCustomMountPath().map(Paths::get);
}
@Override
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
return switch (caller.getMountPointRequirement()) {
case PARENT_NO_MOUNT_POINT -> {
prepareParentNoMountPoint(mountPoint);
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
yield true;
}
case EMPTY_MOUNT_POINT -> {
prepareEmptyMountPoint(mountPoint);
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
yield false;
}
case NONE, UNUSED_ROOT_DIR, PARENT_OPT_MOUNT_POINT -> {
throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement"));
}
};
}
//This is case on Windows when using FUSE
//See https://github.com/billziss-gh/winfsp/issues/320
void prepareParentNoMountPoint(Path mountPoint) throws InvalidMountPointException {
Path hideaway = getHideaway(mountPoint);
var mpExists = Files.exists(mountPoint, LinkOption.NOFOLLOW_LINKS);
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
//TODO: possible improvement by just deleting an _empty_ hideaway
if (mpExists && hideExists) { //both resources exist (whatever type)
throw new InvalidMountPointException(new FileAlreadyExistsException(hideaway.toString()));
} else if (!mpExists && !hideExists) { //neither mountpoint nor hideaway exist
throw new InvalidMountPointException(new NoSuchFileException(mountPoint.toString()));
} else if (!mpExists) { //only hideaway exists
checkIsDirectory(hideaway);
LOG.info("Mountpoint {} for winfsp mount seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint);
try {
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN, true, LinkOption.NOFOLLOW_LINKS);
}
} catch (IOException e) {
throw new InvalidMountPointException(e);
}
} else { //only mountpoint exists
try {
checkIsDirectory(mountPoint);
checkIsEmpty(mountPoint);
Files.move(mountPoint, hideaway);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN, true, LinkOption.NOFOLLOW_LINKS);
}
} catch (IOException e) {
throw new InvalidMountPointException(e);
}
}
}
private void prepareEmptyMountPoint(Path mountPoint) throws InvalidMountPointException {
//This is the case for Windows when using Dokany and for Linux and Mac
checkIsDirectory(mountPoint);
try {
checkIsEmpty(mountPoint);
} catch (IOException exception) {
throw new InvalidMountPointException("IOException while checking folder content", exception);
}
}
@Override
public void cleanup(Volume caller, Path mountPoint) {
if (caller.getMountPointRequirement() == MountPointRequirement.PARENT_NO_MOUNT_POINT) {
Path hideaway = getHideaway(mountPoint);
try {
Files.move(hideaway, mountPoint);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(mountPoint, WIN_HIDDEN, false);
}
} catch (IOException e) {
LOG.error("Unable to clean up mountpoint {} for Winfsp mounting.", mountPoint, e);
}
}
}
private void checkIsDirectory(Path toCheck) throws InvalidMountPointException {
if (!Files.isDirectory(toCheck, LinkOption.NOFOLLOW_LINKS)) {
throw new InvalidMountPointException(new NotDirectoryException(toCheck.toString()));
}
}
private void checkIsEmpty(Path toCheck) throws InvalidMountPointException, IOException {
try (var dirStream = Files.list(toCheck)) {
if (dirStream.findFirst().isPresent()) {
throw new InvalidMountPointException(new DirectoryNotEmptyException(toCheck.toString()));
}
}
}
//visible for testing
Path getHideaway(Path mountPoint) {
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
}
}
@@ -1,16 +0,0 @@
package org.cryptomator.common.mountpoint;
public class InvalidMountPointException extends Exception {
public InvalidMountPointException(String message) {
super(message);
}
public InvalidMountPointException(Throwable cause) {
super(cause);
}
public InvalidMountPointException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -1,42 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
import javax.inject.Inject;
import java.nio.file.Path;
import java.util.Optional;
class MacVolumeMountChooser implements MountPointChooser {
private static final Path VOLUME_PATH = Path.of("/Volumes");
private final VaultSettings vaultSettings;
private final MountPointHelper helper;
@Inject
public MacVolumeMountChooser(VaultSettings vaultSettings, MountPointHelper helper) {
this.vaultSettings = vaultSettings;
this.helper = helper;
}
@Override
public boolean isApplicable(Volume caller) {
return SystemUtils.IS_OS_MAC;
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return Optional.of(helper.chooseTemporaryMountPoint(vaultSettings, VOLUME_PATH));
}
@Override
public boolean prepare(Volume caller, Path mountPoint) {
// https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
// In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
// starting with version 3.5.0, FUSE will create non-existent mount points automatically.
// Therefore we don't need to prepare anything.
return false;
}
}
@@ -1,138 +0,0 @@
package org.cryptomator.common.mountpoint;
import dagger.multibindings.IntKey;
import org.cryptomator.common.vaults.Volume;
import java.nio.file.Path;
import java.util.Optional;
import java.util.SortedSet;
/**
* Base interface for the Mountpoint-Choosing-Operation that results in the choice and
* preparation of a mountpoint or an exception otherwise.<br>
* <p>All <i>MountPointChoosers (MPCs)</i> need to implement this class and must be added to
* the pool of possible MPCs by the {@link MountPointChooserModule MountPointChooserModule.}
* The MountPointChooserModule will sort them according to their {@link IntKey IntKey priority.}
* The priority must be defined by the developer to reflect a useful execution order.<br>
* A specific priority <b>must not</b> be assigned to more than one MPC at a time;
* the result of having two MPCs with equal priority is undefined.
*
* <p>MPCs are executed by a {@link Volume} in descending order of their priority
* (higher priorities are tried first) to find and prepare a suitable mountpoint for the volume.
* The volume has access to a {@link SortedSet} of MPCs in this specific order,
* that is provided by the Module. The Set contains all available Choosers, even if they
* are not {@link #isApplicable(Volume) applicable} for the Vault/Volume. The Volume must
* check whether a MPC is applicable by invoking {@code #isApplicable(Volume)} on it
* <i>before</i> calling {@code #chooseMountPoint(Volume)}.
*
* <p>At execution of a MPC {@link #chooseMountPoint(Volume)} is called to choose a mountpoint
* according to the MPC's <i>strategy.</i> The <i>strategy</i> can involve reading configs,
* searching the filesystem, processing user-input or similar operations.
* If {@code #chooseMountPoint(Volume)} returns a non-null path (everything but
* {@linkplain Optional#empty()}) the MPC's {@link #prepare(Volume, Path)} method is called and the
* MountPoint is verified and/or prepared. In this case <i>no other MPC's will be called for
* this volume, even if {@code #prepare(Volume, Path)} fails.</i>
*
* <p>If {@code #chooseMountPoint(Volume)} yields no result, the next MPC is executed
* <i>without</i> first calling the {@code #prepare(Volume, Path)} method of the current MPC.
* This is repeated until<br>
* <ul>
* <li><b>either</b> a mountpoint is returned by {@code #chooseMountPoint(Volume)}
* and {@code #prepare(Volume, Path)} succeeds or fails, ending the entire operation</li>
* <li><b>or</b> no MPC remains and an {@link InvalidMountPointException} is thrown.</li>
* </ul>
* If the {@code #prepare(Volume, Path)} method of a MPC fails, the entire
* Mountpoint-Choosing-Operation is aborted and the method should do all necessary cleanup
* before throwing the exception.
* If the preparation succeeds {@link #cleanup(Volume, Path)} can be used after unmount to do any
* remaining cleanup.
*/
public interface MountPointChooser {
/**
* Called by the {@link Volume} to determine whether this MountPointChooser is
* applicable for mounting the Vault/Volume, especially with regard to the
* current system configuration and particularities of the Volume type.
*
* <p>Developers should override this method to check for system configurations
* that are unsuitable for this MPC.
*
* @param caller The Volume that is calling the method to determine applicability of the MPC
* @return a boolean flag; true if applicable, else false.
* @see #chooseMountPoint(Volume)
*/
boolean isApplicable(Volume caller);
/**
* Called by a {@link Volume} to choose a mountpoint according to the
* MountPointChoosers strategy.
*
* <p>This method must only be called for MPCs that were deemed
* {@link #isApplicable(Volume) applicable} by the {@link Volume Volume.}
* Developers should override this method to find or extract a mountpoint for
* the volume <b>without</b> preparing it. Preparation should be done by
* {@link #prepare(Volume, Path)} instead.
* Exceptions in this method should be handled gracefully and result in returning
* {@link Optional#empty()} instead of throwing an exception.
*
* @param caller The Volume that is calling the method to choose a mountpoint
* @return the chosen path or {@link Optional#empty()} if an exception occurred
* or no mountpoint could be found.
* @see #isApplicable(Volume)
* @see #prepare(Volume, Path)
*/
Optional<Path> chooseMountPoint(Volume caller);
/**
* Called by a {@link Volume} to prepare and/or verify the chosen mountpoint.<br>
* This method is only called if the {@link #chooseMountPoint(Volume)} method
* of the same MountPointChooser returned a path.
*
* <p>Developers should override this method to prepare the mountpoint for
* the volume and check for any obstacles that could hinder the mount operation.
* The mountpoint is deemed "prepared" if it can be used to mount a volume
* without any further filesystem actions or user interaction. If this is not possible,
* this method should fail. In other words: This method should not return without
* either failing or finalizing the preparation of the mountpoint.
* Generally speaking exceptions should be wrapped as
* {@link InvalidMountPointException} to allow efficient handling by the caller.
*
* <p>Often the preparation of a mountpoint involves creating files or others
* actions that require cleaning up after the volume is unmounted.
* In this case developers should override the {@link #cleanup(Volume, Path)}
* method and return {@code true} to the volume to indicate that the
* {@code #cleanup} method of this MPC should be called after unmount.
*
* <p><b>Please note:</b> If this method fails the entire
* Mountpoint-Choosing-Operation is aborted without calling
* {@link #cleanup(Volume, Path)} or any other MPCs. Therefore this method should
* do all necessary cleanup before throwing the exception.
*
* @param caller The Volume that is calling the method to prepare a mountpoint
* @param mountPoint the mountpoint chosen by {@link #chooseMountPoint(Volume)}
* @return a boolean flag; true if cleanup is needed, false otherwise
* @throws InvalidMountPointException if the preparation fails
* @see #chooseMountPoint(Volume)
* @see #cleanup(Volume, Path)
*/
default boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
return false; //NO-OP
}
/**
* Called by a {@link Volume} to do any cleanup needed after unmount.
*
* <p>This method is only called if the {@link #prepare(Volume, Path)} method
* of the same MountPointChooser returned {@code true}. Typically developers want to
* delete any files created prior to mount or do similar tasks.<br>
* Exceptions in this method should be handled gracefully.
*
* @param caller The Volume that is calling the method to cleanup the prepared mountpoint
* @param mountPoint the mountpoint that was prepared by {@link #prepare(Volume, Path)}
* @see #prepare(Volume, Path)
*/
default void cleanup(Volume caller, Path mountPoint) {
//NO-OP
}
}
@@ -1,64 +0,0 @@
package org.cryptomator.common.mountpoint;
import com.google.common.collect.Iterables;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntKey;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.PerVault;
import javax.inject.Named;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Dagger-Module for {@link MountPointChooser MountPointChoosers.}<br>
* See there for additional information.
*
* @see MountPointChooser
*/
@Module
public abstract class MountPointChooserModule {
@Binds
@IntoMap
@IntKey(1000)
@PerVault
public abstract MountPointChooser bindCustomMountPointChooser(CustomMountPointChooser chooser);
@Binds
@IntoMap
@IntKey(900)
@PerVault
public abstract MountPointChooser bindCustomDriveLetterChooser(CustomDriveLetterChooser chooser);
@Binds
@IntoMap
@IntKey(800)
@PerVault
public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
@Binds
@IntoMap
@IntKey(101)
@PerVault
public abstract MountPointChooser bindMacVolumeMountChooser(MacVolumeMountChooser chooser);
@Binds
@IntoMap
@IntKey(100)
@PerVault
public abstract MountPointChooser bindTemporaryMountPointChooser(TemporaryMountPointChooser chooser);
@Provides
@PerVault
@Named("orderedMountPointChoosers")
public static Iterable<MountPointChooser> provideOrderedMountPointChoosers(Map<Integer, MountPointChooser> choosers) {
SortedMap<Integer, MountPointChooser> sortedChoosers = new TreeMap<>(Comparator.reverseOrder());
sortedChoosers.putAll(choosers);
return Iterables.unmodifiableIterable(sortedChoosers.values());
}
}
@@ -1,122 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Optional;
@Singleton
class MountPointHelper {
public static Logger LOG = LoggerFactory.getLogger(MountPointHelper.class);
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
private final Optional<Path> tmpMountPointDir;
private volatile boolean unmountDebrisCleared = false;
@Inject
public MountPointHelper(Environment env) {
this.tmpMountPointDir = env.getMountPointsDir();
}
public Path chooseTemporaryMountPoint(VaultSettings vaultSettings, Path parentDir) {
String basename = vaultSettings.mountName().get();
//regular
Path mountPoint = parentDir.resolve(basename);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id
mountPoint = parentDir.resolve(basename + " (" + vaultSettings.getId() + ")");
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id and count
for (int i = 1; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
mountPoint = parentDir.resolve(basename + "_(" + vaultSettings.getId() + ")_" + i);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
}
LOG.error("Failed to find feasible mountpoint at {}{}{}_x. Giving up after {} attempts.", parentDir, File.separator, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES);
return null;
}
public synchronized void clearIrregularUnmountDebrisIfNeeded() {
if (unmountDebrisCleared || tmpMountPointDir.isEmpty()) {
return; // nothing to do
}
if (Files.exists(tmpMountPointDir.get(), LinkOption.NOFOLLOW_LINKS)) {
clearIrregularUnmountDebris(tmpMountPointDir.get());
}
unmountDebrisCleared = true;
}
private void clearIrregularUnmountDebris(Path dirContainingMountPoints) {
IOException cleanupFailed = new IOException("Cleanup failed");
try (var ds = Files.newDirectoryStream(dirContainingMountPoints)) {
LOG.debug("Performing cleanup of mountpoint dir {}.", dirContainingMountPoints);
for (Path p : ds) {
try {
var attr = Files.readAttributes(p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
if (attr.isOther() && attr.isDirectory()) { // yes, this is possible with windows junction points -.-
Files.delete(p);
} else if (attr.isDirectory()) {
deleteEmptyDir(p);
} else if (attr.isSymbolicLink()) {
deleteDeadLink(p);
} else {
LOG.debug("Found non-directory element in mountpoint dir: {}", p);
}
} catch (IOException e) {
cleanupFailed.addSuppressed(e);
}
}
if (cleanupFailed.getSuppressed().length > 0) {
throw cleanupFailed;
}
} catch (IOException e) {
LOG.warn("Unable to perform cleanup of mountpoint dir {}.", dirContainingMountPoints, e);
} finally {
unmountDebrisCleared = true;
}
}
private void deleteEmptyDir(Path dir) throws IOException {
assert Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS);
try {
ensureIsEmpty(dir);
Files.delete(dir); // attempt to delete dir non-recursively (will fail, if there are contents)
} catch (DirectoryNotEmptyException e) {
LOG.info("Found non-empty directory in mountpoint dir: {}", dir);
}
}
private void deleteDeadLink(Path symlink) throws IOException {
assert Files.isSymbolicLink(symlink);
if (Files.notExists(symlink)) { // following link: target does not exist
Files.delete(symlink);
}
}
private void ensureIsEmpty(Path dir) throws IOException {
try (var ds = Files.newDirectoryStream(dir)) {
if (ds.iterator().hasNext()){
throw new DirectoryNotEmptyException(dir.toString());
}
}
}
}
@@ -1,87 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
class TemporaryMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(TemporaryMountPointChooser.class);
private final VaultSettings vaultSettings;
private final Environment environment;
private final MountPointHelper helper;
@Inject
public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment, MountPointHelper helper) {
this.vaultSettings = vaultSettings;
this.environment = environment;
this.helper = helper;
}
@Override
public boolean isApplicable(Volume caller) {
if (this.environment.getMountPointsDir().isEmpty()) {
LOG.warn("\"cryptomator.mountPointsDir\" is not set to a valid path!");
return false;
}
return true;
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
assert environment.getMountPointsDir().isPresent();
//clean leftovers of not-regularly unmounted vaults
//see https://github.com/cryptomator/cryptomator/issues/1013 and https://github.com/cryptomator/cryptomator/issues/1061
helper.clearIrregularUnmountDebrisIfNeeded();
return this.environment.getMountPointsDir().map(dir -> this.helper.chooseTemporaryMountPoint(this.vaultSettings, dir));
}
@Override
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
try {
switch (caller.getMountPointRequirement()) {
case PARENT_NO_MOUNT_POINT -> {
Files.createDirectories(mountPoint.getParent());
LOG.debug("Successfully created folder for mount point: {}", mountPoint);
return false;
}
case EMPTY_MOUNT_POINT -> {
Files.createDirectories(mountPoint);
LOG.debug("Successfully created mount point: {}", mountPoint);
return true;
}
case NONE -> {
//Requirement "NONE" doesn't make any sense here.
//No need to prepare/verify a Mountpoint without requiring one...
throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement"));
}
default -> {
//Currently the case for "UNUSED_ROOT_DIR, PARENT_OPT_MOUNT_POINT"
throw new InvalidMountPointException(new IllegalStateException("Not implemented"));
}
}
} catch (IOException exception) {
throw new InvalidMountPointException("IOException while preparing mountpoint", exception);
}
}
@Override
public void cleanup(Volume caller, Path mountPoint) {
try {
Files.delete(mountPoint);
LOG.debug("Successfully deleted mount point: {}", mountPoint);
} catch (IOException e) {
LOG.warn("Could not delete mount point: {}", e.getMessage());
}
}
}
@@ -0,0 +1,99 @@
package org.cryptomator.common.settings;
import com.google.common.base.Preconditions;
import com.google.common.base.Suppliers;
import org.cryptomator.common.Environment;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.cryptolib.common.P384KeyPair;
import org.cryptomator.cryptolib.common.Pkcs12Exception;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.UUID;
import java.util.function.Supplier;
@Singleton
public class DeviceKey {
private static final Logger LOG = LoggerFactory.getLogger(DeviceKey.class);
private static final String KEYCHAIN_KEY = "cryptomator-device-p12";
private static final String KEYCHAIN_DISPLAY_NAME = "Cryptomator Device Keypair .p12 Passphrase";
private final KeychainManager keychainManager;
private final Environment env;
private final SecureRandom csprng;
private final Supplier<P384KeyPair> keyPairSupplier;
@Inject
public DeviceKey(KeychainManager keychainManager, Environment env, SecureRandom csprng) {
this.keychainManager = keychainManager;
this.env = env;
this.csprng = csprng;
this.keyPairSupplier = Suppliers.memoize(this::loadOrCreate);
}
public P384KeyPair get() throws DeviceKeyRetrievalException {
Preconditions.checkState(keychainManager.isSupported());
return keyPairSupplier.get();
}
private P384KeyPair loadOrCreate() throws DeviceKeyRetrievalException {
Path p12File = env.getP12Path().findFirst().orElseThrow(() -> new DeviceKeyRetrievalException("No path for .p12 file configured"));
char[] passphrase = null;
try {
passphrase = keychainManager.loadPassphrase(KEYCHAIN_KEY);
if (passphrase != null && Files.isRegularFile(p12File)) {
return loadExistingKeyPair(passphrase, p12File);
} else { // (re)generate new key pair if either file or password got lost
passphrase = randomPassword();
keychainManager.storePassphrase(KEYCHAIN_KEY, KEYCHAIN_DISPLAY_NAME, CharBuffer.wrap(passphrase));
return createAndStoreNewKeyPair(passphrase, p12File);
}
} catch (KeychainAccessException e) {
throw new DeviceKeyRetrievalException("Failed to access system keychain", e);
} catch (Pkcs12Exception | IOException e) {
throw new DeviceKeyRetrievalException("Failed to access .p12 file", e);
} finally {
if (passphrase != null) {
Arrays.fill(passphrase, '\0');
}
}
}
private P384KeyPair loadExistingKeyPair(char[] passphrase, Path p12File) throws IOException {
LOG.debug("Loading existing device key from {}", p12File);
return P384KeyPair.load(p12File, passphrase);
}
private P384KeyPair createAndStoreNewKeyPair(char[] passphrase, Path p12File) throws IOException {
var keyPair = P384KeyPair.generate();
LOG.debug("Store new device key to {}", p12File);
keyPair.store(p12File, passphrase);
return keyPair;
}
private char[] randomPassword() {
// this is a fast & easy attempt to create a random string:
var uuid = new UUID(csprng.nextLong(), csprng.nextLong());
return uuid.toString().toCharArray();
}
public static class DeviceKeyRetrievalException extends RuntimeException {
private DeviceKeyRetrievalException(String message) {
super(message);
}
private DeviceKeyRetrievalException(String message, Throwable cause) {
super(message, cause);
}
}
}
@@ -32,11 +32,11 @@ public class Settings {
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
public static final boolean DEFAULT_START_HIDDEN = false;
public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
public static final boolean DEFAULT_USE_KEYCHAIN = true;
public static final int DEFAULT_PORT = 42427;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
public static final boolean DEFAULT_DEBUG_MODE = false;
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually
public 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";
@@ -44,17 +44,18 @@ public class Settings {
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
public static final String DEFAULT_LANGUAGE = null;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
private final BooleanProperty autoCloseVaults = new SimpleBooleanProperty(DEFAULT_AUTO_CLOSE_VAULTS);
private final BooleanProperty useKeychain = new SimpleBooleanProperty(DEFAULT_USE_KEYCHAIN);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
private final ObjectProperty<WebDavUrlScheme> preferredGvfsScheme = new SimpleObjectProperty<>(DEFAULT_GVFS_SCHEME);
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
private final ObjectProperty<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
private final ObjectProperty<String> keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
@@ -66,6 +67,10 @@ public class Settings {
private final IntegerProperty windowWidth = new SimpleIntegerProperty();
private final IntegerProperty windowHeight = new SimpleIntegerProperty();
private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
private final StringProperty mountService = new SimpleStringProperty();
private Consumer<Settings> saveCmd;
@@ -80,11 +85,11 @@ public class Settings {
askedForUpdateCheck.addListener(this::somethingChanged);
checkForUpdates.addListener(this::somethingChanged);
startHidden.addListener(this::somethingChanged);
autoCloseVaults.addListener(this::somethingChanged);
useKeychain.addListener(this::somethingChanged);
port.addListener(this::somethingChanged);
numTrayNotifications.addListener(this::somethingChanged);
preferredGvfsScheme.addListener(this::somethingChanged);
debugMode.addListener(this::somethingChanged);
preferredVolumeImpl.addListener(this::somethingChanged);
theme.addListener(this::somethingChanged);
keychainProvider.addListener(this::somethingChanged);
userInterfaceOrientation.addListener(this::somethingChanged);
@@ -96,6 +101,8 @@ public class Settings {
windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
}
void setSaveCmd(Consumer<Settings> saveCmd) {
@@ -130,6 +137,12 @@ public class Settings {
return startHidden;
}
public BooleanProperty autoCloseVaults() {
return autoCloseVaults;
}
public BooleanProperty useKeychain() { return useKeychain; }
public IntegerProperty port() {
return port;
}
@@ -138,16 +151,12 @@ public class Settings {
return numTrayNotifications;
}
public ObjectProperty<WebDavUrlScheme> preferredGvfsScheme() {
return preferredGvfsScheme;
}
public BooleanProperty debugMode() {
return debugMode;
}
public ObjectProperty<VolumeImpl> preferredVolumeImpl() {
return preferredVolumeImpl;
public StringProperty mountService() {
return mountService;
}
public ObjectProperty<UiTheme> theme() {
@@ -191,4 +200,9 @@ public class Settings {
public ObjectProperty<String> displayConfigurationProperty() {
return displayConfiguration;
}
public StringProperty languageProperty() {
return language;
}
}
@@ -41,14 +41,14 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
out.name("startHidden").value(value.startHidden().get());
out.name("autoCloseVaults").value(value.autoCloseVaults().get());
out.name("port").value(value.port().get());
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get().name());
out.name("debugMode").value(value.debugMode().get());
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
out.name("theme").value(value.theme().get().name());
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
out.name("keychainProvider").value(value.keychainProvider().get());
out.name("useKeychain").value(value.useKeychain().get());
out.name("licenseKey").value(value.licenseKey().get());
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
out.name("showTrayIcon").value(value.showTrayIcon().get());
@@ -57,7 +57,8 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("windowWidth").value((value.windowWidthProperty().get()));
out.name("windowHeight").value((value.windowHeightProperty().get()));
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
out.name("language").value((value.languageProperty().get()));
out.name("mountService").value(value.mountService().get());
out.endObject();
}
@@ -81,14 +82,14 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
case "port" -> settings.port().set(in.nextInt());
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
case "preferredGvfsScheme" -> settings.preferredGvfsScheme().set(parseWebDavUrlSchemePrefix(in.nextString()));
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
case "preferredVolumeImpl" -> settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
case "licenseKey" -> settings.licenseKey().set(in.nextString());
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
@@ -97,9 +98,16 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
case "language" -> settings.languageProperty().set(in.nextString());
case "mountService" -> {
var token = in.peek();
if (JsonToken.STRING == token) {
settings.mountService().set(in.nextString());
}
}
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
LOG.warn("Unsupported vault setting found in JSON: {}", name);
in.skipValue();
}
}
@@ -109,24 +117,6 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
return settings;
}
private VolumeImpl parsePreferredVolumeImplName(String nioAdapterName) {
try {
return VolumeImpl.valueOf(nioAdapterName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid volume type {}. Defaulting to {}.", nioAdapterName, Settings.DEFAULT_PREFERRED_VOLUME_IMPL);
return Settings.DEFAULT_PREFERRED_VOLUME_IMPL;
}
}
private WebDavUrlScheme parseWebDavUrlSchemePrefix(String webDavUrlSchemeName) {
try {
return WebDavUrlScheme.valueOf(webDavUrlSchemeName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid WebDAV url scheme {}. Defaulting to {}.", webDavUrlSchemeName, Settings.DEFAULT_GVFS_SCHEME);
return Settings.DEFAULT_GVFS_SCHEME;
}
}
private UiTheme parseUiTheme(String uiThemeName) {
try {
return UiTheme.valueOf(uiThemeName.toUpperCase());
@@ -49,14 +49,12 @@ public class SettingsProvider implements Supplier<Settings> {
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
private final SettingsJsonAdapter settingsJsonAdapter;
private final Environment env;
private final ScheduledExecutorService scheduler;
private final Gson gson;
@Inject
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
this.settingsJsonAdapter = settingsJsonAdapter;
this.env = env;
this.scheduler = scheduler;
this.gson = new GsonBuilder() //
@@ -118,7 +116,7 @@ public class SettingsProvider implements Supplier<Settings> {
try {
Files.createDirectories(settingsPath.getParent());
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE_NEW); //
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
gson.toJson(settings, writer);
}
@@ -3,9 +3,9 @@ package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
public enum UiTheme {
LIGHT("preferences.general.theme.light"), //
DARK("preferences.general.theme.dark"), //
AUTOMATIC("preferences.general.theme.automatic");
LIGHT("preferences.interface.theme.light"), //
DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic");
public static UiTheme[] applicableValues() {
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
@@ -6,12 +6,11 @@
package org.cryptomator.common.settings;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.binding.StringExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
@@ -22,7 +21,6 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
/**
@@ -32,7 +30,6 @@ public class VaultSettings {
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
public static final boolean DEFAULT_USES_READONLY_MODE = false;
public static final String DEFAULT_MOUNT_FLAGS = "";
public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
@@ -45,26 +42,32 @@ public class VaultSettings {
private final String id;
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
private final StringProperty displayName = new SimpleStringProperty();
private final StringProperty winDriveLetter = new SimpleStringProperty();
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
private final StringProperty customMountPath = new SimpleStringProperty();
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
private final StringBinding mountName;
private final StringExpression mountName;
private final ObjectProperty<Path> mountPoint = new SimpleObjectProperty<>();
public VaultSettings(String id) {
this.id = Objects.requireNonNull(id);
this.mountName = Bindings.createStringBinding(this::normalizeDisplayName, displayName);
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name;
if (displayName.isEmpty().get()) {
name = path.get().getFileName().toString();
} else {
name = displayName.get();
}
return normalizeDisplayName(name);
}, displayName, path));
}
Observable[] observables() {
return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, maxCleartextFilenameLength, actionAfterUnlock, autoLockWhenIdle, autoLockIdleSeconds};
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
}
public static VaultSettings withRandomId() {
@@ -78,8 +81,7 @@ public class VaultSettings {
}
//visible for testing
String normalizeDisplayName() {
var original = displayName.getValueSafe();
static String normalizeDisplayName(String original) {
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
return "_";
}
@@ -105,22 +107,10 @@ public class VaultSettings {
return displayName;
}
public StringBinding mountName() {
public StringExpression mountName() {
return mountName;
}
public StringProperty winDriveLetter() {
return winDriveLetter;
}
public Optional<String> getWinDriveLetter() {
String current = this.winDriveLetter.get();
if (!Strings.isNullOrEmpty(current)) {
return Optional.of(current);
}
return Optional.empty();
}
public BooleanProperty unlockAfterStartup() {
return unlockAfterStartup;
}
@@ -129,20 +119,12 @@ public class VaultSettings {
return revealAfterMount;
}
public BooleanProperty useCustomMountPath() {
return useCustomMountPath;
public Path getMountPoint() {
return mountPoint.get();
}
public StringProperty customMountPath() {
return customMountPath;
}
public Optional<String> getCustomMountPath() {
if (useCustomMountPath.get()) {
return Optional.ofNullable(Strings.emptyToNull(customMountPath.get()));
} else {
return Optional.empty();
}
public ObjectProperty<Path> mountPoint() {
return mountPoint;
}
public BooleanProperty usesReadOnlyMode() {
@@ -188,5 +170,4 @@ public class VaultSettings {
return false;
}
}
}
@@ -6,11 +6,14 @@
package org.cryptomator.common.settings;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
class VaultSettingsJsonAdapter {
@@ -22,11 +25,10 @@ class VaultSettingsJsonAdapter {
out.name("id").value(value.getId());
out.name("path").value(value.path().get().toString());
out.name("displayName").value(value.displayName().get());
out.name("winDriveLetter").value(value.winDriveLetter().get());
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
out.name("revealAfterMount").value(value.revealAfterMount().get());
out.name("useCustomMountPath").value(value.useCustomMountPath().get());
out.name("customMountPath").value(value.customMountPath().get());
var mountPoint = value.mountPoint().get();
out.name("mountPoint").value(mountPoint != null ? mountPoint.toAbsolutePath().toString() : null);
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
out.name("mountFlags").value(value.mountFlags().get());
out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
@@ -36,18 +38,18 @@ class VaultSettingsJsonAdapter {
out.endObject();
}
//TODO: usesCustomMountPath, customMountPath and winDriveLetter removed
// -> migration required
public VaultSettings read(JsonReader in) throws IOException {
String id = null;
String path = null;
String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
String displayName = null;
String customMountPath = null;
String winDriveLetter = null;
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
Path mountPoint = null;
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
@@ -61,19 +63,23 @@ class VaultSettingsJsonAdapter {
case "path" -> path = in.nextString();
case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
case "displayName" -> displayName = in.nextString();
case "winDriveLetter" -> winDriveLetter = in.nextString();
case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
case "mountFlags" -> mountFlags = in.nextString();
case "mountPoint" -> {
if (JsonToken.NULL == in.peek()) {
in.nextNull();
} else {
mountPoint = parseMountPoint(in.nextString());
}
}
case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
LOG.warn("Unsupported vault setting found in JSON: {}", name);
in.skipValue();
}
}
@@ -87,20 +93,27 @@ class VaultSettingsJsonAdapter {
vaultSettings.displayName().set(mountName);
}
vaultSettings.path().set(Paths.get(path));
vaultSettings.winDriveLetter().set(winDriveLetter);
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
vaultSettings.revealAfterMount().set(revealAfterMount);
vaultSettings.useCustomMountPath().set(useCustomMountPath);
vaultSettings.customMountPath().set(customMountPath);
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
vaultSettings.mountFlags().set(mountFlags);
vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
vaultSettings.mountPoint().set(mountPoint);
return vaultSettings;
}
private Path parseMountPoint(String mountPoint) {
try {
return Path.of(mountPoint);
} catch (InvalidPathException e) {
LOG.warn("Invalid string as mount point. Defaulting to null.");
return null;
}
}
private WhenUnlocked parseActionAfterUnlock(String actionAfterUnlockName) {
try {
return WhenUnlocked.valueOf(actionAfterUnlockName.toUpperCase());
@@ -1,22 +0,0 @@
package org.cryptomator.common.settings;
public enum WebDavUrlScheme {
DAV("dav", "dav:// (Gnome, Nautilus, ...)"),
WEBDAV("webdav", "webdav:// (KDE, Dolphin, ...)");
private final String prefix;
private final String displayName;
WebDavUrlScheme(String prefix, String displayName) {
this.prefix = prefix;
this.displayName = displayName;
}
public String getPrefix() {
return prefix;
}
public String getDisplayName() {
return displayName;
}
}
@@ -1,46 +0,0 @@
package org.cryptomator.common.vaults;
import com.google.common.collect.Iterables;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import java.nio.file.Path;
import java.util.Optional;
public abstract class AbstractVolume implements Volume {
private final Iterable<MountPointChooser> choosers;
protected Path mountPoint;
private boolean cleanupRequired;
private MountPointChooser usedChooser;
public AbstractVolume(Iterable<MountPointChooser> choosers) {
this.choosers = choosers;
}
protected Path determineMountPoint() throws InvalidMountPointException {
var applicableChoosers = Iterables.filter(choosers, c -> c.isApplicable(this));
for (var chooser : applicableChoosers) {
Optional<Path> chosenPath = chooser.chooseMountPoint(this);
if (chosenPath.isEmpty()) { // chooser couldn't find a feasible mountpoint
continue;
}
this.cleanupRequired = chooser.prepare(this, chosenPath.get());
this.usedChooser = chooser;
return chosenPath.get();
}
throw new InvalidMountPointException(String.format("No feasible MountPoint found by choosers: %s", applicableChoosers));
}
protected void cleanupMountPoint() {
if (this.cleanupRequired) {
this.usedChooser.cleanup(this, this.mountPoint);
}
}
@Override
public Optional<Path> getMountPoint() {
return Optional.ofNullable(mountPoint);
}
}
@@ -1,11 +1,13 @@
package org.cryptomator.common.vaults;
import org.cryptomator.integrations.mount.UnmountFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ObservableList;
import java.io.IOException;
import java.time.Instant;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -39,14 +41,13 @@ public class AutoLocker {
try {
vault.lock(false);
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
} catch (Volume.VolumeException | LockNotCompletedException e) {
} catch (UnmountFailedException | IOException e) {
LOG.error("Autolocking failed.", e);
}
}
private boolean exceedsIdleTime(Vault vault) {
assert vault.isUnlocked();
// TODO: shouldn't we read these properties from within FX Application Thread?
if (vault.getVaultSettings().autoLockWhenIdle().get()) {
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
@@ -1,95 +0,0 @@
package org.cryptomator.common.vaults;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.dokany.DokanyMountFailedException;
import org.cryptomator.frontend.dokany.Mount;
import org.cryptomator.frontend.dokany.MountFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.function.Consumer;
public class DokanyVolume extends AbstractVolume {
private static final Logger LOG = LoggerFactory.getLogger(DokanyVolume.class);
private static final String FS_TYPE_NAME = "CryptomatorFS";
private final VaultSettings vaultSettings;
private Mount mount;
@Inject
public DokanyVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
this.vaultSettings = vaultSettings;
}
@Override
public VolumeImpl getImplementationType() {
return VolumeImpl.DOKANY;
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
try {
this.mount = MountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip(), onExitAction);
} catch (DokanyMountFailedException e) {
if (vaultSettings.getCustomMountPath().isPresent()) {
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
}
throw new VolumeException("Unable to mount Filesystem", e);
}
}
@Override
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@Override
public void unmount() throws VolumeException {
try {
mount.unmount();
} catch (IllegalStateException e) {
throw new VolumeException("Unmount Failed.", e);
}
cleanupMountPoint();
}
@Override
public void unmountForced() {
mount.unmountForced();
cleanupMountPoint();
}
@Override
public boolean supportsForcedUnmount() {
return true;
}
@Override
public boolean isSupported() {
return DokanyVolume.isSupportedStatic();
}
@Override
public MountPointRequirement getMountPointRequirement() {
return this.vaultSettings.getWinDriveLetter().isPresent() ? MountPointRequirement.UNUSED_ROOT_DIR : MountPointRequirement.EMPTY_MOUNT_POINT;
}
public static boolean isSupportedStatic() {
return MountFactory.isApplicable();
}
}
@@ -1,136 +0,0 @@
package org.cryptomator.common.vaults;
import com.google.common.collect.Iterators;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
import org.cryptomator.frontend.fuse.mount.FuseMountException;
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
import org.cryptomator.frontend.fuse.mount.Mount;
import org.cryptomator.frontend.fuse.mount.Mounter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
public class FuseVolume extends AbstractVolume {
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
private static final Pattern NON_WHITESPACE_OR_QUOTED = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); // Thanks to https://stackoverflow.com/a/366532
private final VaultSettings vaultSettings;
private Mount mount;
@Inject
public FuseVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
this.vaultSettings = vaultSettings;
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
mount(fs.getPath("/"), mountFlags, onExitAction);
}
private void mount(Path root, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
try {
Mounter mounter = FuseMountFactory.getMounter();
EnvironmentVariables envVars = EnvironmentVariables.create() //
.withFlags(splitFlags(mountFlags)) //
.withMountPoint(mountPoint) //
.withFileNameTranscoder(mounter.defaultFileNameTranscoder()) //
.build();
this.mount = mounter.mount(root, envVars, onExitAction);
} catch (FuseMountException | FuseNotSupportedException e) {
throw new VolumeException("Unable to mount Filesystem", e);
}
}
private String[] splitFlags(String str) {
List<String> flags = new ArrayList<>();
var matches = Iterators.peekingIterator(NON_WHITESPACE_OR_QUOTED.matcher(str).results().iterator());
while (matches.hasNext()) {
String flag = matches.next().group();
// check if flag is missing its argument:
if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(1) != null) { // next is "double quoted"
// next is "double quoted" and flag is missing its argument
flag += matches.next().group(1);
} else if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(2) != null) {
// next is 'single quoted' and flag is missing its argument
flag += matches.next().group(2);
}
flags.add(flag);
}
return flags.toArray(String[]::new);
}
@Override
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@Override
public boolean supportsForcedUnmount() {
return true;
}
@Override
public synchronized void unmountForced() throws VolumeException {
try {
mount.unmountForced();
} catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
}
@Override
public synchronized void unmount() throws VolumeException {
try {
mount.unmount();
} catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
}
@Override
public boolean isSupported() {
return FuseVolume.isSupportedStatic();
}
@Override
public VolumeImpl getImplementationType() {
return VolumeImpl.FUSE;
}
@Override
public MountPointRequirement getMountPointRequirement() {
if (!SystemUtils.IS_OS_WINDOWS) {
return MountPointRequirement.EMPTY_MOUNT_POINT;
}
return this.vaultSettings.getWinDriveLetter().isPresent() ? MountPointRequirement.UNUSED_ROOT_DIR : MountPointRequirement.PARENT_NO_MOUNT_POINT;
}
public static boolean isSupportedStatic() {
return FuseMountFactory.isFuseSupported();
}
}
@@ -1,12 +0,0 @@
package org.cryptomator.common.vaults;
public class LockNotCompletedException extends Exception {
public LockNotCompletedException(String reason) {
super(reason);
}
public LockNotCompletedException(Throwable cause) {
super(cause);
}
}
@@ -1,33 +0,0 @@
package org.cryptomator.common.vaults;
/**
* Enumeration used to indicate the requirements for mounting a vault
* using a specific {@link Volume VolumeProvider}, e.g. {@link FuseVolume}.
*/
public enum MountPointRequirement {
/**
* The Mountpoint needs to be a filesystem root and must not exist.
*/
UNUSED_ROOT_DIR,
/**
* No Mountpoint on the local filesystem required. (e.g. WebDAV)
*/
NONE,
/**
* A parent folder is required, but the actual Mountpoint must not exist.
*/
PARENT_NO_MOUNT_POINT,
/**
* A parent folder is required, but the actual Mountpoint may exist.
*/
PARENT_OPT_MOUNT_POINT,
/**
* The actual Mountpoint must exist and must be empty.
*/
EMPTY_MOUNT_POINT;
}
@@ -10,9 +10,11 @@ package org.cryptomator.common.vaults;
import com.google.common.base.Strings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.Constants;
import org.cryptomator.common.Environment;
import org.cryptomator.common.mount.WindowsDriveLetters;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume.VolumeException;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
@@ -21,29 +23,42 @@ import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountBuilder;
import org.cryptomator.integrations.mount.MountCapability;
import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.beans.Observable;
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.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_SYSTEM_CHOSEN_PATH;
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_WITHIN_EXISTING_PARENT;
@PerVault
public class Vault {
@@ -51,15 +66,16 @@ public class Vault {
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
private static final int UNLIMITED_FILENAME_LENGTH = Integer.MAX_VALUE;
private final Environment env;
private final Settings settings;
private final VaultSettings vaultSettings;
private final Provider<Volume> volumeProvider;
private final StringBinding defaultMountFlags;
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
private final VaultState state;
private final ObjectProperty<Exception> lastKnownException;
private final ObservableValue<MountService> mountService;
private final ObservableValue<String> defaultMountFlags;
private final VaultConfigCache configCache;
private final VaultStats stats;
private final StringBinding displayName;
private final StringBinding displayablePath;
private final BooleanBinding locked;
private final BooleanBinding processing;
@@ -67,23 +83,24 @@ public class Vault {
private final BooleanBinding missing;
private final BooleanBinding needsMigration;
private final BooleanBinding unknownError;
private final StringBinding accessPoint;
private final BooleanBinding accessPointPresent;
private final ObjectBinding<Mountpoint> mountPoint;
private final WindowsDriveLetters windowsDriveLetters;
private final BooleanProperty showingStats;
private volatile Volume volume;
private AtomicReference<MountHandle> mountHandle = new AtomicReference<>(null);
@Inject
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
Vault(Environment env, Settings settings, VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, ObservableValue<MountService> mountService, VaultStats stats, WindowsDriveLetters windowsDriveLetters) {
this.env = env;
this.settings = settings;
this.vaultSettings = vaultSettings;
this.configCache = configCache;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
this.cryptoFileSystem = cryptoFileSystem;
this.state = state;
this.lastKnownException = lastKnownException;
this.mountService = mountService;
this.defaultMountFlags = mountService.map(MountService::getDefaultMountFlags);
this.stats = stats;
this.displayName = Bindings.createStringBinding(this::getDisplayName, vaultSettings.displayName());
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
this.locked = Bindings.createBooleanBinding(this::isLocked, state);
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
@@ -91,8 +108,8 @@ public class Vault {
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state);
this.accessPointPresent = this.accessPoint.isNotEmpty();
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
this.windowsDriveLetters = windowsDriveLetters;
this.showingStats = new SimpleBooleanProperty(false);
}
@@ -125,6 +142,7 @@ public class Vault {
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
@@ -141,7 +159,46 @@ public class Vault {
}
}
public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
private MountBuilder prepareMount(Path cryptoRoot) throws IOException {
var mountProvider = mountService.getValue();
var builder = mountProvider.forFileSystem(cryptoRoot);
for (var capability : mountProvider.capabilities()) {
switch (capability) {
case FILE_SYSTEM_NAME -> builder.setFileSystemName("crypto");
case LOOPBACK_PORT -> builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings?
case LOOPBACK_HOST_NAME -> builder.setLoopbackHostName("cryptomator-vault"); //TODO: Read from system property
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
case MOUNT_FLAGS -> builder.setMountFlags(defaultMountFlags.getValue()); // TODO use custom mount flags (pre-populated with default mount flags)
case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId());
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
}
}
var userChosenMountPoint = vaultSettings.getMountPoint();
var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
if (userChosenMountPoint == null) {
if (mountProvider.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
// no need to set a mount point
} else if (mountProvider.hasCapability(MOUNT_AS_DRIVE_LETTER)) {
builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow());
} else if (mountProvider.hasCapability(MOUNT_WITHIN_EXISTING_PARENT)) {
Files.createDirectories(defaultMountPointBase);
builder.setMountpoint(defaultMountPointBase);
} else if (mountProvider.hasCapability(MOUNT_TO_EXISTING_DIR) ) {
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
Files.createDirectories(mountPoint);
builder.setMountpoint(mountPoint);
}
} else if (mountProvider.hasCapability(MOUNT_TO_EXISTING_DIR) || mountProvider.hasCapability(MOUNT_WITHIN_EXISTING_PARENT) || mountProvider.hasCapability(MOUNT_AS_DRIVE_LETTER)) {
// TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT?
builder.setMountpoint(userChosenMountPoint);
}
return builder;
}
public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, MountFailedException {
if (cryptoFileSystem.get() != null) {
throw new IllegalStateException("Already unlocked.");
}
@@ -149,9 +206,10 @@ public class Vault {
boolean success = false;
try {
cryptoFileSystem.set(fs);
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
success = true;
var rootPath = fs.getRootDirectories().iterator().next();
var supportsForcedUnmount = mountService.getValue().hasCapability(MountCapability.UNMOUNT_FORCED);
var mountHandle = new MountHandle(prepareMount(rootPath).mount(), supportsForcedUnmount);
success = this.mountHandle.compareAndSet(null, mountHandle);
} finally {
if (!success) {
destroyCryptoFileSystem();
@@ -159,37 +217,28 @@ public class Vault {
}
}
private void lockOnVolumeExit(Throwable t) {
LOG.info("Unmounted vault '{}'", getDisplayName());
destroyCryptoFileSystem();
state.set(VaultState.Value.LOCKED);
if (t != null) {
LOG.warn("Unexpected unmount and lock of vault " + getDisplayName(), t);
}
}
public synchronized void lock(boolean forced) throws VolumeException, LockNotCompletedException {
//initiate unmount
if (forced && volume.supportsForcedUnmount()) {
volume.unmountForced();
public synchronized void lock(boolean forced) throws UnmountFailedException, IOException {
var mountHandle = this.mountHandle.get();
if (mountHandle == null) {
//TODO: noop or InvalidStateException?
return;
}
if (forced && mountHandle.supportsUnmountForced) {
mountHandle.mount.unmountForced();
} else {
volume.unmount();
mountHandle.mount.unmount();
}
//wait for lockOnVolumeExit to be executed
try {
boolean locked = state.awaitState(VaultState.Value.LOCKED, 3000, TimeUnit.MILLISECONDS);
if (!locked) {
throw new LockNotCompletedException("Locking of vault " + this.getDisplayName() + " still in progress.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new LockNotCompletedException(e);
mountHandle.mount.close();
} finally {
destroyCryptoFileSystem();
}
}
public void reveal(Volume.Revealer vaultRevealer) throws VolumeException {
volume.reveal(vaultRevealer);
this.mountHandle.set(null);
LOG.info("Locked vault '{}'", getDisplayName());
}
// ******************************************************************************
@@ -264,33 +313,21 @@ public class Vault {
return state.get() == VaultState.Value.ERROR;
}
public StringBinding displayNameProperty() {
return displayName;
public ReadOnlyStringProperty displayNameProperty() {
return vaultSettings.displayName();
}
public String getDisplayName() {
return vaultSettings.displayName().get();
}
public StringBinding accessPointProperty() {
return accessPoint;
public ObjectBinding<Mountpoint> mountPointProperty() {
return mountPoint;
}
public String getAccessPoint() {
if (state.getValue() == VaultState.Value.UNLOCKED) {
assert volume != null;
return volume.getMountPoint().orElse(Path.of("")).toString();
} else {
return "";
}
}
public BooleanBinding accessPointPresentProperty() {
return accessPointPresent;
}
public boolean isAccessPointPresent() {
return accessPointPresent.get();
public Mountpoint getMountPoint() {
var handle = mountHandle.get();
return handle == null ? null : handle.mount.getMountpoint();
}
public StringBinding displayablePathProperty() {
@@ -313,7 +350,7 @@ public class Vault {
}
public boolean isShowingStats() {
return accessPointPresent.get();
return mountHandle.get() != null;
}
@@ -342,18 +379,18 @@ public class Vault {
return !Strings.isNullOrEmpty(vaultSettings.mountFlags().get());
}
public StringBinding defaultMountFlagsProperty() {
public ObservableValue<String> defaultMountFlagsProperty() {
return defaultMountFlags;
}
public String getDefaultMountFlags() {
return defaultMountFlags.get();
return defaultMountFlags.getValue();
}
public String getEffectiveMountFlags() {
String mountFlags = vaultSettings.mountFlags().get();
if (Strings.isNullOrEmpty(mountFlags)) {
return getDefaultMountFlags();
return ""; //TODO: should the provider provide dem defaults??
} else {
return mountFlags;
}
@@ -371,10 +408,6 @@ public class Vault {
return vaultSettings.getId();
}
public Optional<Volume> getVolume() {
return Optional.ofNullable(this.volume);
}
// ******************************************************************************
// Hashcode / Equals
// *******************************************************************************/
@@ -393,7 +426,15 @@ public class Vault {
}
}
/* TODO: reactivate/ needed at all?
public boolean supportsForcedUnmount() {
return volume.supportsForcedUnmount();
}
*/
private record MountHandle(Mount mount, boolean supportsUnmountForced) {
}
}
@@ -8,33 +8,23 @@ package org.cryptomator.common.vaults;
import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.mountpoint.MountPointChooserModule;
import org.cryptomator.common.settings.VaultSettings;
import javax.inject.Named;
@PerVault
@Subcomponent(modules = {VaultModule.class, MountPointChooserModule.class})
@Subcomponent(modules = {VaultModule.class})
public interface VaultComponent {
Vault vault();
@Subcomponent.Builder
interface Builder {
@Subcomponent.Factory
interface Factory {
@BindsInstance
Builder vaultSettings(VaultSettings vaultSettings);
VaultComponent create(@BindsInstance VaultSettings vaultSettings, //
@BindsInstance VaultConfigCache configCache, //
@BindsInstance VaultState.Value vaultState, //
@BindsInstance @Nullable @Named("lastKnownException") Exception initialErrorCause);
@BindsInstance
Builder vaultConfigCache(VaultConfigCache configCache);
@BindsInstance
Builder initialVaultState(VaultState.Value vaultState);
@BindsInstance
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
VaultComponent build();
}
}
}
@@ -38,15 +38,15 @@ public class VaultListManager {
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder;
private final VaultComponent.Factory vaultComponentFactory;
private final ObservableList<Vault> vaultList;
private final String defaultVaultName;
@Inject
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Factory vaultComponentFactory, ResourceBundle resourceBundle, Settings settings) {
this.vaultList = vaultList;
this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder;
this.vaultComponentFactory = vaultComponentFactory;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
addAll(settings.getDirectories());
@@ -93,21 +93,17 @@ public class VaultListManager {
}
private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
var wrapper = new VaultConfigCache(vaultSettings);
try {
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
VaultConfigCache wrapper = new VaultConfigCache(vaultSettings);
compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config
var vaultState = determineVaultState(vaultSettings.path().get());
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig();
}
compBuilder.initialVaultState(vaultState);
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
compBuilder.initialVaultState(ERROR);
compBuilder.initialErrorCause(e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
}
return compBuilder.build().vault();
}
public static VaultState.Value redetermineVaultState(Vault vault) {
@@ -7,26 +7,14 @@ package org.cryptomator.common.vaults;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
@Module
@@ -47,130 +35,4 @@ public class VaultModule {
return new SimpleObjectProperty<>(initialErrorCause);
}
@Provides
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
if (VolumeImpl.DOKANY == preferredImpl && dokanyVolume.isSupported()) {
return dokanyVolume;
} else if (VolumeImpl.FUSE == preferredImpl && fuseVolume.isSupported()) {
return fuseVolume;
} else {
if (VolumeImpl.WEBDAV != preferredImpl) {
LOG.warn("Using WebDAV, because {} is not supported.", preferredImpl.getDisplayName());
}
assert webDavVolume.isSupported();
return webDavVolume;
}
}
@Provides
@PerVault
@DefaultMountFlags
public StringBinding provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
ObjectProperty<VolumeImpl> preferredVolumeImpl = settings.preferredVolumeImpl();
StringBinding mountName = vaultSettings.mountName();
BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
return Bindings.createStringBinding(() -> {
VolumeImpl v = preferredVolumeImpl.get();
if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_MAC) {
return getMacFuseDefaultMountFlags(mountName, readOnly);
} else if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_LINUX) {
return getLinuxFuseDefaultMountFlags(readOnly);
} else if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_WINDOWS) {
return getWindowsFuseDefaultMountFlags(mountName, readOnly);
} else if (v == VolumeImpl.DOKANY && SystemUtils.IS_OS_WINDOWS) {
return getDokanyDefaultMountFlags(readOnly);
} else {
return "--flags-supported-on-FUSE-or-DOKANY-only";
}
}, mountName, readOnly, preferredVolumeImpl);
}
// see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
private String getMacFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_MAC_OSX;
StringBuilder flags = new StringBuilder();
if (readOnly.get()) {
flags.append(" -ordonly");
}
flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
flags.append(" -oatomic_o_trunc");
flags.append(" -oauto_xattr");
flags.append(" -oauto_cache");
flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
try {
Path userHome = Paths.get(System.getProperty("user.home"));
int uid = (int) Files.getAttribute(userHome, "unix:uid");
int gid = (int) Files.getAttribute(userHome, "unix:gid");
flags.append(" -ouid=").append(uid);
flags.append(" -ogid=").append(gid);
} catch (IOException e) {
LOG.error("Could not read uid/gid from USER_HOME", e);
}
return flags.toString().strip();
}
// see https://manpages.debian.org/testing/fuse/mount.fuse.8.en.html
private String getLinuxFuseDefaultMountFlags(ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_LINUX;
StringBuilder flags = new StringBuilder();
if (readOnly.get()) {
flags.append(" -oro");
}
flags.append(" -oauto_unmount");
try {
Path userHome = Paths.get(System.getProperty("user.home"));
int uid = (int) Files.getAttribute(userHome, "unix:uid");
int gid = (int) Files.getAttribute(userHome, "unix:gid");
flags.append(" -ouid=").append(uid);
flags.append(" -ogid=").append(gid);
} catch (IOException e) {
LOG.error("Could not read uid/gid from USER_HOME", e);
}
return flags.toString().strip();
}
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse_main.c#L53-L62 for syntax guide
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse.c#L295-L319 for options (-o <...>)
// see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were chosen
private String getWindowsFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_WINDOWS;
StringBuilder flags = new StringBuilder();
//WinFSP has no explicit "readonly"-option, nut not setting the group/user-id has the same effect, tho.
//So for the time being not setting them is the way to go...
//See: https://github.com/billziss-gh/winfsp/issues/319
if (!readOnly.get()) {
flags.append(" -ouid=-1");
flags.append(" -ogid=11");
}
flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
//Dokany requires this option to be set, WinFSP doesn't seem to share this peculiarity,
//but the option exists. Let's keep this here in case we need it.
// flags.append(" -oThreadCount=").append(5);
return flags.toString().strip();
}
// see https://github.com/cryptomator/dokany-nio-adapter/blob/develop/src/main/java/org/cryptomator/frontend/dokany/MountUtil.java#L30-L34
private String getDokanyDefaultMountFlags(ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_WINDOWS;
StringBuilder flags = new StringBuilder();
flags.append(" --options CURRENT_SESSION");
if (readOnly.get()) {
flags.append(",WRITE_PROTECTION");
}
flags.append(" --thread-count 5");
flags.append(" --timeout 10000");
flags.append(" --allocation-unit-size 4096");
flags.append(" --sector-size 4096");
return flags.toString().strip();
}
}
@@ -83,7 +83,7 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
if (success) {
fireValueChangedEvent();
} else {
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
LOG.debug("Failed transiting into state {}: Expected state was not {}.", fromState, toState);
}
return success;
}
@@ -1,102 +0,0 @@
package org.cryptomator.common.vaults;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Takes a Volume and uses it to mount an unlocked vault
*/
public interface Volume {
/**
* Checks in constant time whether this volume type is supported on the system running Cryptomator.
*
* @return true if this volume can be mounted
*/
boolean isSupported();
/**
* Gets the corresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
*
* @return the type of implementation as defined by the {@link VolumeImpl VolumeImpl enum}
*/
VolumeImpl getImplementationType();
/**
* @param fs
* @throws IOException
*/
void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws IOException, VolumeException, InvalidMountPointException;
/**
* Reveals the mounted volume.
* <p>
* The given {@code revealer} might be used to do it, but not necessarily.
*
* @param revealer An object capable of revealing the location of the mounted vault to view the content (e.g. in the default file browser).
* @throws VolumeException
*/
void reveal(Revealer revealer) throws VolumeException;
void unmount() throws VolumeException;
Optional<Path> getMountPoint();
MountPointRequirement getMountPointRequirement();
// optional forced unmounting:
default boolean supportsForcedUnmount() {
return false;
}
default void unmountForced() throws VolumeException {
throw new VolumeException("Operation not supported.");
}
static VolumeImpl[] getCurrentSupportedAdapters() {
return Stream.of(VolumeImpl.values()).filter(impl -> switch (impl) {
case WEBDAV -> WebDavVolume.isSupportedStatic();
case DOKANY -> DokanyVolume.isSupportedStatic();
case FUSE -> FuseVolume.isSupportedStatic();
}).toArray(VolumeImpl[]::new);
}
/**
* Exception thrown when a volume-specific command such as mount/unmount/reveal failed.
*/
class VolumeException extends Exception {
public VolumeException(String message) {
super(message);
}
public VolumeException(Throwable cause) {
super(cause);
}
public VolumeException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Hides and unifies the different Revealer implementations in the different nio-adapters.
*/
@FunctionalInterface
interface Revealer {
void reveal(Path p) throws VolumeException;
}
}
@@ -1,170 +0,0 @@
package org.cryptomator.common.vaults;
import com.google.common.base.CharMatcher;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.cryptomator.frontend.webdav.mount.MountParams;
import org.cryptomator.frontend.webdav.mount.Mounter;
import org.cryptomator.frontend.webdav.servlet.WebDavServletController;
import javax.inject.Inject;
import javax.inject.Provider;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class WebDavVolume implements Volume {
private static final String LOCALHOST_ALIAS = "cryptomator-vault";
private final Provider<WebDavServer> serverProvider;
private final VaultSettings vaultSettings;
private final Settings settings;
private final WindowsDriveLetters windowsDriveLetters;
private WebDavServer server;
private WebDavServletController servlet;
private Mounter.Mount mount;
private Consumer<Throwable> onExitAction;
@Inject
public WebDavVolume(Provider<WebDavServer> serverProvider, VaultSettings vaultSettings, Settings settings, WindowsDriveLetters windowsDriveLetters) {
this.serverProvider = serverProvider;
this.vaultSettings = vaultSettings;
this.settings = settings;
this.windowsDriveLetters = windowsDriveLetters;
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
startServlet(fs);
mountServlet();
this.onExitAction = onExitAction;
}
private void startServlet(CryptoFileSystem fs) {
if (server == null) {
server = serverProvider.get();
}
if (!server.isRunning()) {
server.start();
}
CharMatcher acceptable = CharMatcher.inRange('0', '9').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('a', 'z'));
String urlConformMountName = acceptable.negate().collapseFrom(vaultSettings.mountName().get(), '_');
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + urlConformMountName);
servlet.start();
}
private void mountServlet() throws VolumeException {
if (servlet == null) {
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
}
//on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specific one or there is no free.
Supplier<String> driveLetterSupplier;
if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
driveLetterSupplier = () -> windowsDriveLetters.getDesiredAvailableDriveLetter().orElse(null);
} else {
driveLetterSupplier = () -> vaultSettings.winDriveLetter().get();
}
MountParams mountParams = MountParams.create() //
.withWindowsDriveLetter(driveLetterSupplier.get()) //
.withPreferredGvfsScheme(settings.preferredGvfsScheme().get().getPrefix())//
.withWebdavHostname(getLocalhostAliasOrNull()) //
.build();
try {
this.mount = servlet.mount(mountParams); // might block this thread for a while
} catch (Mounter.CommandFailedException e) {
throw new VolumeException(e);
}
}
@Override
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@Override
public synchronized void unmount() throws VolumeException {
try {
mount.unmount();
} catch (Mounter.CommandFailedException e) {
throw new VolumeException(e);
}
cleanup();
onExitAction.accept(null);
}
@Override
public synchronized void unmountForced() throws VolumeException {
try {
mount.forced().orElseThrow(IllegalStateException::new).unmount();
} catch (Mounter.CommandFailedException e) {
throw new VolumeException(e);
}
cleanup();
onExitAction.accept(null);
}
@Override
public Optional<Path> getMountPoint() {
return mount.getMountPoint();
}
@Override
public MountPointRequirement getMountPointRequirement() {
return MountPointRequirement.NONE;
}
private String getLocalhostAliasOrNull() {
try {
InetAddress alias = InetAddress.getByName(LOCALHOST_ALIAS);
if (alias.getHostAddress().equals("127.0.0.1")) {
return LOCALHOST_ALIAS;
} else {
return null;
}
} catch (UnknownHostException e) {
return null;
}
}
private void cleanup() {
if (servlet != null) {
servlet.stop();
}
}
@Override
public boolean isSupported() {
return WebDavVolume.isSupportedStatic();
}
@Override
public VolumeImpl getImplementationType() {
return VolumeImpl.WEBDAV;
}
@Override
public boolean supportsForcedUnmount() {
return mount != null && mount.forced().isPresent();
}
public static boolean isSupportedStatic() {
return true;
}
}
@@ -1,85 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.vaults;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.SystemUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
@Singleton
public final class WindowsDriveLetters {
private static final Set<String> A_TO_Z;
static {
try (IntStream stream = IntStream.rangeClosed('A', 'Z')) {
A_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(ImmutableSet.toImmutableSet());
}
}
@Inject
public WindowsDriveLetters() {
}
public Set<String> getAllDriveLetters() {
return A_TO_Z;
}
public Set<String> getOccupiedDriveLetters() {
if (!SystemUtils.IS_OS_WINDOWS) {
return Set.of();
} else {
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
return StreamSupport.stream(rootDirs.spliterator(), false).map(p -> p.toString().substring(0, 1)).collect(Collectors.toSet());
}
}
public Set<String> getAvailableDriveLetters() {
return Sets.difference(getAllDriveLetters(), getOccupiedDriveLetters());
}
public Optional<String> getAvailableDriveLetter() {
return getAvailableDriveLetters().stream().findFirst();
}
public Optional<Path> getAvailableDriveLetterPath() {
return getAvailableDriveLetter().map(this::toPath);
}
/**
* Skips A and B and only returns them if all other are occupied.
*
* @return an Optional containing either the letter of a free drive letter or empty, if none is available
*/
public Optional<String> getDesiredAvailableDriveLetter() {
var availableDriveLetters = getAvailableDriveLetters();
var optString = availableDriveLetters.stream().filter(s -> !(s.equals("A") || s.equals("B"))).findFirst();
return optString.or(() -> availableDriveLetters.stream().findFirst());
}
/**
* Skips A and B and only returns them if all other are occupied.
*
* @return an Optional containing either the path to a free drive letter or empty, if none is available
*/
public Optional<Path> getDesiredAvailableDriveLetterPath() {
return getDesiredAvailableDriveLetter().map(this::toPath);
}
public Path toPath(String driveLetter) {
return Path.of(driveLetter + ":\\");
}
}
@@ -0,0 +1,13 @@
package org.cryptomator.launcher;
import java.nio.file.Path;
import java.util.Collection;
public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection<Path> pathsToOpen) {
public enum EventType {
REVEAL_APP,
OPEN_FILE
}
}
@@ -12,48 +12,58 @@ import org.cryptomator.common.Environment;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration;
import org.cryptomator.ui.launcher.UiLauncher;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@Singleton
public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis();
// DaggerCryptomatorComponent gets generated by Dagger.
// Run Maven and include target/generated-sources/annotations in your IDE.
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private final LoggerConfiguration logConfig;
private final DebugMode debugMode;
private final SupportedLanguages supportedLanguages;
private final Environment env;
private final Lazy<IpcMessageHandler> ipcMessageHandler;
private final CountDownLatch shutdownLatch;
private final ShutdownHook shutdownHook;
private final Lazy<UiLauncher> uiLauncher;
@Inject
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
this.logConfig = logConfig;
Cryptomator(DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
this.debugMode = debugMode;
this.supportedLanguages = supportedLanguages;
this.env = env;
this.ipcMessageHandler = ipcMessageHandler;
this.shutdownLatch = shutdownLatch;
this.shutdownHook = shutdownHook;
this.uiLauncher = uiLauncher;
}
public static void main(String[] args) {
var printVersion = Optional.ofNullable(args) //
.stream() //Streams either one element (the args-array) or zero elements
.flatMap(Arrays::stream) //
.anyMatch(arg -> "-v".equals(arg) || "--version".equals(arg));
if (printVersion) {
var appVer = System.getProperty("cryptomator.appVersion", "SNAPSHOT");
var buildNumber = System.getProperty("cryptomator.buildNumber", "SNAPSHOT");
//Reduce noise for parsers by using System.out directly
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
return;
}
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads.
@@ -66,9 +76,11 @@ public class Cryptomator {
* @return Nonzero exit code in case of an error.
*/
private int run(String[] args) {
logConfig.init();
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
env.log();
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
debugMode.initialize();
supportedLanguages.applyPreferred();
/*
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
@@ -79,7 +91,7 @@ public class Cryptomator {
communicator.sendHandleLaunchargs(List.of(args));
communicator.sendRevealRunningApp();
LOG.info("Found running application instance. Shutting down...");
return 2;
return 0;
} else {
shutdownHook.runOnShutdown(communicator::closeUnchecked);
var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build());
@@ -96,21 +108,38 @@ public class Cryptomator {
}
/**
* Launches the JavaFX application and waits until shutdown is requested.
* Launches the JavaFX application, blocking the main thread until shuts down.
*
* @return Nonzero exit code in case of an error.
* @implNote This method blocks until {@link #shutdownLatch} reached zero.
*/
private int runGuiApplication() {
try {
uiLauncher.get().launch();
shutdownLatch.await();
Application.launch(MainApp.class);
LOG.info("UI shut down");
return 0;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
LOG.error("Terminating due to error", e);
return 1;
}
}
public static class MainApp extends Application {
@Override
public void start(Stage primaryStage) {
LOG.info("JavaFX runtime started after {}ms", System.currentTimeMillis() - STARTUP_TIME);
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
.fxApplication(this) //
.primaryStage(primaryStage) //
.build();
component.application().start();
}
@Override
public void stop() {
LOG.info("JavaFX application stopped.");
}
}
}
@@ -1,16 +1,24 @@
package org.cryptomator.launcher;
import dagger.BindsInstance;
import dagger.Component;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.logging.LoggerModule;
import org.cryptomator.ui.launcher.UiLauncherModule;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
import javax.inject.Singleton;
@Singleton
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class})
@Component(modules = {CryptomatorModule.class, CommonsModule.class})
public interface CryptomatorComponent {
Cryptomator application();
FxApplicationComponent.Builder fxAppComponentBuilder();
@Component.Factory
interface Factory {
CryptomatorComponent create(@BindsInstance @Named("startupTime") long startupTime);
}
}
@@ -2,20 +2,50 @@ package org.cryptomator.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Module
@Module(subcomponents = {FxApplicationComponent.class})
class CryptomatorModule {
@Provides
@Singleton
@Named("shutdownLatch")
static CountDownLatch provideShutdownLatch() {
return new CountDownLatch(1);
static ResourceBundle provideLocalization() {
return ResourceBundle.getBundle("i18n.strings");
}
@Provides
@Singleton
@Named("launchEventQueue")
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
return new ArrayBlockingQueue<>(10);
}
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
return UiAppearanceProvider.get();
}
@Provides
@Singleton
static Optional<AutoStartProvider> provideAutostartProvider() {
return AutoStartProvider.get();
}
@Provides
@Singleton
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
return TrayIntegrationProvider.get();
}
}
@@ -6,7 +6,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
@Singleton
class FileOpenRequestHandler {
@@ -1,7 +1,6 @@
package org.cryptomator.launcher;
import org.cryptomator.ipc.IpcMessageListener;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -0,0 +1,39 @@
package org.cryptomator.launcher;
import org.cryptomator.common.settings.Settings;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Locale;
@Singleton
public class SupportedLanguages {
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fil", "fa", "fr", "gl", "he", //
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "si", "sk", "sr", "sr-Latn", "sv", "sw", //
"ta", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
@Nullable
private final String preferredLanguage;
@Inject
public SupportedLanguages(Settings settings) {
this.preferredLanguage = settings.languageProperty().get();
}
public void applyPreferred() {
if (preferredLanguage == null) {
LOG.debug("Using system locale");
return;
}
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
Locale.setDefault(preferredLocale);
}
}

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