Compare commits

..
Author SHA1 Message Date
JaniruTEC 0dca0d6f1c Fixed #2270 2022-06-30 02:28:42 +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 8c65bd19c2 Make FUSE the default volume impl 2022-06-13 10:00:14 +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
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
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
Tobias Hagemann 5233e6b690 improved cleanup in manual build script for macos
[ci skip]
2022-05-17 18:33:02 +02:00
JaniruTEC 0040c8a5f8 Added catching executors 2022-05-17 16:30:23 +02:00
Armin Schrenk 4989eacaf9 Fixes #2256 2022-05-16 12:58:36 +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 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
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
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 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
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
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 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
235 changed files with 5272 additions and 3027 deletions
+4 -2
View File
@@ -1,11 +1,13 @@
# Number of days of inactivity before an issue becomes stale # 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 # Number of days of inactivity before a stale issue is closed
daysUntilClose: 30 daysUntilClose: 90
# Issues with these labels will never be considered stale # Issues with these labels will never be considered stale
exemptLabels: exemptLabels:
- type:security-issue # never close automatically - type:security-issue # never close automatically
- type:feature-request # 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:awaiting-response # handled by different bot
- state:blocked - state:blocked
- state:confirmed - state:confirmed
+17 -7
View File
@@ -4,6 +4,10 @@ on:
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env: env:
JAVA_VERSION: 17 JAVA_VERSION: 17
@@ -28,6 +32,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/} SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR} 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 else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi fi
@@ -54,10 +61,16 @@ jobs:
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods" --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.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-header-files
--no-man-pages --no-man-pages
--strip-debug --strip-debug
--compress=1 --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 - name: Run jpackage
run: > run: >
${JAVA_HOME}/bin/jpackage ${JAVA_HOME}/bin/jpackage
@@ -83,12 +96,12 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=false" --java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources --resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir - name: Patch Cryptomator.AppDir
run: | run: |
mv appdir/Cryptomator Cryptomator.AppDir mv appdir/Cryptomator Cryptomator.AppDir
cp -r dist/linux/appimage/resources/AppDir/* 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.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.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 cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
@@ -100,9 +113,6 @@ jobs:
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun 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 - name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
run: | run: |
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'` JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
@@ -146,6 +156,6 @@ jobs:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: | files: |
*.AppImage cryptomator-*.AppImage
*.zsync cryptomator-*.zsync
*.asc cryptomator-*.asc
+12 -6
View File
@@ -32,6 +32,7 @@ jobs:
restore-keys: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar
- name: Build and Test - name: Build and Test
run: > run: >
xvfb-run
mvn -B verify mvn -B verify
jacoco:report jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
@@ -42,13 +43,15 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Upload code coverage report - name: Sign source tarball with key 615D449FE6E6A235
id: codacyCoverageReporter if: startsWith(github.ref, 'refs/tags/')
if: "github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'pr:safe')" run: |
run: bash <(curl -Ls https://coverage.codacy.com/get.sh) 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: env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
continue-on-error: true GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Draft a release - name: Draft a release
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
@@ -57,6 +60,9 @@ jobs:
discussion_category_name: releases discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true generate_release_notes: true
files: |
cryptomator-*.tar.gz.asc
fail_on_unmatched_files: true
body: |- body: |-
:construction: Work in Progress :construction: Work in Progress
+6
View File
@@ -10,6 +10,9 @@ on:
required: true required: true
default: false default: false
type: boolean type: boolean
version:
description: 'Version'
required: false
env: env:
JAVA_VERSION: 17 JAVA_VERSION: 17
@@ -38,6 +41,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/} SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR} 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 else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi fi
+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
+28 -32
View File
@@ -4,6 +4,10 @@ on:
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env: env:
JAVA_VERSION: 17 JAVA_VERSION: 17
@@ -28,6 +32,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/} SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR} 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 else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi fi
@@ -75,13 +82,15 @@ jobs:
--app-version "${{ steps.versions.outputs.semVerNum }}" --app-version "${{ steps.versions.outputs.semVerNum }}"
--java-options "-Xss5m" --java-options "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dapple.awt.enableTemplateImages=true" --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.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" --java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" --java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" --java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\""
--mac-package-identifier org.cryptomator --mac-package-identifier org.cryptomator
@@ -95,6 +104,16 @@ jobs:
env: env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }} VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
REVISION_NO: ${{ steps.versions.outputs.revNum }} 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 - name: Install codesign certificate
run: | run: |
# create variables # create variables
@@ -169,33 +188,14 @@ jobs:
Cryptomator-${VERSION_NO}.dmg dmg Cryptomator-${VERSION_NO}.dmg dmg
env: env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }} 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 - name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
run: | uses: cocoalibs/xcode-notarization-action@v1
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db with:
sudo xcode-select -s /Applications/Xcode_13.0.app app-path: 'Cryptomator-*.dmg'
xcrun notarytool submit Cryptomator-*.dmg --keychain-profile "${NOTARIZATION_KEYCHAIN_PROFILE}" --keychain "${KEYCHAIN_PATH}" --wait apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
xcrun stapler staple Cryptomator-*.dmg password: ${{ secrets.MACOS_NOTARIZATION_PW }}
env: team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
- name: Add possible alpha/beta tags to installer name - 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 }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235 - name: Create detached GPG signature with key 615D449FE6E6A235
@@ -209,10 +209,6 @@ jobs:
if: ${{ always() }} if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true 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 - name: Upload artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
@@ -226,7 +222,7 @@ jobs:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: | files: |
*.dmg Cryptomator-*.dmg
*.asc Cryptomator-*.asc
+1 -1
View File
@@ -23,4 +23,4 @@ jobs:
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Build and Test - 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
+98 -7
View File
@@ -4,6 +4,10 @@ on:
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
inputs:
version:
description: 'Version'
required: false
env: env:
JAVA_VERSION: 17 JAVA_VERSION: 17
@@ -33,6 +37,9 @@ jobs:
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/} SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR} 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 else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi fi
@@ -80,6 +87,7 @@ jobs:
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}" --app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
--java-options "-Xss5m" --java-options "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\"" --java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\"" --java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
@@ -89,6 +97,7 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\"" --java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--resource-dir dist/win/resources --resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico --icon dist/win/resources/Cryptomator.ico
- name: Patch Application Directory - name: Patch Application Directory
@@ -107,12 +116,17 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com' timestampUrl: 'http://timestamp.digicert.com'
folder: appdir/Cryptomator folder: appdir/Cryptomator
recursive: true recursive: true
- name: Generate license - name: Generate license for MSI
run: > run: >
mvn -B license:add-third-party mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/resources" "-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 - name: Create MSI
run: > run: >
${JAVA_HOME}/bin/jpackage ${JAVA_HOME}/bin/jpackage
@@ -175,6 +189,19 @@ jobs:
semVerStr: ${{ steps.versions.outputs.semVerStr }} semVerStr: ${{ steps.versions.outputs.semVerStr }}
revNum: ${{ steps.versions.outputs.revNum }} revNum: ${{ steps.versions.outputs.revNum }}
publish-winget:
name: Publish on winget repo
runs-on: windows-latest
needs: [build-msi]
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
$installerUrl = $github.release.assets | Where-Object -Property name -match 'Cryptomator-*.msi' | Select -ExpandProperty browser_download_url -First 1
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
shell: pwsh
build-exe: build-exe:
name: Build .exe installer name: Build .exe installer
runs-on: windows-latest runs-on: windows-latest
@@ -193,12 +220,17 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Generate license - name: Generate license for exe
run: > run: >
mvn -B license:add-third-party mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl" "-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources" "-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 - name: Download WinFsp
run: run:
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }} curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
@@ -217,7 +249,26 @@ jobs:
run: > run: >
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj "${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
-ext WixBalExtension -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: FF52240075AD7D14AF25629FDF69635357C7D14B
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 - name: Codesign EXE
uses: skymatic/code-sign-action@v1 uses: skymatic/code-sign-action@v1
with: with:
@@ -228,7 +279,7 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com' timestampUrl: 'http://timestamp.digicert.com'
folder: installer folder: installer
- name: Add possible alpha/beta tags to installer name - 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 - name: Create detached GPG signature with key 615D449FE6E6A235
run: | run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -251,5 +302,45 @@ jobs:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: | files: |
*.exe Cryptomator-*.exe
*.asc 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/
+1 -18
View File
@@ -5,26 +5,9 @@
*.war *.war
*.ear *.ear
# Eclipse Settings Files #
.settings
.project
.classpath
# Maven # # Maven #
target/ target/
pom.xml.versionsBackup pom.xml.versionsBackup
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) # # Java Crash Logs
.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
hs_err_pid*.log 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.41/dagger-compiler-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.41/dagger-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.41/dagger-producers-2.41.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.41/dagger-spi-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.5.30-1.0.0/symbol-processing-api-1.5.30-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.32/kotlin-stdlib-jdk8-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.32/kotlin-stdlib-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.32/kotlin-stdlib-common-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.32/kotlin-stdlib-jdk7-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.3.0/kotlinx-metadata-jvm-0.3.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" />
</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
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.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" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.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" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs> </envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.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" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs> </envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-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.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" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -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) [![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
[![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator) [![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator)
[![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) [![Twitter](https://img.shields.io/badge/twitter-@Cryptomator-blue.svg?style=flat)](http://twitter.com/Cryptomator)
[![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/) [![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/)
[![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest) [![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest)
@@ -36,8 +36,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
</tbody> </tbody>
</table> </table>
- [Jameson Lopp](https://www.lopp.net/)
--- ---
## Introduction ## Introduction
@@ -82,7 +80,6 @@ For more information on the security details visit [cryptomator.org](https://doc
* JDK 17 (e.g. temurin) * JDK 17 (e.g. temurin)
* Maven 3 * 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 ### Run Maven
+3
View File
@@ -20,12 +20,14 @@ ${JAVA_HOME}/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods" \ --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.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-header-files \
--no-man-pages \ --no-man-pages \
--strip-debug \ --strip-debug \
--compress=1 --compress=1
# create app dir # create app dir
envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
${JAVA_HOME}/bin/jpackage \ ${JAVA_HOME}/bin/jpackage \
--verbose \ --verbose \
--type app-image \ --type app-image \
@@ -48,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \ --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \ --java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources --resource-dir ../resources
# transform AppDir # 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 $?` GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
fi 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 # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
export LD_PRELOAD=lib/app/libjffi.so export LD_PRELOAD=lib/app/libjffi.so
# start Cryptomator if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
./lib/runtime/bin/java \ bin/Cryptomator-gtk2
-p "lib/app/mods" \ else
-cp "lib/app/*" \ bin/Cryptomator
-Dfile.encoding="utf-8" \ fi
-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
+1 -1
View File
@@ -7,5 +7,5 @@ Terminal=false
Type=Application Type=Application
Categories=Utility;Security;FileTools; Categories=Utility;Security;FileTools;
StartupNotify=true StartupNotify=true
StartupWMClass=org.cryptomator.launcher.Cryptomator StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault; MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
@@ -66,6 +66,9 @@
</content_rating> </content_rating>
<releases> <releases>
<release date="2022-05-03" version="1.6.10"/>
<release date="2022-04-27" version="1.6.9"/>
<release date="2022-03-30" version="1.6.8"/>
<release date="2021-12-16" version="1.6.5"/> <release date="2021-12-16" version="1.6.5"/>
</releases> </releases>
</component> </component>
+1
View File
@@ -19,6 +19,7 @@ override_dh_auto_build:
jlink \ jlink \
--output runtime \ --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.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-header-files \
--no-man-pages \ --no-man-pages \
--strip-debug \ --strip-debug \
+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 # created during build
Cryptomator.app/
runtime/ runtime/
dmg/ dmg/
*.dmg *.dmg
license.rtf
+49 -28
View File
@@ -14,9 +14,17 @@ while getopts ":s:" o; do
done done
shift "$((OPTIND-1))" shift "$((OPTIND-1))"
# prepare working dir and variables # prepare working dir
cd $(dirname $0) 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` REVISION_NO=`git rev-list --count HEAD`
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
@@ -31,13 +39,14 @@ fi
# compile # compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../target/cryptomator-*.jar ../../../target/mods cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime # add runtime
${JAVA_HOME}/bin/jlink \ ${JAVA_HOME}/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods" \ --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.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-header-files \
--no-man-pages \ --no-man-pages \
--strip-debug \ --strip-debug \
@@ -50,35 +59,47 @@ ${JAVA_HOME}/bin/jpackage \
--runtime-image runtime \ --runtime-image runtime \
--input ../../../target/libs \ --input ../../../target/libs \
--module-path ../../../target/mods \ --module-path ../../../target/mods \
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \ --module ${MODULE_AND_MAIN_CLASS} \
--dest . \ --dest . \
--name Cryptomator \ --name ${APP_NAME} \
--vendor "Skymatic GmbH" \ --vendor "${VENDOR}" \
--copyright "(C) 2016 - 2022 Skymatic GmbH" \ --copyright "(C) ${COPYRIGHT_YEARS} ${VENDOR}" \
--app-version "${VERSION_NO}" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--app-version "${VERSION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \ --java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Dapple.awt.enableTemplateImages=true" \ --java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\"" \ --java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" \ --java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" \ --java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" \ --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.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \ --java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier org.cryptomator \ --mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources --resource-dir ../resources
# transform app dir # transform app dir
cp ../resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/ cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.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 # codesign
if [ -n "${CODESIGN_IDENTITY}" ]; then if [ -n "${CODESIGN_IDENTITY}" ]; then
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \; find ${APP_NAME}.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do for JAR_PATH in `find ${APP_NAME}.app -name "*.jar"`; do
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
JAR_FILENAME=$(basename ${JAR_PATH}) JAR_FILENAME=$(basename ${JAR_PATH})
OUTPUT_PATH=${JAR_PATH%.*} OUTPUT_PATH=${JAR_PATH%.*}
@@ -93,25 +114,25 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
rm -r ${OUTPUT_PATH} rm -r ${OUTPUT_PATH}
fi fi
done done
echo "Codesigning Cryptomator.app..." echo "Codesigning ${APP_NAME}.app..."
codesign --force --deep --entitlements ../Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app codesign --force --deep --entitlements ../${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
fi fi
# prepare dmg contents # prepare dmg contents
mkdir dmg mkdir dmg
mv Cryptomator.app dmg mv ${APP_NAME}.app dmg
cp resources/macFUSE.webloc dmg cp resources/macFUSE.webloc dmg
# create dmg # create dmg
create-dmg \ create-dmg \
--volname Cryptomator \ --volname ${APP_NAME} \
--volicon "resources/Cryptomator-Volume.icns" \ --volicon "resources/${APP_NAME}-Volume.icns" \
--background "resources/Cryptomator-background.tiff" \ --background "resources/${APP_NAME}-background.tiff" \
--window-pos 400 100 \ --window-pos 400 100 \
--window-size 640 694 \ --window-size 640 694 \
--icon-size 128 \ --icon-size 128 \
--icon "Cryptomator.app" 128 245 \ --icon "${APP_NAME}.app" 128 245 \
--hide-extension "Cryptomator.app" \ --hide-extension "${APP_NAME}.app" \
--icon "macFUSE.webloc" 320 501 \ --icon "macFUSE.webloc" 320 501 \
--hide-extension "macFUSE.webloc" \ --hide-extension "macFUSE.webloc" \
--app-drop-link 512 245 \ --app-drop-link 512 245 \
@@ -119,4 +140,4 @@ create-dmg \
--icon ".background" 128 758 \ --icon ".background" 128 758 \
--icon ".fseventsd" 320 758 \ --icon ".fseventsd" 320 758 \
--icon ".VolumeIcon.icns" 512 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 @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
+52 -37
View File
@@ -1,5 +1,15 @@
# check parameters Param(
$clean = $args[0] -eq "fresh" [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 # check preconditions
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null) if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
@@ -24,12 +34,11 @@ Write-Output "`$revisionNo=$revisionNo"
Write-Output "`$buildDir=$buildDir" Write-Output "`$buildDir=$buildDir"
Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME" Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$vendor = "Skymatic GmbH" $copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
$copyright = "(C) 2016 - 2022 Skymatic GmbH"
# compile # compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin &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 # add runtime
$runtimeImagePath = '.\runtime' $runtimeImagePath = '.\runtime'
@@ -42,12 +51,13 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
--output runtime ` --output runtime `
--module-path "$Env:JAVA_HOME/jmods" ` --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.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-header-files `
--no-man-pages ` --no-man-pages `
--strip-debug ` --strip-debug `
--compress=1 --compress=1
$appPath = '.\Cryptomator' $appPath = ".\$AppName"
if ($clean -and (Test-Path -Path $appPath)) { if ($clean -and (Test-Path -Path $appPath)) {
Remove-Item -Path $appPath -Force -Recurse Remove-Item -Path $appPath -Force -Recurse
} }
@@ -59,68 +69,73 @@ if ($clean -and (Test-Path -Path $appPath)) {
--runtime-image runtime ` --runtime-image runtime `
--input ../../target/libs ` --input ../../target/libs `
--module-path ../../target/mods ` --module-path ../../target/mods `
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator ` --module $ModuleAndMainClass `
--dest . ` --dest . `
--name Cryptomator ` --name $AppName `
--vendor $vendor ` --vendor $Vendor `
--copyright $copyright ` --copyright $copyright `
--java-options "-Xss5m" ` --java-options "-Xss5m" `
--java-options "-Xmx256m" ` --java-options "-Xmx256m" `
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" ` --java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" ` --app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" ` --java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/Cryptomator`"" ` --java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/Cryptomator/Plugins`"" ` --java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/Cryptomator/settings.json`"" ` --java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/Cryptomator/ipc.socket`"" ` --java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/Cryptomator/keychain.json`"" ` --java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/Cryptomator`"" ` --java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.showTrayIcon=true" ` --java-options "-Dcryptomator.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" ` --java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources ` --resource-dir resources `
--icon resources/Cryptomator.ico --icon resources/$AppName.ico
#Create RTF license for msi #Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` &mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" ` "-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" ` "-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 # patch app dir
Copy-Item "contrib\*" -Destination "Cryptomator" Copy-Item "contrib\*" -Destination "$AppName"
attrib -r "Cryptomator\Cryptomator.exe" attrib -r "$AppName\$AppName.exe"
$aboutUrl="https://cryptomator.org"
$updateUrl="https://cryptomator.org/downloads/"
$helpUrl="https://cryptomator.org/contact/"
# create .msi # create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources" $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
& "$Env:JAVA_HOME\bin\jpackage" ` & "$Env:JAVA_HOME\bin\jpackage" `
--verbose ` --verbose `
--type msi ` --type msi `
--win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 ` --win-upgrade-uuid $UpgradeUUID `
--app-image Cryptomator ` --app-image $AppName `
--dest installer ` --dest installer `
--name Cryptomator ` --name $AppName `
--vendor $vendor ` --vendor $Vendor `
--copyright $copyright ` --copyright $copyright `
--app-version "$semVerNo" ` --app-version "$semVerNo" `
--win-menu ` --win-menu `
--win-dir-chooser ` --win-dir-chooser `
--win-shortcut-prompt ` --win-shortcut-prompt `
--win-update-url $updateUrl ` --win-menu-group $AppName `
--win-menu-group Cryptomator `
--resource-dir resources ` --resource-dir resources `
--about-url $aboutUrl `
--license-file resources/license.rtf ` --license-file resources/license.rtf `
--win-update-url $UpdateUrl `
--about-url $AboutUrl `
--file-associations resources/FAvaultFile.properties --file-associations resources/FAvaultFile.properties
#Create RTF license for bundle #Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` &mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" ` "-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" ` "-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 # download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
@@ -130,14 +145,14 @@ Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
# copy MSI to bundle resources # 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 # create bundle including winfsp
& "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ ` & "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ `
-dBundleVersion="$semVerNo.$revisionNo" ` -dBundleVersion="$semVerNo.$revisionNo" `
-dBundleVendor="$vendor" ` -dBundleVendor="$Vendor" `
-dBundleCopyright="$copyright" ` -dBundleCopyright="$copyright" `
-dAboutUrl="$aboutUrl" ` -dAboutUrl="$AboutUrl" `
-dHelpUrl="$helpUrl" ` -dHelpUrl="$HelpUrl" `
-dUpdateUrl="$updateUrl" -dUpdateUrl="$UpdateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\CryptomatorBundle.exe & "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\$AppName-Installer.exe
+6 -6
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 ) "> <#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> </#if>
</#function> </#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;} {\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1 \vieww12000\viewh15840\viewkind0
\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 \pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par \par
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par \b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
\par \par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par \par
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 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 \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 \par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\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> <#if projects?size &gt; 0>
\tab ${license}:\par \tab ${license}:\par
<#list projects as project> <#list projects as project>
\tab\tab- ${artifactFormat(project)}\par \tab\tab - ${artifactFormat(project)}\par
</#list> </#list>
</#if> </#if>
</#list> </#list>
+5 -1
View File
@@ -1,3 +1,7 @@
@echo off @echo off
:: Default values for Cryptomator builds
SET LOOPBACK_ALIAS="cryptomator-vault"
cd %~dp0 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 #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 { function Add-AliasToHost {
param (
[string]$LoopbackAlias
)
$sysdir = [Environment]::SystemDirectory $sysdir = [Environment]::SystemDirectory
$hostsFile = "$sysdir\drivers\etc\hosts" $hostsFile = "$sysdir\drivers\etc\hosts"
$aliasLine = '127.0.0.1 cryptomator-vault' $aliasLine = "127.0.0.1 $LoopbackAlias"
foreach ($line in Get-Content $hostsFile) { foreach ($line in Get-Content $hostsFile) {
if ($line -eq $aliasLine){ if ($line -eq $aliasLine){
@@ -49,7 +55,7 @@ function Edit-ProviderOrder {
} }
Add-AliasToHost Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file' Write-Output 'Ensured alias exists in hosts file'
Set-WebDAVFileSizeLimit Set-WebDAVFileSizeLimit
+25 -2
View File
@@ -13,7 +13,6 @@
<DialogRef Id="BrowseDlg" /> <DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" /> <DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" /> <DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" /> <DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" /> <DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" /> <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="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> <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="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="LicenseAgreementDlg">NOT Installed</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish> <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
@@ -75,11 +75,34 @@
</Control> </Control>
</Dialog> </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> <InstallUISequence>
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/> <Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
</InstallUISequence> </InstallUISequence>
<AdminUISequence> <AdminUISequence>
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/> <Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
</AdminUISequence> </AdminUISequence>
</UI> </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 ) "> <#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> </#if>
</#function> </#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;} {\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1 \vieww12000\viewh15840\viewkind0
\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 \pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par \par
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par \b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
\par \par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par \par
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 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 \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 \par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\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> <#if projects?size &gt; 0>
\tab ${license}:\par \tab ${license}:\par
<#list projects as project> <#list projects as project>
\tab\tab- ${artifactFormat(project)}\par \tab\tab - ${artifactFormat(project)}\par
</#list> </#list>
</#if> </#if>
</#list> </#list>
\par \par
\b Cryptomator uses other third-party assets under the following licenses:\b0\par \b Cryptomator uses other third-party assets under the following licenses:\b0\par
\tab SIL OFL 1.1 License:\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 --> <!-- Non-Opening ProgID -->
<DirectoryRef Id="INSTALLDIR"> <DirectoryRef Id="INSTALLDIR">
<Component Win64="yes" Id="nonStartingProgID" > <Component Win64="yes" Id="nonStartingProgID" >
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\Cryptomator-Vault.ico" Name="Cryptomator-Vault.ico"></File> <File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileC9rC9s)" Name="$(var.IconFileC9rC9s)"></File>
<ProgId Id="Cryptomator.Encrypted.1" Description="Cryptomator Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0"> <ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"> <Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
<MIME ContentType="application/vnd.cryptomator.encrypted" Default="yes"></MIME> <MIME ContentType="$(var.ProgIdContentType)" Default="yes"></MIME>
</Extension> </Extension>
<Extension Id="c9s" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"/> <Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
</ProgId> </ProgId>
</Component> </Component>
</DirectoryRef> </DirectoryRef>
@@ -127,6 +127,20 @@
<!-- WebDAV patches --> <!-- WebDAV patches -->
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" /> <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 ?> <?ifdef JpIcon ?>
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/> <Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/> <Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
@@ -155,7 +169,12 @@
<?ifndef JpAllowDowngrades ?> <?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom> <Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?> <?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> <Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence> </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
+71 -27
View File
@@ -27,34 +27,35 @@
<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> <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>
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.3.1</cryptomator.cryptofs.version> <cryptomator.cryptofs.version>2.4.2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.1.0-beta1</cryptomator.integrations.version> <cryptomator.integrations.version>1.1.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version> <cryptomator.integrations.win.version>1.1.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.1.1</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.1.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version> <cryptomator.fuse.version>1.3.4</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version> <cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.6</cryptomator.webdav.version> <cryptomator.webdav.version>1.2.7</cryptomator.webdav.version>
<!-- 3rd party dependencies --> <!-- 3rd party dependencies -->
<javafx.version>17.0.2</javafx.version> <javafx.version>18.0.1</javafx.version>
<commons-lang3.version>3.12.0</commons-lang3.version> <commons-lang3.version>3.12.0</commons-lang3.version>
<jwt.version>3.18.3</jwt.version> <jwt.version>3.19.1</jwt.version>
<easybind.version>2.2</easybind.version> <easybind.version>2.2</easybind.version>
<guava.version>31.0-jre</guava.version> <guava.version>31.1-jre</guava.version>
<dagger.version>2.40.3</dagger.version> <dagger.version>2.41</dagger.version>
<gson.version>2.8.9</gson.version> <gson.version>2.9.0</gson.version>
<zxcvbn.version>1.5.2</zxcvbn.version> <zxcvbn.version>1.7.0</zxcvbn.version>
<slf4j.version>1.7.32</slf4j.version> <slf4j.version>1.7.36</slf4j.version>
<logback.version>1.2.9</logback.version> <logback.version>1.2.11</logback.version>
<!-- test dependencies --> <!-- test dependencies -->
<junit.jupiter.version>5.8.1</junit.jupiter.version> <junit.jupiter.version>5.8.1</junit.jupiter.version>
<mockito.version>3.12.4</mockito.version> <mockito.version>4.4.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version> <hamcrest.version>2.2</hamcrest.version>
<!-- build plugin dependencies --> <!-- build-time dependencies -->
<dependency-check.version>7.0.0</dependency-check.version> <jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>7.1.0</dependency-check.version>
<jacoco.version>0.8.7</jacoco.version> <jacoco.version>0.8.7</jacoco.version>
</properties> </properties>
@@ -224,6 +225,13 @@
<version>1.2</version> <version>1.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>${jetbrains.annotations.version}</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -290,6 +298,41 @@
</compilerArgs> </compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.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> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -347,23 +390,24 @@
</goals> </goals>
<phase>generate-resources</phase> <phase>generate-resources</phase>
<configuration> <configuration>
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename> <thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
<includedScopes>compile</includedScopes> <includedScopes>compile</includedScopes>
<excludedGroups>org\.cryptomator</excludedGroups> <excludedGroups>org\.cryptomator</excludedGroups>
<licenseMerges> <licenseMergesUrl>file:///${project.basedir}/license/merges</licenseMergesUrl>
<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> <fileTemplate>${project.basedir}/src/main/resources/license/template.ftl</fileTemplate>
<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>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>license/*</exclude>
</excludes>
</resource>
</resources>
</build> </build>
<profiles> <profiles>
-1
View File
@@ -1 +0,0 @@
com.github.serceman--jnr-fuse--0.5.4=MIT License
+8 -8
View File
@@ -1,9 +1,9 @@
import org.cryptomator.integrations.autostart.AutoStartProvider; import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.ui.traymenu.AwtTrayMenuController;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
module org.cryptomator.desktop { module org.cryptomator.desktop {
requires static org.jetbrains.annotations;
requires org.cryptomator.cryptofs; requires org.cryptomator.cryptofs;
requires org.cryptomator.frontend.dokany; requires org.cryptomator.frontend.dokany;
requires org.cryptomator.frontend.fuse; requires org.cryptomator.frontend.fuse;
@@ -29,13 +29,13 @@ module org.cryptomator.desktop {
requires logback.classic; requires logback.classic;
requires logback.core; requires logback.core;
uses AutoStartProvider; exports org.cryptomator.ui.traymenu to org.cryptomator.integrations.api;
uses KeychainAccessProvider; provides TrayMenuController with AwtTrayMenuController;
uses TrayIntegrationProvider;
uses UiAppearanceProvider;
opens org.cryptomator.common.settings to com.google.gson; opens org.cryptomator.common.settings to com.google.gson;
opens org.cryptomator.launcher to javafx.graphics;
opens org.cryptomator.common to javafx.fxml; opens org.cryptomator.common to javafx.fxml;
opens org.cryptomator.common.vaults to javafx.fxml; opens org.cryptomator.common.vaults to javafx.fxml;
opens org.cryptomator.ui.addvaultwizard to javafx.fxml; opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
@@ -0,0 +1,92 @@
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.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
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 && f.isDone()) {
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) {
assert future.isDone();
try {
future.get();
} catch (CancellationException ce) {
callHandler(Thread.currentThread(), ce);
} catch (ExecutionException ee) {
callHandler(Thread.currentThread(), ee.getCause());
} catch (InterruptedException ie) {
//Ignore/Reset
Thread.currentThread().interrupt();
}
}
}
@@ -12,9 +12,7 @@ import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.keychain.KeychainModule; import org.cryptomator.common.keychain.KeychainModule;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.SettingsProvider; import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent; import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultListModule; import org.cryptomator.common.vaults.VaultListModule;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer; import org.cryptomator.frontend.webdav.WebDavServer;
@@ -25,16 +23,13 @@ import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import javafx.beans.binding.Binding; import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Comparator; import java.util.Comparator;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue; import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -93,7 +88,7 @@ public abstract class CommonsModule {
@Singleton @Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) { static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1); 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()); String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
Thread t = new Thread(r); Thread t = new Thread(r);
t.setName(name); t.setName(name);
@@ -110,7 +105,7 @@ public abstract class CommonsModule {
@Singleton @Singleton
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) { static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1); 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()); String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
Thread t = new Thread(r); Thread t = new Thread(r);
t.setName(name); t.setName(name);
@@ -6,6 +6,7 @@ public interface Constants {
String MASTERKEY_BACKUP_SUFFIX = ".bkup"; String MASTERKEY_BACKUP_SUFFIX = ".bkup";
String VAULTCONFIG_FILENAME = "vault.cryptomator"; String VAULTCONFIG_FILENAME = "vault.cryptomator";
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator"; String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
byte[] PEPPER = new byte[0]; byte[] PEPPER = new byte[0];
} }
@@ -42,7 +42,6 @@ public class Environment {
LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion")); LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber")); LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon")); LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
} }
public boolean useCustomLogbackConfig() { public boolean useCustomLogbackConfig() {
@@ -89,11 +88,6 @@ public class Environment {
return Boolean.getBoolean("cryptomator.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");
}
private int getInt(String propertyName, int defaultValue) { private int getInt(String propertyName, int defaultValue) {
String value = System.getProperty(propertyName); String value = System.getProperty(propertyName);
try { try {
@@ -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(); return getClass().getName();
} }
@Override
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
@Override @Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, displayName, passphrase); getKeychainOrFail().storePassphrase(key, displayName, passphrase);
@@ -68,14 +62,6 @@ public class KeychainManager implements KeychainAccessProvider {
setPassphraseStored(key, false); setPassphraseStored(key, false);
} }
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) {
getKeychainOrFail().changePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
}
@Override @Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) { if (isPassphraseStored(key)) {
@@ -2,42 +2,30 @@ package org.cryptomator.common.keychain;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import javax.inject.Singleton; import javax.inject.Singleton;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectExpression; import javafx.beans.binding.ObjectExpression;
import java.util.ServiceLoader; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Module @Module
public class KeychainModule { public class KeychainModule {
@Provides @Provides
@Singleton @Singleton
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) { static List<KeychainAccessProvider> provideSupportedKeychainAccessProviders() {
return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet()); return KeychainAccessProvider.get().toList();
} }
@Provides @Provides
@Singleton @Singleton
static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) { static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
return availableFactories.stream() //
.map(ServiceLoader.Provider::get) //
.filter(KeychainAccessProvider::isSupported) //
.collect(Collectors.toUnmodifiableSet());
}
@Provides
@Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> { return Bindings.createObjectBinding(() -> {
var selectedProviderClass = settings.keychainProvider().get(); var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny(); 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); return selectedProvider.orElse(fallbackProvider);
}, settings.keychainProvider()); }, settings.keychainProvider());
} }
@@ -19,6 +19,7 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException; import java.nio.file.NotDirectoryException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Optional; import java.util.Optional;
class CustomMountPointChooser implements MountPointChooser { class CustomMountPointChooser implements MountPointChooser {
@@ -29,17 +30,15 @@ class CustomMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class); private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
private final VaultSettings vaultSettings; private final VaultSettings vaultSettings;
private final Environment environment;
@Inject @Inject
public CustomMountPointChooser(VaultSettings vaultSettings, Environment environment) { public CustomMountPointChooser(VaultSettings vaultSettings) {
this.vaultSettings = vaultSettings; this.vaultSettings = vaultSettings;
this.environment = environment;
} }
@Override @Override
public boolean isApplicable(Volume caller) { public boolean isApplicable(Volume caller) {
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse(); return caller.getImplementationType() != VolumeImpl.WEBDAV;
} }
@Override @Override
@@ -36,7 +36,7 @@ public class Settings {
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV; public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
public static final boolean DEFAULT_DEBUG_MODE = false; 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 VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = VolumeImpl.FUSE;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT; public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually @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"; 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,6 +44,7 @@ public class Settings {
public static final String DEFAULT_LICENSE_KEY = ""; public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false; public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
public static final String DEFAULT_DISPLAY_CONFIGURATION = ""; 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 ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
@@ -66,6 +67,7 @@ public class Settings {
private final IntegerProperty windowWidth = new SimpleIntegerProperty(); private final IntegerProperty windowWidth = new SimpleIntegerProperty();
private final IntegerProperty windowHeight = new SimpleIntegerProperty(); private final IntegerProperty windowHeight = new SimpleIntegerProperty();
private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION); private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
private Consumer<Settings> saveCmd; private Consumer<Settings> saveCmd;
@@ -96,6 +98,7 @@ public class Settings {
windowWidth.addListener(this::somethingChanged); windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged); windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged); displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
} }
void setSaveCmd(Consumer<Settings> saveCmd) { void setSaveCmd(Consumer<Settings> saveCmd) {
@@ -191,4 +194,8 @@ public class Settings {
public ObjectProperty<String> displayConfigurationProperty() { public ObjectProperty<String> displayConfigurationProperty() {
return displayConfiguration; return displayConfiguration;
} }
public StringProperty languageProperty() {
return language;
}
} }
@@ -57,6 +57,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("windowWidth").value((value.windowWidthProperty().get())); out.name("windowWidth").value((value.windowWidthProperty().get()));
out.name("windowHeight").value((value.windowHeightProperty().get())); out.name("windowHeight").value((value.windowHeightProperty().get()));
out.name("displayConfiguration").value((value.displayConfigurationProperty().get())); out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
out.name("language").value((value.languageProperty().get()));
out.endObject(); out.endObject();
} }
@@ -97,6 +98,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt()); case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt()); case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString()); case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
case "language" -> settings.languageProperty().set(in.nextString());
default -> { default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name); LOG.warn("Unsupported vault setting found in JSON: " + name);
@@ -3,9 +3,9 @@ package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
public enum UiTheme { public enum UiTheme {
LIGHT("preferences.general.theme.light"), // LIGHT("preferences.interface.theme.light"), //
DARK("preferences.general.theme.dark"), // DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.general.theme.automatic"); AUTOMATIC("preferences.interface.theme.automatic");
public static UiTheme[] applicableValues() { public static UiTheme[] applicableValues() {
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) { if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier @Qualifier
@Documented @Documented
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface DefaultMountFlags { @interface DefaultMountFlags {
} }
@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume.VolumeException; import org.cryptomator.common.vaults.Volume.VolumeException;
@@ -125,6 +126,7 @@ public class Vault {
.withKeyLoader(keyLoader) // .withKeyLoader(keyLoader) //
.withFlags(flags) // .withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.build(); .build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
} }
@@ -19,22 +19,13 @@ public interface VaultComponent {
Vault vault(); Vault vault();
@Subcomponent.Builder @Subcomponent.Factory
interface Builder { interface Factory {
@BindsInstance VaultComponent create(@BindsInstance VaultSettings vaultSettings, //
Builder vaultSettings(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 static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
private final AutoLocker autoLocker; private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder; private final VaultComponent.Factory vaultComponentFactory;
private final ObservableList<Vault> vaultList; private final ObservableList<Vault> vaultList;
private final String defaultVaultName; private final String defaultVaultName;
@Inject @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.vaultList = vaultList;
this.autoLocker = autoLocker; this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder; this.vaultComponentFactory = vaultComponentFactory;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName"); this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
addAll(settings.getDirectories()); addAll(settings.getDirectories());
@@ -93,21 +93,17 @@ public class VaultListManager {
} }
private Vault create(VaultSettings vaultSettings) { private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings); var wrapper = new VaultConfigCache(vaultSettings);
try { try {
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get()); var 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
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig(); wrapper.reloadConfig();
} }
compBuilder.initialVaultState(vaultState); return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e); LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
compBuilder.initialVaultState(ERROR); return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
compBuilder.initialErrorCause(e);
} }
return compBuilder.build().vault();
} }
public static VaultState.Value redetermineVaultState(Vault vault) { public static VaultState.Value redetermineVaultState(Vault vault) {
@@ -83,7 +83,7 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
if (success) { if (success) {
fireValueChangedEvent(); fireValueChangedEvent();
} else { } 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; return success;
} }
@@ -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
}
}
@@ -13,47 +13,60 @@ import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator; import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode; import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration; import org.cryptomator.logging.LoggerConfiguration;
import org.cryptomator.ui.launcher.UiLauncher; import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; 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.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@Singleton @Singleton
public class Cryptomator { public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis();
// DaggerCryptomatorComponent gets generated by Dagger. // DaggerCryptomatorComponent gets generated by Dagger.
// Run Maven and include target/generated-sources/annotations in your IDE. // 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 static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private final LoggerConfiguration logConfig; private final LoggerConfiguration logConfig;
private final DebugMode debugMode; private final DebugMode debugMode;
private final SupportedLanguages supportedLanguages;
private final Environment env; private final Environment env;
private final Lazy<IpcMessageHandler> ipcMessageHandler; private final Lazy<IpcMessageHandler> ipcMessageHandler;
private final CountDownLatch shutdownLatch;
private final ShutdownHook shutdownHook; private final ShutdownHook shutdownHook;
private final Lazy<UiLauncher> uiLauncher;
@Inject @Inject
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) { Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
this.logConfig = logConfig; this.logConfig = logConfig;
this.debugMode = debugMode; this.debugMode = debugMode;
this.supportedLanguages = supportedLanguages;
this.env = env; this.env = env;
this.ipcMessageHandler = ipcMessageHandler; this.ipcMessageHandler = ipcMessageHandler;
this.shutdownLatch = shutdownLatch;
this.shutdownHook = shutdownHook; this.shutdownHook = shutdownHook;
this.uiLauncher = uiLauncher;
} }
public static void main(String[] args) { 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); int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
LOG.info("Exit {}", exitCode); LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads. System.exit(exitCode); // end remaining non-daemon threads.
@@ -67,8 +80,10 @@ public class Cryptomator {
*/ */
private int run(String[] args) { private int run(String[] args) {
logConfig.init(); logConfig.init();
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
debugMode.initialize(); debugMode.initialize();
supportedLanguages.applyPreferred();
/* /*
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args. * Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
@@ -79,7 +94,7 @@ public class Cryptomator {
communicator.sendHandleLaunchargs(List.of(args)); communicator.sendHandleLaunchargs(List.of(args));
communicator.sendRevealRunningApp(); communicator.sendRevealRunningApp();
LOG.info("Found running application instance. Shutting down..."); LOG.info("Found running application instance. Shutting down...");
return 2; return 0;
} else { } else {
shutdownHook.runOnShutdown(communicator::closeUnchecked); shutdownHook.runOnShutdown(communicator::closeUnchecked);
var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build()); var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build());
@@ -96,21 +111,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. * @return Nonzero exit code in case of an error.
* @implNote This method blocks until {@link #shutdownLatch} reached zero.
*/ */
private int runGuiApplication() { private int runGuiApplication() {
try { try {
uiLauncher.get().launch(); Application.launch(MainApp.class);
shutdownLatch.await();
LOG.info("UI shut down"); LOG.info("UI shut down");
return 0; return 0;
} catch (InterruptedException e) { } catch (Exception e) {
Thread.currentThread().interrupt(); LOG.error("Terminating due to error", e);
return 1; 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,25 @@
package org.cryptomator.launcher; package org.cryptomator.launcher;
import dagger.BindsInstance;
import dagger.Component; import dagger.Component;
import org.cryptomator.common.CommonsModule; import org.cryptomator.common.CommonsModule;
import org.cryptomator.logging.LoggerModule; 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; import javax.inject.Singleton;
@Singleton @Singleton
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class}) @Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
public interface CryptomatorComponent { public interface CryptomatorComponent {
Cryptomator application(); 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.Module;
import dagger.Provides; 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.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.Optional; 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 { class CryptomatorModule {
@Provides @Provides
@Singleton @Singleton
@Named("shutdownLatch") static ResourceBundle provideLocalization() {
static CountDownLatch provideShutdownLatch() { return ResourceBundle.getBundle("i18n.strings");
return new CountDownLatch(1); }
@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; package org.cryptomator.launcher;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
@Singleton @Singleton
class FileOpenRequestHandler { class FileOpenRequestHandler {
@@ -1,7 +1,6 @@
package org.cryptomator.launcher; package org.cryptomator.launcher;
import org.cryptomator.ipc.IpcMessageListener; import org.cryptomator.ipc.IpcMessageListener;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", //
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", //
"sr-Latn", "sv", "sw", "ta", "te", "th", "tr", "uk", "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);
}
}
@@ -14,7 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil; import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.mainwindow.MainWindow; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController; import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
import javax.inject.Named; import javax.inject.Named;
@@ -43,12 +43,12 @@ public abstract class AddVaultModule {
@Provides @Provides
@AddVaultWizardWindow @AddVaultWizardWindow
@AddVaultWizardScoped @AddVaultWizardScoped
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) { static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create(); Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title")); stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false); stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL); stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(owner); stage.initOwner(primaryStage);
return stage; return stage;
} }
@@ -2,25 +2,24 @@ package org.cryptomator.ui.addvaultwizard;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.Optional;
@AddVaultWizardScoped @AddVaultWizardScoped
public class AddVaultSuccessController implements FxController { public class AddVaultSuccessController implements FxController {
private final FxApplication fxApplication; private final FxApplicationWindows appWindows;
private final Stage window; private final Stage window;
private final ReadOnlyObjectProperty<Vault> vault; private final ReadOnlyObjectProperty<Vault> vault;
@Inject @Inject
AddVaultSuccessController(FxApplication fxApplication, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) { AddVaultSuccessController(FxApplicationWindows appWindows, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
this.fxApplication = fxApplication; this.appWindows = appWindows;
this.window = window; this.window = window;
this.vault = vault; this.vault = vault;
} }
@@ -28,7 +27,7 @@ public class AddVaultSuccessController implements FxController {
@FXML @FXML
public void unlockAndClose() { public void unlockAndClose() {
close(); close();
fxApplication.startUnlockWorkflow(vault.get(), Optional.of(window)); appWindows.startUnlockWorkflow(vault.get(), window);
} }
@FXML @FXML
@@ -2,12 +2,14 @@ package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy; import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,12 +22,11 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@AddVaultWizardScoped @AddVaultWizardScoped
public class ChooseExistingVaultController implements FxController { public class ChooseExistingVaultController implements FxController {
@@ -34,30 +35,32 @@ public class ChooseExistingVaultController implements FxController {
private final Stage window; private final Stage window;
private final Lazy<Scene> welcomeScene; private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> successScene; private final Lazy<Scene> successScene;
private final ErrorComponent.Builder errorComponent; private final FxApplicationWindows appWindows;
private final ObjectProperty<Path> vaultPath; private final ObjectProperty<Path> vaultPath;
private final ObjectProperty<Vault> vault; private final ObjectProperty<Vault> vault;
private final VaultListManager vaultListManager; private final VaultListManager vaultListManager;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final Settings settings;
private Image screenshot; private Image screenshot;
@Inject @Inject
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) { ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, Settings settings) {
this.window = window; this.window = window;
this.welcomeScene = welcomeScene; this.welcomeScene = welcomeScene;
this.successScene = successScene; this.successScene = successScene;
this.errorComponent = errorComponent; this.appWindows = appWindows;
this.vaultPath = vaultPath; this.vaultPath = vaultPath;
this.vault = vault; this.vault = vault;
this.vaultListManager = vaultListManager; this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.settings = settings;
} }
@FXML @FXML
public void initialize() { public void initialize() {
if (SystemUtils.IS_OS_MAC) { if (SystemUtils.IS_OS_MAC) {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac.png").toString()); this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac"+(UiTheme.LIGHT == settings.theme().get()? "":"-dark")+".png").toString());
} else { } else {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString()); this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString());
} }
@@ -72,7 +75,7 @@ public class ChooseExistingVaultController implements FxController {
public void chooseFileAndNext() { public void chooseFileAndNext() {
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle")); fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Vault", "*.cryptomator")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
File masterkeyFile = fileChooser.showOpenDialog(window); File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) { if (masterkeyFile != null) {
vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent()); vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent());
@@ -82,7 +85,7 @@ public class ChooseExistingVaultController implements FxController {
window.setScene(successScene.get()); window.setScene(successScene.get());
} catch (IOException e) { } catch (IOException e) {
LOG.error("Failed to open existing vault.", e); LOG.error("Failed to open existing vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene(); appWindows.showErrorWindow(e, window, window.getScene());
} }
} }
} }
@@ -46,7 +46,7 @@ public class CreateNewVaultLocationController implements FxController {
private final Stage window; private final Stage window;
private final Lazy<Scene> chooseNameScene; private final Lazy<Scene> chooseNameScene;
private final Lazy<Scene> choosePasswordScene; private final Lazy<Scene> choosePasswordScene;
private final LocationPresets locationPresets; private final ObservedLocationPresets locationPresets;
private final ObjectProperty<Path> vaultPath; private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName; private final StringProperty vaultName;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
@@ -71,7 +71,7 @@ public class CreateNewVaultLocationController implements FxController {
public FontAwesome5IconView badLocation; public FontAwesome5IconView badLocation;
@Inject @Inject
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) { CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window; this.window = window;
this.chooseNameScene = chooseNameScene; this.chooseNameScene = chooseNameScene;
this.choosePasswordScene = choosePasswordScene; this.choosePasswordScene = choosePasswordScene;
@@ -197,7 +197,7 @@ public class CreateNewVaultLocationController implements FxController {
return validVaultPath.get(); return validVaultPath.get();
} }
public LocationPresets getLocationPresets() { public ObservedLocationPresets getObservedLocationPresets() {
return locationPresets; return locationPresets;
} }
@@ -10,12 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.common.Tasks;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory; import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -60,7 +60,7 @@ public class CreateNewVaultPasswordController implements FxController {
private final Lazy<Scene> chooseLocationScene; private final Lazy<Scene> chooseLocationScene;
private final Lazy<Scene> recoveryKeyScene; private final Lazy<Scene> recoveryKeyScene;
private final Lazy<Scene> successScene; private final Lazy<Scene> successScene;
private final ErrorComponent.Builder errorComponent; private final FxApplicationWindows appWindows;
private final ExecutorService executor; private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory; private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty vaultNameProperty; private final StringProperty vaultNameProperty;
@@ -83,12 +83,12 @@ public class CreateNewVaultPasswordController implements FxController {
public NewPasswordController newPasswordSceneController; public NewPasswordController newPasswordSceneController;
@Inject @Inject
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) { CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window; this.window = window;
this.chooseLocationScene = chooseLocationScene; this.chooseLocationScene = chooseLocationScene;
this.recoveryKeyScene = recoveryKeyScene; this.recoveryKeyScene = recoveryKeyScene;
this.successScene = successScene; this.successScene = successScene;
this.errorComponent = errorComponent; this.appWindows = appWindows;
this.executor = executor; this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory; this.recoveryKeyFactory = recoveryKeyFactory;
this.vaultNameProperty = vaultName; this.vaultNameProperty = vaultName;
@@ -121,16 +121,6 @@ public class CreateNewVaultPasswordController implements FxController {
@FXML @FXML
public void next() { public void next() {
Path pathToVault = vaultPathProperty.get();
try {
Files.createDirectory(pathToVault);
} catch (IOException e) {
LOG.error("Failed to create vault directory.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
return;
}
if (showRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) { if (showRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
showRecoveryKeyScene(); showRecoveryKeyScene();
} else if (skipRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) { } else if (skipRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
@@ -144,15 +134,15 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get(); Path pathToVault = vaultPathProperty.get();
processing.set(true); processing.set(true);
Tasks.create(() -> { Tasks.create(() -> {
initializeVault(pathToVault); createVault(pathToVault);
return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters()); return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters());
}).onSuccess(recoveryKey -> { }).onSuccess(recoveryKey -> {
initializationSucceeded(pathToVault); creationSucceeded(pathToVault);
recoveryKeyProperty.set(recoveryKey); recoveryKeyProperty.set(recoveryKey);
window.setScene(recoveryKeyScene.get()); window.setScene(recoveryKeyScene.get());
}).onError(IOException.class, e -> { }).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e); LOG.error("Failed to create vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene(); appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> { }).andFinally(() -> {
processing.set(false); processing.set(false);
}).runOnce(executor); }).runOnce(executor);
@@ -162,19 +152,22 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get(); Path pathToVault = vaultPathProperty.get();
processing.set(true); processing.set(true);
Tasks.create(() -> { Tasks.create(() -> {
initializeVault(pathToVault); createVault(pathToVault);
}).onSuccess(() -> { }).onSuccess(() -> {
initializationSucceeded(pathToVault); creationSucceeded(pathToVault);
window.setScene(successScene.get()); window.setScene(successScene.get());
}).onError(IOException.class, e -> { }).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e); LOG.error("Failed to create vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene(); appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> { }).andFinally(() -> {
processing.set(false); processing.set(false);
}).runOnce(executor); }).runOnce(executor);
} }
private void initializeVault(Path path) throws IOException { private void createVault(Path path) throws IOException {
// 0. create directory
Files.createDirectory(path);
// 1. write masterkey: // 1. write masterkey:
Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME); Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
try (Masterkey masterkey = Masterkey.generate(csprng)) { try (Masterkey masterkey = Masterkey.generate(csprng)) {
@@ -193,7 +186,7 @@ public class CreateNewVaultPasswordController implements FxController {
ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf())); ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
} }
} catch (CryptoException e) { } catch (CryptoException e) {
throw new IOException("Failed initialize vault.", e); throw new IOException("Vault initialization failed", e);
} }
} }
@@ -206,7 +199,7 @@ public class CreateNewVaultPasswordController implements FxController {
LOG.info("Created vault at {}", path); LOG.info("Created vault at {}", path);
} }
private void initializationSucceeded(Path pathToVault) { private void creationSucceeded(Path pathToVault) {
try { try {
Vault newVault = vaultListManager.add(pathToVault); Vault newVault = vaultListManager.add(pathToVault);
vaultProperty.set(newVault); vaultProperty.set(newVault);
@@ -1,23 +1,15 @@
package org.cryptomator.ui.addvaultwizard; package org.cryptomator.ui.addvaultwizard;
import org.cryptomator.common.LocationPreset;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
@AddVaultWizardScoped @AddVaultWizardScoped
public class LocationPresets { public class ObservedLocationPresets {
private static final String USER_HOME = System.getProperty("user.home");
private static final String[] ICLOUDDRIVE_LOCATIONS = {"~/Library/Mobile Documents/iCloud~com~setolabs~Cryptomator/Documents", "~/iCloudDrive/iCloud~com~setolabs~Cryptomator"};
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive/My Drive", "~/Google Drive"};
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
private final ReadOnlyObjectProperty<Path> iclouddriveLocation; private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
private final ReadOnlyObjectProperty<Path> dropboxLocation; private final ReadOnlyObjectProperty<Path> dropboxLocation;
@@ -33,13 +25,13 @@ public class LocationPresets {
private final BooleanBinding foundPcloud; private final BooleanBinding foundPcloud;
@Inject @Inject
public LocationPresets() { public ObservedLocationPresets() {
this.iclouddriveLocation = new SimpleObjectProperty<>(existingWritablePath(ICLOUDDRIVE_LOCATIONS)); this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath());
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS)); this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath());
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS)); this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath());
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS)); this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath());
this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS)); this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath());
this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS)); this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath());
this.foundIclouddrive = iclouddriveLocation.isNotNull(); this.foundIclouddrive = iclouddriveLocation.isNotNull();
this.foundDropbox = dropboxLocation.isNotNull(); this.foundDropbox = dropboxLocation.isNotNull();
this.foundGdrive = gdriveLocation.isNotNull(); this.foundGdrive = gdriveLocation.isNotNull();
@@ -48,24 +40,6 @@ public class LocationPresets {
this.foundPcloud = pcloudLocation.isNotNull(); this.foundPcloud = pcloudLocation.isNotNull();
} }
private static Path existingWritablePath(String... candidates) {
for (String candidate : candidates) {
Path path = Paths.get(resolveHomePath(candidate));
if (Files.isDirectory(path)) {
return path;
}
}
return null;
}
private static String resolveHomePath(String path) {
if (path.startsWith("~/")) {
return USER_HOME + path.substring(1);
} else {
return path;
}
}
/* Observables */ /* Observables */
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() { public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
@@ -8,10 +8,10 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.security.SecureRandom;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX; import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -38,9 +37,8 @@ public class ChangePasswordController implements FxController {
private final Stage window; private final Stage window;
private final Vault vault; private final Vault vault;
private final ErrorComponent.Builder errorComponent; private final FxApplicationWindows appWindows;
private final KeychainManager keychain; private final KeychainManager keychain;
private final SecureRandom csprng;
private final MasterkeyFileAccess masterkeyFileAccess; private final MasterkeyFileAccess masterkeyFileAccess;
public NiceSecurePasswordField oldPasswordField; public NiceSecurePasswordField oldPasswordField;
@@ -49,12 +47,11 @@ public class ChangePasswordController implements FxController {
public NewPasswordController newPasswordController; public NewPasswordController newPasswordController;
@Inject @Inject
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) { public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, FxApplicationWindows appWindows, KeychainManager keychain, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window; this.window = window;
this.vault = vault; this.vault = vault;
this.errorComponent = errorComponent; this.appWindows = appWindows;
this.keychain = keychain; this.keychain = keychain;
this.csprng = csprng;
this.masterkeyFileAccess = masterkeyFileAccess; this.masterkeyFileAccess = masterkeyFileAccess;
} }
@@ -95,7 +92,7 @@ public class ChangePasswordController implements FxController {
oldPasswordField.requestFocus(); oldPasswordField.requestFocus();
} catch (IOException | CryptoException e) { } catch (IOException | CryptoException e) {
LOG.error("Password change failed. Unable to perform operation.", e); LOG.error("Password change failed. Unable to perform operation.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene(); appWindows.showErrorWindow(e, window, window.getScene());
} }
} }
@@ -4,7 +4,6 @@ import dagger.BindsInstance;
import dagger.Subcomponent; import dagger.Subcomponent;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
import javafx.application.Platform;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -16,34 +15,17 @@ public interface ErrorComponent {
@FxmlScene(FxmlFile.ERROR) @FxmlScene(FxmlFile.ERROR)
Scene scene(); Scene scene();
default void showErrorScene() { default Stage show() {
if (Platform.isFxApplicationThread()) {
show();
} else {
Platform.runLater(this::show);
}
}
private void show() {
Stage stage = window(); Stage stage = window();
stage.setScene(scene()); stage.setScene(scene());
stage.show(); stage.show();
return stage;
} }
@Subcomponent.Builder @Subcomponent.Factory
interface Builder { interface Factory {
@BindsInstance
Builder cause(Throwable cause);
@BindsInstance
Builder window(Stage window);
@BindsInstance
Builder returnToScene(@Nullable Scene previousScene);
ErrorComponent build();
ErrorComponent create(@BindsInstance Throwable cause, @BindsInstance Stage window, @BindsInstance @Nullable Scene previousScene);
} }
} }
@@ -42,7 +42,7 @@ public enum FxmlFile {
this.ressourcePathString = ressourcePathString; this.ressourcePathString = ressourcePathString;
} }
String getRessourcePathString() { public String getRessourcePathString() {
return ressourcePathString; return ressourcePathString;
} }
} }
@@ -22,6 +22,10 @@ public class FxmlLoaderFactory {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
} }
public static <T extends FxController> FxmlLoaderFactory forController(T controller, Function<Parent, Scene> sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(Map.of(controller.getClass(), () -> controller), sceneFactory, resourceBundle);
}
/** /**
* @return A new FXMLLoader instance * @return A new FXMLLoader instance
*/ */
@@ -1,23 +1,24 @@
package org.cryptomator.ui.common; package org.cryptomator.ui.common;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import javax.inject.Inject;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import java.util.function.Consumer; import java.util.function.Consumer;
@FxApplicationScoped
public class StageFactory { public class StageFactory {
private final Consumer<Stage> initializer; private final Consumer<Stage> initializer;
public StageFactory(Consumer<Stage> initializer) { @Inject
public StageFactory(StageInitializer initializer) {
this.initializer = initializer; this.initializer = initializer;
} }
public Stage create() { public Stage create() {
return create(StageStyle.DECORATED); Stage stage = new Stage(StageStyle.DECORATED);
}
public Stage create(StageStyle stageStyle) {
Stage stage = new Stage(stageStyle);
initializer.accept(stage); initializer.accept(stage);
return stage; return stage;
} }
@@ -0,0 +1,32 @@
package org.cryptomator.ui.common;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import javax.inject.Inject;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.util.List;
import java.util.function.Consumer;
/**
* Performs common setup for all stages
*/
@FxApplicationScoped
public class StageInitializer implements Consumer<Stage> {
private final List<Image> windowIcons;
@Inject
public StageInitializer() {
this.windowIcons = SystemUtils.IS_OS_MAC ? List.of() : List.of( //
new Image(StageInitializer.class.getResource("/img/window_icon_32.png").toString()), //
new Image(StageInitializer.class.getResource("/img/window_icon_512.png").toString()) //
);
}
@Override
public void accept(Stage stage) {
stage.getIcons().setAll(windowIcons);
}
}
@@ -1,61 +0,0 @@
package org.cryptomator.ui.common;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class UserInteractionLock<E extends Enum<E>> {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private final BooleanProperty awaitingInteraction = new SimpleBooleanProperty();
private final AtomicBoolean interacted = new AtomicBoolean();
private final AtomicReference<E> state;
public UserInteractionLock(E initialValue) {
this.state = new AtomicReference<>(initialValue);
}
public synchronized void reset(E value) {
state.set(value);
interacted.set(false);
}
public void interacted(E result) {
assert Platform.isFxApplicationThread();
lock.lock();
try {
state.set(result);
interacted.set(true);
awaitingInteraction.set(false);
condition.signal();
} finally {
lock.unlock();
}
}
public E awaitInteraction() throws InterruptedException {
assert !Platform.isFxApplicationThread();
lock.lock();
try {
Platform.runLater(() -> awaitingInteraction.set(true));
while (!interacted.get()) {
condition.await();
}
return state.get();
} finally {
lock.unlock();
}
}
public ReadOnlyBooleanProperty awaitingInteraction() {
return awaitingInteraction;
}
}
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.stage.Stage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
@@ -53,7 +54,9 @@ public class VaultService {
* *
* @param vault The vault to lock * @param vault The vault to lock
* @param forced Whether to attempt a forced lock * @param forced Whether to attempt a forced lock
* @deprecated use {@link org.cryptomator.ui.fxapp.FxApplicationWindows#startLockWorkflow(Vault, Stage)}
*/ */
@Deprecated
public void lock(Vault vault, boolean forced) { public void lock(Vault vault, boolean forced) {
executorService.execute(createLockTask(vault, forced)); executorService.execute(createLockTask(vault, forced));
} }
@@ -90,7 +93,7 @@ public class VaultService {
* @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask * @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask
*/ */
public Task<Collection<Vault>> createLockAllTask(Collection<Vault> vaults, boolean forced) { public Task<Collection<Vault>> createLockAllTask(Collection<Vault> vaults, boolean forced) {
List<Task<Vault>> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList()); List<Task<Vault>> lockTasks = vaults.stream().<Task<Vault>>map(v -> new LockVaultTask(v, forced)).toList();
lockTasks.forEach(executorService::execute); lockTasks.forEach(executorService::execute);
Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks); Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks);
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", ")); String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
@@ -1,5 +1,7 @@
package org.cryptomator.ui.controls; package org.cryptomator.ui.controls;
import org.cryptomator.common.Passphrase;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@@ -82,7 +84,7 @@ public class NiceSecurePasswordField extends StackPane {
return passwordField.textProperty(); return passwordField.textProperty();
} }
public CharSequence getCharacters() { public Passphrase getCharacters() {
return passwordField.getCharacters(); return passwordField.getCharacters();
} }
@@ -9,6 +9,7 @@
package org.cryptomator.ui.controls; package org.cryptomator.ui.controls;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.cryptomator.common.Passphrase;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.NamedArg; import javafx.beans.NamedArg;
@@ -28,7 +29,6 @@ import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
import java.nio.CharBuffer;
import java.text.Normalizer; import java.text.Normalizer;
import java.text.Normalizer.Form; import java.text.Normalizer.Form;
import java.util.Arrays; import java.util.Arrays;
@@ -203,8 +203,8 @@ public class SecurePasswordField extends TextField {
* @see #wipe() * @see #wipe()
*/ */
@Override @Override
public CharSequence getCharacters() { public Passphrase getCharacters() {
return CharBuffer.wrap(content, 0, length); return new Passphrase(content, 0, length);
} }
/** /**
@@ -1,14 +1,14 @@
package org.cryptomator.ui.launcher; package org.cryptomator.ui.fxapp;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.launcher.AppLaunchEvent;
import org.cryptomator.ui.common.VaultService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton;
import javafx.application.Platform; import javafx.application.Platform;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -17,22 +17,25 @@ import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT; import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
@Singleton // TODO: use message bus
@FxApplicationScoped
class AppLaunchEventHandler { class AppLaunchEventHandler {
private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class); private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class);
private final BlockingQueue<AppLaunchEvent> launchEventQueue; private final BlockingQueue<AppLaunchEvent> launchEventQueue;
private final ExecutorService executorService; private final ExecutorService executorService;
private final FxApplicationStarter fxApplicationStarter; private final FxApplicationWindows appWindows;
private final VaultListManager vaultListManager; private final VaultListManager vaultListManager;
private final VaultService vaultService;
@Inject @Inject
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationStarter fxApplicationStarter, VaultListManager vaultListManager) { public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
this.launchEventQueue = launchEventQueue; this.launchEventQueue = launchEventQueue;
this.executorService = executorService; this.executorService = executorService;
this.fxApplicationStarter = fxApplicationStarter; this.appWindows = appWindows;
this.vaultListManager = vaultListManager; this.vaultListManager = vaultListManager;
this.vaultService = vaultService;
} }
public void startHandlingLaunchEvents() { public void startHandlingLaunchEvents() {
@@ -52,14 +55,12 @@ class AppLaunchEventHandler {
} }
private void handleLaunchEvent(AppLaunchEvent event) { private void handleLaunchEvent(AppLaunchEvent event) {
switch (event.getType()) { switch (event.type()) {
case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow); case REVEAL_APP -> appWindows.showMainWindow();
case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> { case OPEN_FILE -> Platform.runLater(() -> {
Platform.runLater(() -> { event.pathsToOpen().forEach(this::addOrRevealVault);
event.getPathsToOpen().forEach(this::addOrRevealVault);
});
}); });
default -> LOG.warn("Unsupported event type: {}", event.getType()); default -> LOG.warn("Unsupported event type: {}", event.type());
} }
} }
@@ -75,7 +76,7 @@ class AppLaunchEventHandler {
} }
if (v.isUnlocked()) { if (v.isUnlocked()) {
fxApplicationStarter.get().thenAccept(app -> app.getVaultService().reveal(v)); vaultService.reveal(v);
} }
LOG.debug("Added vault {}", potentialVaultPath); LOG.debug("Added vault {}", potentialVaultPath);
} catch (IOException e) { } catch (IOException e) {
@@ -0,0 +1,26 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Inject;
import javafx.collections.ObservableList;
@FxApplicationScoped
public class AutoUnlocker {
private final ObservableList<Vault> vaults;
private final FxApplicationWindows appWindows;
@Inject
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows) {
this.vaults = vaults;
this.appWindows = appWindows;
}
public void unlock() {
vaults.stream().filter(Vault::isLocked).filter(v -> v.getVaultSettings().unlockAfterStartup().get()).forEach(v -> {
appWindows.startUnlockWorkflow(v, null);
});
}
}
@@ -0,0 +1,20 @@
package org.cryptomator.ui.fxapp;
import javafx.application.Platform;
import java.awt.desktop.QuitResponse;
record ExitingQuitResponse(QuitResponse delegate) implements QuitResponse {
@Override
public void performQuit() {
Platform.exit();
// TODO wait a moment for javafx to terminate?
delegate.performQuit();
}
@Override
public void cancelQuit() {
delegate.cancelQuit();
}
}
@@ -1,213 +1,74 @@
package org.cryptomator.ui.fxapp; package org.cryptomator.ui.fxapp;
import dagger.Lazy; import dagger.Lazy;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
import javafx.stage.Window;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme; import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Named;
import java.awt.desktop.QuitResponse; import javafx.application.Platform;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@FxApplicationScoped @FxApplicationScoped
public class FxApplication extends Application { public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class); private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
private final long startupTime;
private final Settings settings; private final Settings settings;
private final Lazy<MainWindowComponent> mainWindow; private final AppLaunchEventHandler launchEventHandler;
private final Lazy<PreferencesComponent> preferencesWindow; private final Lazy<TrayMenuComponent> trayMenu;
private final Lazy<QuitComponent> quitWindow; private final FxApplicationWindows appWindows;
private final Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider; private final FxApplicationStyle applicationStyle;
private final Provider<LockComponent.Builder> lockWorkflowBuilderProvider; private final FxApplicationTerminator applicationTerminator;
private final ErrorComponent.Builder errorWindowBuilder; private final AutoUnlocker autoUnlocker;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final VaultService vaultService;
private final LicenseHolder licenseHolder;
private final ObservableList<Window> visibleWindows;
private final BooleanBinding hasVisibleWindows;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject @Inject
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) { FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
this.startupTime = startupTime;
this.settings = settings; this.settings = settings;
this.mainWindow = mainWindow; this.launchEventHandler = launchEventHandler;
this.preferencesWindow = preferencesWindow; this.trayMenu = trayMenu;
this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider; this.appWindows = appWindows;
this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider; this.applicationStyle = applicationStyle;
this.quitWindow = quitWindow; this.applicationTerminator = applicationTerminator;
this.errorWindowBuilder = errorWindowBuilder; this.autoUnlocker = autoUnlocker;
this.trayIntegration = trayIntegration;
this.appearanceProvider = appearanceProvider;
this.vaultService = vaultService;
this.licenseHolder = licenseHolder;
this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
} }
public void start() { public void start() {
LOG.trace("FxApplication.start()"); LOG.trace("FxApplication.start()");
Platform.setImplicitExit(false); applicationStyle.initialize();
appWindows.initialize();
applicationTerminator.initialize();
hasVisibleWindows.addListener(this::hasVisibleStagesChanged); // init system tray
final boolean hasTrayIcon;
settings.theme().addListener(this::appThemeChanged); if (settings.showTrayIcon().get() && trayMenu.get().isSupported()) {
loadSelectedStyleSheet(settings.theme().get()); trayMenu.get().initializeTrayIcon();
} Platform.setImplicitExit(false); // don't quit when closing all windows
hasTrayIcon = true;
@Override
public void start(Stage stage) {
throw new UnsupportedOperationException("Use start() instead.");
}
private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
LOG.debug("has visible stages: {}", newValue);
if (newValue) {
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
} else { } else {
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray); hasTrayIcon = false;
} }
}
public void showPreferencesWindow(SelectedPreferencesTab selectedTab) { // show main window
Platform.runLater(() -> { appWindows.showMainWindow().thenAccept(stage -> {
preferencesWindow.get().showPreferencesWindow(selectedTab); if (settings.startHidden().get()) {
LOG.debug("Showing Preferences"); if (hasTrayIcon) {
}); stage.hide();
} } else {
stage.setIconified(true);
public CompletionStage<Stage> showMainWindow() { }
CompletableFuture<Stage> future = new CompletableFuture<>();
Platform.runLater(() -> {
var win = mainWindow.get().showMainWindow();
LOG.debug("Showing MainWindow");
future.complete(win);
});
return future;
}
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
Platform.runLater(() -> {
if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
} else {
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
} }
LOG.debug("Main window initialized after {}ms", System.currentTimeMillis() - startupTime);
}).exceptionally(error -> {
LOG.error("Failed to show main window", error);
return null;
}); });
}
public void startLockWorkflow(Vault vault, Optional<Stage> owner) { launchEventHandler.startHandlingLaunchEvents();
Platform.runLater(() -> { autoUnlocker.unlock();
if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
} else {
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
}
});
}
public void showQuitWindow(QuitResponse response) {
Platform.runLater(() -> {
quitWindow.get().showQuitWindow(response);
LOG.debug("Showing QuitWindow");
});
}
public VaultService getVaultService() {
return vaultService;
}
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
try {
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to disable automatic theme switching.");
}
}
loadSelectedStyleSheet(newValue);
}
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
case AUTOMATIC -> {
appearanceProvider.ifPresent(appearanceProvider -> {
try {
appearanceProvider.addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic theme switching.");
}
});
applySystemTheme();
}
}
}
private void systemInterfaceThemeChanged(Theme theme) {
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
}
}
private void applySystemTheme() {
if (appearanceProvider.isPresent()) {
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
} else {
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
applyLightTheme();
}
}
private void applyLightTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
appearanceProvider.ifPresent(appearanceProvider -> {
appearanceProvider.adjustToTheme(Theme.LIGHT);
});
}
private void applyDarkTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
appearanceProvider.ifPresent(appearanceProvider -> {
appearanceProvider.adjustToTheme(Theme.DARK);
});
} }
} }
@@ -5,8 +5,12 @@
*******************************************************************************/ *******************************************************************************/
package org.cryptomator.ui.fxapp; package org.cryptomator.ui.fxapp;
import dagger.BindsInstance;
import dagger.Subcomponent; import dagger.Subcomponent;
import javafx.application.Application;
import javafx.stage.Stage;
@FxApplicationScoped @FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class) @Subcomponent(modules = FxApplicationModule.class)
public interface FxApplicationComponent { public interface FxApplicationComponent {
@@ -16,6 +20,12 @@ public interface FxApplicationComponent {
@Subcomponent.Builder @Subcomponent.Builder
interface Builder { interface Builder {
@BindsInstance
Builder fxApplication(Application application);
@BindsInstance
Builder primaryStage(@PrimaryStage Stage primaryStage);
FxApplicationComponent build(); FxApplicationComponent build();
} }
@@ -5,7 +5,6 @@
*******************************************************************************/ *******************************************************************************/
package org.cryptomator.ui.fxapp; package org.cryptomator.ui.fxapp;
import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
@@ -15,67 +14,46 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent; import org.cryptomator.ui.unlock.UnlockComponent;
import javax.inject.Named; import javax.inject.Named;
import javafx.application.Application;
import javafx.collections.ObservableSet;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class}) @Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
abstract class FxApplicationModule { abstract class FxApplicationModule {
@Provides
@Named("windowIcons")
@FxApplicationScoped
static List<Image> provideWindowIcons() {
if (SystemUtils.IS_OS_MAC) {
return Collections.emptyList();
}
try {
return List.of( //
createImageFromResource("/img/window_icon_32.png"), //
createImageFromResource("/img/window_icon_512.png") //
);
} catch (IOException e) {
throw new UncheckedIOException("Failed to load embedded resource.", e);
}
}
@Provides
@FxApplicationScoped
static StageFactory provideStageFactory(@Named("windowIcons") List<Image> windowIcons) {
return new StageFactory(stage -> {
stage.getIcons().addAll(windowIcons);
});
}
private static Image createImageFromResource(String resourceName) throws IOException { private static Image createImageFromResource(String resourceName) throws IOException {
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) { try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
return new Image(in); return new Image(in);
} }
} }
@Binds @Provides
abstract Application bindApplication(FxApplication application); @FxApplicationScoped
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
return builder.build();
}
@Provides @Provides
@FxApplicationScoped
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) { static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
return builder.build(); return builder.build();
} }
@Provides @Provides
@FxApplicationScoped
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) { static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
return builder.build(); return builder.build();
} }
@Provides @Provides
@FxApplicationScoped
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) { static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
return builder.build(); return builder.build();
} }
@@ -0,0 +1,108 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import java.util.Optional;
@FxApplicationScoped
public class FxApplicationStyle {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStyle.class);
private final Settings settings;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final LicenseHolder licenseHolder;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder){
this.settings = settings;
this.appearanceProvider = appearanceProvider;
this.licenseHolder = licenseHolder;
}
public void initialize() {
settings.theme().addListener(this::appThemeChanged);
loadSelectedStyleSheet(settings.theme().get());
}
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
try {
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to disable automatic theme switching.");
}
}
loadSelectedStyleSheet(newValue);
}
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
case AUTOMATIC -> {
appearanceProvider.ifPresent(provider -> {
try {
provider.addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic theme switching.");
}
});
applySystemTheme();
}
}
}
private void systemInterfaceThemeChanged(Theme theme) {
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
}
}
private void applySystemTheme() {
if (appearanceProvider.isPresent()) {
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
} else {
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
applyLightTheme();
}
}
private void applyLightTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
.orElse(getClass().getResource("/css/light_theme.css"));
if (stylesheet == null) {
LOG.warn("Failed to load light_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
}
}
private void applyDarkTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
.orElse(getClass().getResource("/css/dark_theme.css"));
if (stylesheet == null) {
LOG.warn("Failed to load dark_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
}
}
}
@@ -0,0 +1,134 @@
package org.cryptomator.ui.fxapp;
import com.google.common.base.Preconditions;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import java.awt.Desktop;
import java.awt.desktop.QuitResponse;
import java.awt.desktop.QuitStrategy;
import java.util.EnumSet;
import java.util.EventObject;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@FxApplicationScoped
public class FxApplicationTerminator {
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
private final ObservableList<Vault> vaults;
private final ShutdownHook shutdownHook;
private final FxApplicationWindows appWindows;
private final AtomicBoolean allowQuitWithoutPrompt = new AtomicBoolean();
@Inject
public FxApplicationTerminator(ObservableList<Vault> vaults, ShutdownHook shutdownHook, FxApplicationWindows appWindows) {
this.vaults = vaults;
this.shutdownHook = shutdownHook;
this.appWindows = appWindows;
}
public void initialize() {
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
Desktop desktop = Desktop.getDesktop();
// register quit handler
if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
desktop.setQuitHandler(this::handleQuitRequest);
}
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
if (desktop.isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
desktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
}
// allow sudden termination?
vaultListChanged(vaults);
vaults.addListener(this::vaultListChanged);
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
}
/**
* Gracefully terminates the application.
*/
public void terminate() {
handleQuitRequest(null, new NoopQuitResponse());
}
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
boolean allowSuddenTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
boolean stateChanged = allowQuitWithoutPrompt.compareAndSet(!allowSuddenTermination, allowSuddenTermination);
Desktop desktop = Desktop.getDesktop();
if (stateChanged && desktop.isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
if (allowSuddenTermination) {
LOG.debug("Enabling sudden termination");
desktop.enableSuddenTermination();
} else {
LOG.debug("Disabling sudden termination");
desktop.disableSuddenTermination();
}
}
}
/**
* Asks the app to quit. If confirmed, the JavaFX application will exit before giving a {@code response}.
*
* @param e ignored
* @param response a quit response that will be {@link ExitingQuitResponse decorated in order to exit the JavaFX application}.
*/
private void handleQuitRequest(@SuppressWarnings("unused") @Nullable EventObject e, QuitResponse response) {
var exitingResponse = new ExitingQuitResponse(response);
if (allowQuitWithoutPrompt.get()) {
exitingResponse.performQuit();
} else {
appWindows.showQuitWindow(exitingResponse);
}
}
private void forceUnmountRemainingVaults() {
for (Vault vault : vaults) {
if (vault.isUnlocked()) {
try {
vault.lock(true);
} catch (Volume.VolumeException e) {
LOG.error("Failed to unmount vault " + vault.getPath(), e);
} catch (LockNotCompletedException e) {
LOG.error("Failed to lock vault " + vault.getPath(), e);
}
}
}
}
/**
* A dummy QuitResponse that ignores the response.
*
* To be used with {@link #handleQuitRequest(EventObject, QuitResponse)} if the invoking method is not interested in the response.
*/
private static class NoopQuitResponse implements QuitResponse {
@Override
public void performQuit() {
// no-op
}
@Override
public void cancelQuit() {
// no-op
}
}
}
@@ -0,0 +1,155 @@
package org.cryptomator.ui.fxapp;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.awt.Desktop;
import java.awt.desktop.AppReopenedListener;
import java.awt.desktop.QuitResponse;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
@FxApplicationScoped
public class FxApplicationWindows {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationWindows.class);
private final Stage primaryStage;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> preferencesWindow;
private final Lazy<QuitComponent> quitWindow;
private final UnlockComponent.Factory unlockWorkflowFactory;
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final ExecutorService executor;
private final FilteredList<Window> visibleWindows;
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional<TrayIntegrationProvider> trayIntegration, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Lazy<QuitComponent> quitWindow, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.quitWindow = quitWindow;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.executor = executor;
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
}
public void initialize() {
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
Desktop desktop = Desktop.getDesktop();
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
}
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
}
// register app reopen listener
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
}
// observe visible windows
if (trayIntegration.isPresent()) {
visibleWindows.addListener(this::visibleWindowsChanged);
}
}
private void visibleWindowsChanged(ListChangeListener.Change<? extends Window> change) {
int visibleWindows = change.getList().size();
LOG.debug("visible windows: {}", visibleWindows);
if (visibleWindows > 0) {
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
} else {
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
}
}
public CompletionStage<Stage> showMainWindow() {
return CompletableFuture.supplyAsync(mainWindow.get()::showMainWindow, Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showPreferencesWindow(SelectedPreferencesTab selectedTab) {
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showQuitWindow(QuitResponse response) {
return CompletableFuture.supplyAsync(() -> quitWindow.get().showQuitWindow(response), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
}, Platform::runLater) //
.thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
.exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null;
});
}
public CompletionStage<Void> startLockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING), "Vault not unlocked.");
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
return lockWorkflowFactory.create(vault, owner).lockWorkflow();
}, Platform::runLater) //
.thenCompose(lockWorkflow -> CompletableFuture.runAsync(lockWorkflow, executor)) //
.exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null;
});
}
/**
* Displays the generic error scene in the given window.
*
* @param cause The exception to show
* @param window What window to display the scene in
* @param previousScene To what scene to return to when pressing "back". Back button will be hidden, if <code>null</code>
* @return A
*/
public CompletionStage<Stage> showErrorWindow(Throwable cause, Stage window, @Nullable Scene previousScene) {
return CompletableFuture.supplyAsync(() -> errorWindowFactory.create(cause, window, previousScene).show(), Platform::runLater).whenComplete(this::reportErrors);
}
private void reportErrors(@Nullable Stage stage, @Nullable Throwable error) {
if (error != null) {
LOG.error("Failed to display stage", error);
}
}
}
@@ -0,0 +1,14 @@
package org.cryptomator.ui.fxapp;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface PrimaryStage {
}
@@ -43,7 +43,9 @@ public abstract class UpdateCheckerModule {
@FxApplicationScoped @FxApplicationScoped
static Optional<HttpClient> provideHttpClient() { static Optional<HttpClient> provideHttpClient() {
try { try {
return Optional.of(HttpClient.newHttpClient()); return Optional.of(HttpClient.newBuilder() //
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
.build());
} catch (UncheckedIOException e) { } catch (UncheckedIOException e) {
LOG.error("HttpClient for update check cannot be created.", e); LOG.error("HttpClient for update check cannot be created.", e);
return Optional.empty(); return Optional.empty();
@@ -1,9 +1,8 @@
package org.cryptomator.ui.health; package org.cryptomator.ui.health;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -15,9 +14,7 @@ import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode; import javafx.scene.control.SelectionMode;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -37,7 +34,7 @@ public class CheckListController implements FxController {
private final ObjectProperty<Check> selectedCheck; private final ObjectProperty<Check> selectedCheck;
private final BooleanBinding mainRunStarted; //TODO: rerunning not considered for now private final BooleanBinding mainRunStarted; //TODO: rerunning not considered for now
private final BooleanBinding somethingsRunning; private final BooleanBinding somethingsRunning;
private final Lazy<ErrorComponent.Builder> errorComponentBuilder; private final FxApplicationWindows appWindows;
private final IntegerBinding chosenTaskCount; private final IntegerBinding chosenTaskCount;
private final BooleanBinding anyCheckSelected; private final BooleanBinding anyCheckSelected;
private final CheckListCellFactory listCellFactory; private final CheckListCellFactory listCellFactory;
@@ -46,7 +43,7 @@ public class CheckListController implements FxController {
public ListView<Check> checksListView; public ListView<Check> checksListView;
@Inject @Inject
public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, Lazy<ErrorComponent.Builder> errorComponentBuilder, CheckListCellFactory listCellFactory) { public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, FxApplicationWindows appWindows, CheckListCellFactory listCellFactory) {
this.window = window; this.window = window;
this.checks = FXCollections.observableList(checks, Check::observables); this.checks = FXCollections.observableList(checks, Check::observables);
this.checkExecutor = checkExecutor; this.checkExecutor = checkExecutor;
@@ -54,7 +51,7 @@ public class CheckListController implements FxController {
this.chosenChecks = this.checks.filtered(Check::isChosenForExecution); this.chosenChecks = this.checks.filtered(Check::isChosenForExecution);
this.reportWriter = reportWriteTask; this.reportWriter = reportWriteTask;
this.selectedCheck = selectedCheck; this.selectedCheck = selectedCheck;
this.errorComponentBuilder = errorComponentBuilder; this.appWindows = appWindows;
this.chosenTaskCount = Bindings.size(this.chosenChecks); this.chosenTaskCount = Bindings.size(this.chosenChecks);
this.mainRunStarted = Bindings.isEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.RUNNABLE)); this.mainRunStarted = Bindings.isEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.RUNNABLE));
this.somethingsRunning = Bindings.isNotEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.SCHEDULED || c.getState() == Check.CheckState.RUNNING)); this.somethingsRunning = Bindings.isNotEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.SCHEDULED || c.getState() == Check.CheckState.RUNNING));
@@ -104,7 +101,7 @@ public class CheckListController implements FxController {
reportWriter.writeReport(chosenChecks); reportWriter.writeReport(chosenChecks);
} catch (IOException e) { } catch (IOException e) {
LOG.error("Failed to write health check report.", e); LOG.error("Failed to write health check report.", e);
errorComponentBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene(); appWindows.showErrorWindow(e, window, window.getScene());
} }
} }
@@ -7,10 +7,10 @@ import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException; import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.unlock.UnlockCancelledException; import org.cryptomator.ui.unlock.UnlockCancelledException;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -40,10 +40,10 @@ public class StartController implements FxController {
private final AtomicReference<Masterkey> masterkeyRef; private final AtomicReference<Masterkey> masterkeyRef;
private final AtomicReference<VaultConfig> vaultConfigRef; private final AtomicReference<VaultConfig> vaultConfigRef;
private final Lazy<Scene> checkScene; private final Lazy<Scene> checkScene;
private final Lazy<ErrorComponent.Builder> errorComponent; private final FxApplicationWindows appWindows;
@Inject @Inject
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) { public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, FxApplicationWindows appWindows, @Named("unlockWindow") Stage unlockWindow) {
this.window = window; this.window = window;
this.unlockWindow = unlockWindow; this.unlockWindow = unlockWindow;
this.vaultConfig = vault.getVaultConfigCache(); this.vaultConfig = vault.getVaultConfigCache();
@@ -52,7 +52,7 @@ public class StartController implements FxController {
this.masterkeyRef = masterkeyRef; this.masterkeyRef = masterkeyRef;
this.vaultConfigRef = vaultConfigRef; this.vaultConfigRef = vaultConfigRef;
this.checkScene = checkScene; this.checkScene = checkScene;
this.errorComponent = errorComponent; this.appWindows = appWindows;
} }
@FXML @FXML
@@ -106,10 +106,10 @@ public class StartController implements FxController {
// ok // ok
} else if (e instanceof VaultKeyInvalidException) { } else if (e instanceof VaultKeyInvalidException) {
LOG.error("Invalid key"); //TODO: specific error screen LOG.error("Invalid key"); //TODO: specific error screen
errorComponent.get().window(window).cause(e).build().showErrorScene(); appWindows.showErrorWindow(e, window, null);
} else { } else {
LOG.error("Failed to load key.", e); LOG.error("Failed to load key.", e);
errorComponent.get().window(window).cause(e).build().showErrorScene(); appWindows.showErrorWindow(e, window, null);
} }
} }
@@ -26,7 +26,7 @@ abstract class KeyLoadingModule {
@Provides @Provides
@KeyLoading @KeyLoading
@KeyLoadingScoped @KeyLoadingScoped
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map<String, Provider<KeyLoadingStrategy>> strategies) { static KeyLoadingStrategy provideKeyLoadingStrategy(@KeyLoading Vault vault, Map<String, Provider<KeyLoadingStrategy>> strategies) {
try { try {
String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme(); String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme();
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme)); var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
@@ -0,0 +1,25 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import dagger.Subcomponent;
import javafx.scene.Scene;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
@ChooseMasterkeyFileScoped
@Subcomponent(modules = {ChooseMasterkeyFileModule.class})
public interface ChooseMasterkeyFileComponent {
@ChooseMasterkeyFileScoped
Scene chooseMasterkeyScene();
@ChooseMasterkeyFileScoped
CompletableFuture<Path> result();
@Subcomponent.Builder
interface Builder {
ChooseMasterkeyFileComponent build();
}
}
@@ -0,0 +1,59 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.File;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@ChooseMasterkeyFileScoped
public class ChooseMasterkeyFileController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ChooseMasterkeyFileController.class);
private final Stage window;
private final CompletableFuture<Path> result;
private final ResourceBundle resourceBundle;
@Inject
public ChooseMasterkeyFileController(@KeyLoading Stage window, CompletableFuture<Path> result, ResourceBundle resourceBundle) {
this.window = window;
this.result = result;
this.resourceBundle = resourceBundle;
this.window.setOnHiding(this::windowClosed);
}
@FXML
public void cancel() {
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
@FXML
public void proceed() {
LOG.trace("proceed()");
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("unlock.chooseMasterkey.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("unlock.chooseMasterkey.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
LOG.debug("Chose masterkey file: {}", masterkeyFile);
result.complete(masterkeyFile.toPath());
}
}
}
@@ -0,0 +1,29 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import javafx.scene.Scene;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
@Module
interface ChooseMasterkeyFileModule {
@Provides
@ChooseMasterkeyFileScoped
static CompletableFuture<Path> provideResult() {
return new CompletableFuture<>();
}
@Provides
@ChooseMasterkeyFileScoped
static Scene provideChooseMasterkeyScene(ChooseMasterkeyFileController controller, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return FxmlLoaderFactory.forController(controller, sceneFactory, resourceBundle).createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
}
}

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