Compare commits

..
Author SHA1 Message Date
Armin Schrenk 58eb6a6dee change status view from Label to Stackpane & adjust spinner size computation 2021-06-30 20:22:22 +02:00
Armin Schrenk b256670bcc replace static icon in vault detail screen by statusView 2021-06-30 13:50:06 +02:00
Armin Schrenk 91162d3d8f use RotateTranslation isntead of custom Animation 2021-06-30 13:20:34 +02:00
Armin Schrenk 683f17ef24 sum status indication with icon up in single class and apply proper css 2021-06-30 11:04:41 +02:00
Armin Schrenk e626104c06 Add progress indicator skin to light theme & correct attribution of origin 2021-06-29 17:59:15 +02:00
Armin Schrenk f73bcc0a9e clean up 2021-06-29 17:19:13 +02:00
Armin Schrenk 4167fdc850 create custom spinner 2021-06-29 16:30:52 +02:00
Armin Schrenk f544f72327 test impl 2021-06-29 15:59:45 +02:00
Armin SchrenkGitHubSnyk botTobias HagemannSebastian Stenzel <sebastian.stenzel@skymatic.de >
a96115ecd6 Feature: revamp health workflow init (#1695)
* Add health tab to vault options:
* health tool is started from there
* health tool closes old Window and creates new one
* added info and remarks about health tool
* adds confirmation checkbox before starting health tool
* close vault options window when starting health workflow
* Remove AutoLock tab, move options to general tab

Co-authored-by: Snyk bot <snyk-bot@snyk.io>
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
Co-authored-by: Sebastian Stenzel <sebastian.stenzel@skymatic.de >
2021-06-29 09:57:45 +02:00
Sebastian Stenzel c6aceb3e59 don't setPassphraseStored to true after changing pw, when it isn't actually stored 2021-06-28 18:52:34 +02:00
Sebastian Stenzel cadd071ebd update cryptofs 2021-06-28 18:42:10 +02:00
Sebastian Stenzel b64264ef8b updated 3rd party licenses
[ci skip]
2021-06-26 17:49:18 +02:00
Sebastian Stenzel 46691f057e dependency update 2021-06-26 17:42:40 +02:00
Sebastian Stenzel f1b40e32a7 dependency update 2021-06-26 17:25:00 +02:00
b4a97803ff [Snyk] Security upgrade org.cryptomator:webdav-nio-adapter from 1.2.2 to 1.2.3 (#1698)
* fix: pom.xml to reduce vulnerabilities

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-ORGECLIPSEJETTY-1313686

* adjusted suppression config

* bump webdav version

Co-authored-by: Sebastian Stenzel <sebastian.stenzel@gmail.com>
2021-06-24 09:58:47 +02:00
JaniruTECandGitHub 8ac253504f Merge pull request #1697 from cryptomator/refactoring/spellchecking
Enabled spellchecking, added dictionaries, fixed spelling mistakes
2021-06-23 22:49:57 +02:00
JaniruTEC 135b61070d Fixed misspelled key 2021-06-23 22:48:18 +02:00
Armin Schrenk 903f55a24f improve health check gui:
* convert milliseconds to hours, minutes and seconds
* spelling
* adjust to dark theme
2021-06-22 17:17:28 +02:00
Julian Raufelder f2fadafa66 Fix snyk.io badge in README due to switch to single maven module 2021-06-16 16:11:31 +02:00
Julian RaufelderandGitHub 0676732796 Merge pull request #1693 from cryptomator/feature/sign-src-tarball
Sign tarball in release using GPG
2021-06-16 12:54:37 +00:00
Julian Raufelder 08cbfd47ea Prefix signature of tarball in release with cryptomator
To apply convention of the other release assets
2021-06-16 14:20:52 +02:00
b15cda260e Apply suggestions from code review
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2021-06-16 12:13:49 +00:00
Julian Raufelder 74355b128a Sign tarball in release using GPG 2021-06-16 13:49:26 +02:00
JaniruTEC 000f27e166 Fixed spelling mistakes that required API breaking changes 2021-06-12 01:56:15 +02:00
JaniruTEC e1d83c996e Fixed independent spelling mistakes
Added @SuppressWarnings("SpellCheckingInspection") to binary data (e.g. keys) or deliberately misspelled strings
2021-06-12 01:45:31 +02:00
JaniruTEC 0dc401883f Enabled proofreading
Removed dict_* files from .gitignore
Added dict_de.xml
Added dict_project.xml
2021-06-12 01:35:36 +02:00
JaniruTEC 364d59fdf7 Added ".idea/**/shelf" to .gitignore
See: https://github.com/github/gitignore/blob/991e760c1c6d50fdda246e0178b9c58b06770b90/Global/JetBrains.gitignore#L9
2021-06-11 18:28:25 +02:00
Sebastian Stenzel 210050d9d5 update cryptofs 2021-06-09 18:15:39 +02:00
Sebastian Stenzel f10d2b713b made tests public to avoid reflection and "open modules"
see also https://issues.apache.org/jira/browse/SUREFIRE-1909
2021-06-09 10:14:08 +02:00
JaniruTEC f9545b5532 Refactored MPC priority 2021-06-09 01:16:51 +02:00
Sebastian StenzelandGitHub 58376bddef Merge pull request #1685 from cryptomator/feature/provider-agnostic-keychains
Remove all references to specific keychain access providers
2021-06-08 14:42:56 +02:00
Sebastian Stenzel 72e2f64a68 show provided display name 2021-06-07 16:24:42 +02:00
Sebastian Stenzel 48ad74715e * store class name of selected keychain in settings.json
* prepared GeneralPreferencesController to use a provider-supplied display name
2021-06-07 15:17:58 +02:00
Ralph PlawetzkiandGitHub 2e1f33ca82 Fix instructions for building (#1682) 2021-06-06 21:56:28 +02:00
2d3f4d1c03 New Crowdin updates (#1678)
[ci skip]

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2021-06-05 09:11:48 +02:00
04c13ba242 New Crowdin updates (#1677)
[ci skip]

Co-authored-by: Cryptobot <cryptobot@users.noreply.github.com>
2021-06-04 21:00:56 +02:00
Cryptobot b70033e777 Update Crowdin configuration file 2021-06-04 20:48:48 +02:00
Sebastian Stenzel fafb7b229f update i18n config
[ci skip]
2021-06-04 20:29:15 +02:00
Sebastian StenzelandGitHub 7fac6da448 Single maven module (#1676)
combined all sources into single maven module
2021-06-04 20:09:10 +02:00
Sebastian StenzelandGitHub 72bd9c1fdf New Crowdin updates (#1643)
[ci skip]
2021-06-04 15:44:48 +02:00
Sebastian StenzelandGitHub 39b8e25f4f Merge pull request #1596 from eas5/test_improvement
Improved grouped assertions
2021-06-04 15:42:16 +02:00
Sebastian Stenzel c8fa7f1edd fix NPE in autolocker, when reading "lastActivity" before any activity happened 2021-06-04 09:40:23 +02:00
Sebastian StenzelandGitHub 95b98b8a36 Merge pull request #1603 from cryptomator/feature/autoLock
fixes #274
2021-06-02 16:24:26 +02:00
Sebastian Stenzel 005d2a82fe use stopwatch item for autolock tab 2021-06-02 16:22:19 +02:00
Sebastian Stenzel 820f7989d0 remove debug log statement
[ci skip]
2021-06-02 16:10:42 +02:00
Sebastian Stenzel 751a6079c3 use explicit imports
[ci skip]
2021-06-02 16:09:23 +02:00
Sebastian Stenzel 9be95efd10 store autolock time in seconds instead of minutes 2021-06-02 16:08:46 +02:00
Sebastian Stenzel 8f8fa0248c converted lock after "time" to "idle time" 2021-06-02 15:53:57 +02:00
Sebastian Stenzel cd7acea54e Use of NumericTextField 2021-06-02 14:52:27 +02:00
Sebastian StenzelandGitHub 18152e4f3d Merge branch 'develop' into feature/autoLock 2021-06-02 14:31:28 +02:00
7b689c6b78 Keychain is locked (#1550)
Fixes #1526

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
Co-authored-by: Sebastian Stenzel <sebastian.stenzel@gmail.com>
2021-06-02 14:24:51 +02:00
Armin SchrenkandGitHub 1159681824 Merge pull request #1615 from cryptomator/feature/sanitizer
Add Vault Health Check

Fixes #312 and fixes #1224.
2021-06-01 18:09:22 +02:00
Armin Schrenk c868ac32cc Change localized text and tweak ui 2021-06-01 18:01:54 +02:00
Sebastian Stenzel f85f953668 Bind countOfWarnSeverity and countOfCritSeverity to list 2021-06-01 15:01:23 +02:00
Armin Schrenk 5185df631d rather add functionality to formattedLabel class than to extend it 2021-06-01 13:22:56 +02:00
Armin Schrenk 617feec111 deactivate fix button 2021-06-01 13:14:31 +02:00
Armin Schrenk eb196165c5 move healthcheck button to vault options 2021-06-01 11:53:33 +02:00
Armin Schrenk 8c37ab3675 Closes #961 2021-06-01 10:45:48 +02:00
Armin Schrenk 7eaade1555 Depeding on state of selected task, show appropiate status text in check detail view 2021-06-01 10:29:41 +02:00
Armin Schrenk 186c129a30 Replace stub check detail description with info about check duration and count of warnings and critical errors 2021-05-31 15:28:30 +02:00
Armin Schrenk f74b9d3347 Remove Scope 2021-05-26 15:50:50 +02:00
Armin Schrenk 6038559172 Localization and small ui fixes 2021-05-26 15:50:03 +02:00
Armin Schrenk 98a899c7e2 scope checklist cell and properly update graphic property 2021-05-25 13:21:13 +02:00
Armin Schrenk 783c8718c1 add localization for health check names 2021-05-25 13:20:18 +02:00
Armin Schrenk b47382811f apply ugly workaround for new vault config loading 2021-05-25 12:37:29 +02:00
Armin Schrenk 62f279b9f7 Merge branch 'develop' into feature/sanitizer 2021-05-25 11:16:27 +02:00
Armin Schrenk ab961f1a8b update thrid party licences 2021-05-25 11:16:17 +02:00
Armin Schrenk 9562db7e13 Closes 1659 2021-05-25 11:10:03 +02:00
Armin SchrenkandGitHub 1ca2e029dc method loadVaultConfig() throws exception instead of returning optional (#1661) 2021-05-21 18:51:49 +02:00
Sebastian Stenzel 944e3e6c50 placeholder: use identifier to look up human-readable names from ResourceBundle instead of if/elsing on classes
(e.g. `resourceBundle.getString(identifier() + ".name")`)

TODO @infeo
2021-05-21 17:55:39 +02:00
Sebastian Stenzel e134d2f422 keep more ui code in fxml 2021-05-21 17:48:20 +02:00
Sebastian Stenzel 04e5170403 * Made DiagnosticResultAction a "pseudo-singleton" that consumes diagnostic results
* renamed FxmlFile.HEALTH_CHECK to match fxml file name
* added missing DI scopes
* simplified cell layout
2021-05-21 17:47:42 +02:00
Armin Schrenk ea008df9f7 Add error screen to vault check start screen 2021-05-21 16:25:37 +02:00
Armin Schrenk e25dfaad35 clean up 2021-05-21 15:59:43 +02:00
Armin Schrenk e98f92c63f Only use CheckTask title in UI instead of HealthCheck::identifier 2021-05-21 15:56:32 +02:00
Armin Schrenk 85b761a9d7 show error screen when unable to write report 2021-05-21 15:29:14 +02:00
Armin Schrenk be8dcd4dc4 remove obsolete TODOs 2021-05-21 13:41:19 +02:00
Armin Schrenk c01c405f20 add select all checkbox 2021-05-21 13:16:14 +02:00
Armin Schrenk b4f268f551 more ui & clean up 2021-05-21 13:15:51 +02:00
Armin Schrenk 21f9e72cc5 preselect correct check in check screen 2021-05-21 12:48:58 +02:00
Armin Schrenk 4845984a35 ui sugar
* show correct name for health check tasks
* some padding
* resize window when run button is hit
2021-05-21 12:45:00 +02:00
Armin Schrenk b6f5ddaf30 Only show button when severity is WARN 2021-05-21 12:40:44 +02:00
Armin Schrenk 1abf0e1bfa Replace result table by result list 2021-05-21 12:00:46 +02:00
Armin Schrenk a8364a8c1a show directory containing exported health report 2021-05-20 16:52:28 +02:00
Armin Schrenk 13c95868fa fix report writer. Ignore not run tests 2021-05-20 16:46:34 +02:00
Armin Schrenk 85ff7542f1 rework gui to be more wizard like:
* show first only checkbox list of checks and single run button
* then show list of check run status with results
2021-05-20 16:35:37 +02:00
Armin Schrenk 432be6dd80 more ui alignments 2021-05-19 13:18:54 +02:00
Armin Schrenk ca0402aaf0 clear styleclass of icon view when content of ResultSeverityCell changes. 2021-05-19 12:38:04 +02:00
Armin Schrenk c7ef0056f9 let UI take space if window size is increased 2021-05-19 09:47:38 +02:00
Armin Schrenk cf13311665 add style class to used icon in result view 2021-05-19 09:32:55 +02:00
Armin Schrenk a7133dbebe Add icon to severity column 2021-05-18 17:12:37 +02:00
Armin Schrenk 168f9b9fb9 fix report writer 2021-05-18 16:57:25 +02:00
Armin Schrenk 2b332c3831 Add column with action button 2021-05-18 16:53:48 +02:00
Armin Schrenk 66e3625a0b Wrap diagnostic result in object to prepare it for possible fix action 2021-05-18 16:49:39 +02:00
Armin Schrenk 20f88e203a simplification and cleanup 2021-05-18 13:40:56 +02:00
Armin Schrenk 66a8dc9811 Revert d2680b9857 2021-05-18 13:36:00 +02:00
Armin Schrenk 9d89efc98c Unify interface:
* move all buttons to one button bar
* switch between runAll and runSelected, depending on list selection
* add a little localization
2021-05-18 09:09:53 +02:00
Armin Schrenk dfbe017316 Partially revert 8e1be4c 2021-05-18 08:47:40 +02:00
Armin Schrenk d2680b9857 Rename controller and fxml file which represents the whole health check screen 2021-05-17 13:06:28 +02:00
Armin Schrenk 8e1be4c67b remove redundant property, fix logic error 2021-05-17 12:49:08 +02:00
Armin Schrenk 749e0ac02d disable export-button as long as no batch run is finished 2021-05-17 12:40:29 +02:00
Armin SchrenkandGitHub a2ad7d69ab Simplify password creation (#1646)
* Remove the password properties and directly access password fields from included fxml files
* wipe the password fields when closing a window
2021-05-17 10:18:10 +02:00
Sebastian Stenzel a47c05c87d hide/disable elements if not relevant in current state 2021-05-10 15:56:46 +02:00
Sebastian Stenzel 6b871e56c8 hide results until task is started 2021-05-10 15:38:52 +02:00
Sebastian Stenzel 40dbc0ba48 cleanup 2021-05-10 15:38:37 +02:00
Sebastian Stenzel 2e52afed51 write report synchronously, reducing complexity 2021-05-10 13:39:46 +02:00
Sebastian Stenzel 64d54133f8 split up check view:
- checkListController is responsible for selecting and (batch)running checks
- checkDetailController is responsible for displaying results
2021-05-07 15:51:47 +02:00
Sebastian Stenzel ac289cc3f7 Merge branch 'develop' into feature/sanitizer
# Conflicts:
#	main/pom.xml
2021-05-07 09:20:54 +02:00
Armin Schrenk 189699d359 sh*te happens
check value instead reference for null
2021-05-06 15:44:27 +02:00
Sebastian StenzelandGitHub 0ce41e7ac3 New Crowdin updates (#1628)
New translations strings.properties

Japanese; Polish; Spanish; Dutch; Czech; Norwegian Bokmal; Korean; Greek; Catalan; Slovak; Russian; Punjabi; Italian; Arabic; Bosnian; Hungarian; German; Cyrillic); Latin); Romanian; French; Chinese Simplified; Croatian; Chinese Traditional; Portuguese; Latvian; Hindi; Indonesian; Turkish; Portuguese, Brazilian; Swedish; Norwegian Nynorsk; 

[ci skip]
2021-05-06 14:25:59 +02:00
Armin SchrenkandGitHub c1a249fd7f Merge pull request #1454 from cryptomator/feature/vault-format-8
Vault Format 8
2021-05-06 14:05:26 +02:00
Armin Schrenk c266c7583b bump dependencies (same as in cryptofs) 2021-05-06 13:59:37 +02:00
Armin Schrenk 00331d4857 check for vaultpath existence instead of catching (undocumented) exception 2021-05-06 13:51:30 +02:00
Armin Schrenk 8fd484e2bb Revert "Move window placements of "popup dialogs" to their corresponding controller"
This reverts commit bc83e23a34.
2021-05-06 13:44:05 +02:00
Armin Schrenk 165d740acd if vault directory is not present, declare it as missing 2021-05-06 12:51:39 +02:00
Armin Schrenk fd4010c6c9 cleanup 2021-05-06 12:43:15 +02:00
Armin Schrenk aa2900fa9e ensure to use cryptolib rc 2021-05-06 12:27:07 +02:00
Sebastian Stenzel 76d1875e01 adjusted to new CryptoFileSystemProvider.checkDirStructureForVault API 2021-05-06 09:56:50 +02:00
Armin Schrenk bc83e23a34 Move window placements of "popup dialogs" to their corresponding controller 2021-05-05 15:03:09 +02:00
Armin Schrenk 6a326bcbce remove password of migration window, when window is closed 2021-05-04 10:51:12 +02:00
Armin Schrenk 573ad03d68 bug fixin' 2021-05-03 17:33:28 +02:00
Sebastian Stenzel 07efb3e94d Merge branch 'feature/vault-format-8' into feature/sanitizer
# Conflicts:
#	main/pom.xml
2021-05-03 12:34:28 +02:00
Armin Schrenk 89e680fb45 reveal report storage location when task is finished. 2021-04-30 11:42:31 +02:00
Armin Schrenk 54128a0016 Refined exportet health report 2021-04-30 11:32:56 +02:00
Armin Schrenk e2565097cd add function to export a report of health checks 2021-04-29 16:31:39 +02:00
Armin Schrenk f827b1fc89 Refactor HealthCheckTask execution:
* new class HealthCheckSupervisor which encapsulates execution of all selected health checks
* checkControllor only users supervisor
2021-04-29 16:31:05 +02:00
Armin Schrenk c0baab2329 change comments 2021-04-28 09:58:14 +02:00
Armin Schrenk 70baa4d09f Rework sanitizer workflow:
* select set of performed HealthChecks prior to loading masterkey
* two predefined healthcheck sets and custom one
* start sequential execution of checks after successful masterkey loading
* removed vault config info
* destroy masterkey on window close, on cancellation, on check finish
2021-04-27 17:41:28 +02:00
Armin Schrenk d710d406d0 Merge branch 'master' into develop 2021-04-23 10:30:12 +02:00
Armin Schrenk 554f245f5d Merge branch 'hotfix/1.5.16' 2021-04-23 10:27:48 +02:00
Sebastian Stenzel 34995088ba addressed some issues identified during code review 2021-04-23 10:24:31 +02:00
Sebastian Stenzel 69c63702f2 pass a single keyloader to cryptofs 2021-04-23 09:55:08 +02:00
Armin Schrenk db7e731554 bumping dokany-nio-adapter 2021-04-23 09:48:56 +02:00
Armin Schrenk e08b43bf17 preparing hotfix 1.5.16 2021-04-23 09:48:40 +02:00
Sebastian Stenzel 58b45bacd1 Merge branch 'develop' into feature/vault-format-8
[ci skip]
2021-04-21 13:27:54 +02:00
Sebastian Stenzel 4f942bc23f reset version
[ci skip]
2021-04-21 13:27:25 +02:00
Armin Schrenk 09a8618fa0 Merge tag '1.5.15' into develop
1.5.15
2021-04-21 12:26:46 +02:00
Armin Schrenk 52af530d1b Merge branch 'release/1.5.15' 2021-04-21 12:26:45 +02:00
Armin Schrenk 86a42234c6 preparing 1.5.15 2021-04-21 12:21:50 +02:00
Armin Schrenk 37c4e78b1d update third party licenses
[ci skip]
2021-04-21 12:05:37 +02:00
Sebastian StenzelandGitHub 9c2c234bee New Crowdin updates (#1604)
New translations strings.properties

Slovak; Czech; Italian; French; Catalan; Romanian; Polish; Chinese Simplified; Dutch; Chinese Traditional;
Norwegian Nynorsk; Croatian; Turkish; German; Spanish; Russian; Hungarian; Punjabi; Portuguese;
Norwegian Bokmal; Indonesian; Hindi; Bosnian; Cyrillic); Swedish; Greek; Korean; Portuguese, Brazilian; Latin); Arabic; Latvian; Japanese; 

[ci skip]
2021-04-21 12:01:09 +02:00
Sebastian Stenzel 59f91267ae fix save password 2021-04-21 11:20:44 +02:00
Sebastian Stenzel b3ff9423b4 Merge branch 'develop' into feature/vault-format-8
# Conflicts:
#	main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
#	main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
#	main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
2021-04-21 11:20:31 +02:00
Armin Schrenk e3f3090341 bump javafx to version 16 2021-04-21 11:08:42 +02:00
Armin Schrenk 83be6c0864 enhanced release template for ci 2021-04-21 10:41:58 +02:00
Sebastian Stenzel c654951dc1 removed ambiguous filenameLengthLimit from settings, added maxCleartextFilenameLength, shortening is now a 100% independent setting stored in the vault rather than the device settings 2021-04-20 18:15:25 +02:00
Armin Schrenk 4e177c9ea7 Correcting misleading log message 2021-04-20 16:53:59 +02:00
Armin Schrenk dc3a951a1b add hotkeys to remove a vault 2021-04-20 15:07:14 +02:00
Armin Schrenk defa9c75eb add hotkeys for vault selection 2021-04-20 12:18:15 +02:00
Nour AghaandGitHub c1f498a114 Add MEGA & pCloud providers to vault location presets (#1622)
Fixes #1621
2021-04-20 08:09:17 +02:00
Armin Schrenk 8457c50ebc add muted style class to status text of newVaultLocationScreen 2021-04-19 16:11:28 +02:00
Armin SchrenkandGitHub 0144cbb99f Merge pull request #1618 from cryptomator/feature/#1508-observable-mounts
Closes #1508
2021-04-16 16:27:40 +02:00
Armin SchrenkandGitHub d6e4c7d177 Rework choose-location-screen in add vault wizard: (#1620)
* more checks for the chosen vault path
* every check has own error message
* perform checks when vault path changes
* if any radio button selected, enable vault path field (no-edit)
2021-04-16 16:18:01 +02:00
3376b16b7b Rename Donation Key to Supporter Certificate (#1613)
Renamed all occurences of donation key to supporter certificate and adjust ui.

Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2021-04-16 12:18:03 +02:00
Sebastian Stenzel 673fdcd095 stupid. 2021-04-15 13:45:20 +02:00
Armin Schrenk 0d00520ac1 Throw specifc exception on not completed lock of a vault 2021-04-15 12:54:19 +02:00
Armin Schrenk 22a0d3a9a5 bump fuse/dokany-nio versions 2021-04-15 11:11:05 +02:00
Sebastian Stenzel 24baa44e70 stronger encapsulation of vault state await/signal mechanism 2021-04-15 10:30:29 +02:00
Armin Schrenk 03886f88e8 Fix lock workflow for webdav:
* internally, wait for condition that onExit-Method is exceuted (with timeout)
* store and execute onExitAction also for webdav
2021-04-15 10:14:28 +02:00
Armin Schrenk 642816b631 rebuild tray menu when vaultname changes 2021-04-14 17:26:04 +02:00
Sebastian Stenzel f3a03c71ec implemented cancellable health checks 2021-04-14 10:20:34 +02:00
Sebastian Stenzel 1fab246fcd ugly PoC for new UI 2021-04-13 15:43:59 +02:00
Armin Schrenk cd5c55aad7 Refactor lock/unlock workflows:
* don't set vault state on successful lock workflow
* improved error handling
2021-04-13 11:22:42 +02:00
Armin Schrenk b066b4b045 opening a vault in read-only always assumes a filename length limit of 220
references #1605
2021-04-12 18:06:09 +02:00
JaniruTEC 41d2a2c77e Added .idea/uiDesigner.xml to .gitignore 2021-04-10 23:16:00 +02:00
Sebastian Stenzel 69186b916c adjusted to new async health check api 2021-04-09 16:04:52 +02:00
Armin Schrenk 0840695e0a Refactor lock/unlock convinience methods in FxApplication:
* execute vault state transition here
* on failed transition show error window
* only start worfklow on successful transition
2021-04-08 17:28:49 +02:00
Armin Schrenk 8447f105b0 rename instance variables 2021-04-08 13:08:05 +02:00
Sebastian Stenzel c306d8df04 alternative thread-safe vault state without requiring explicit synchronization 2021-04-08 11:23:57 +02:00
Armin Schrenk beba6490c3 Add locking mechanism to change the vault state t 2021-04-06 13:26:58 +02:00
Armin Schrenk fb1078b35b bump to SNAPSHOT nio-adapter and refactor Volume.mount() method:
* returns void
* add onExitAction parameter
* adjust classes
2021-04-06 10:05:32 +02:00
Armin Schrenk be0912e6ca Merge branch 'develop' into feature/#1508-observable-mounts 2021-04-06 09:00:05 +02:00
Sebastian Stenzel 13db1aa9b9 Actually run tests from within Sanitizer UI 2021-04-01 17:40:53 +02:00
Sebastian Stenzel d2cd2c923d unlock for health check 2021-04-01 13:26:33 +02:00
Martin Beyer 92de05b3db Changed textfield to only accept Integers, Using scheduled Executor as Timer 2021-04-01 11:42:41 +02:00
Sebastian Stenzel c1000d9958 Merge branch 'feature/vault-format-8' into feature/sanitizer 2021-04-01 09:26:21 +02:00
Sebastian Stenzel 2810c044ea reduced visibility 2021-03-31 16:23:54 +02:00
Sebastian Stenzel e09bd160b7 Made masterkey loading strategies reusable 2021-03-31 16:12:31 +02:00
Sebastian Stenzel e6d1e4697c Merge develop into feature/vault-format-8 2021-03-31 11:39:16 +02:00
Sebastian Stenzel 497e5ce73f Add health check window 2021-03-31 11:37:55 +02:00
Armin Schrenk cf7cbae567 Set dev branch back to SNAPSHOT version
[ci skip]
2021-03-31 11:32:06 +02:00
Armin Schrenk 803f517c62 Merge tag '1.5.14' into develop
1.5.14
2021-03-31 11:11:34 +02:00
Armin Schrenk 0afa7b8e37 Merge branch 'release/1.5.14' 2021-03-31 11:11:27 +02:00
Armin Schrenk 1a0f70f8e8 updating third-party-licenses 2021-03-31 10:59:21 +02:00
Armin Schrenk 8737eb83f0 Bumping webdav-nio-adapter dependency 2021-03-31 10:58:23 +02:00
Armin Schrenk a96239a19f closes #1245 2021-03-31 10:52:30 +02:00
Armin Schrenk 2b7cfcd1dc preparing 1.5.14 2021-03-31 09:52:24 +02:00
Sebastian StenzelandGitHub ebccb61750 New Crowdin updates (#1560) 2021-03-31 09:46:45 +02:00
Armin Schrenk 285f2aec23 unify wording
[ci skip]
2021-03-30 16:10:24 +02:00
Armin SchrenkandGitHub 217e31fbd7 Merge pull request #1595 from cryptomator/feature/MCGA
Feature: Make Context menu Great Again
* adding additional entries
* fixes #1478
* only appears on a selected vault
2021-03-30 16:03:41 +02:00
Sebastian Stenzel f0ebf7a638 EasyBind rulez 2021-03-30 14:20:05 +02:00
Armin Schrenk c05c5e3f90 closes #961 2021-03-30 13:57:21 +02:00
Sebastian Stenzel 30f0c5e697 split VaultList and ContextMenu into two separate controllers 2021-03-30 13:43:37 +02:00
Armin Schrenk f989b8627c Update java-jwt 2021-03-30 13:10:54 +02:00
Armin Schrenk ff84230566 structure constructor 2021-03-30 11:53:30 +02:00
Armin Schrenk 3b4f6276b5 Improve selection model of list view:
* Clear selection if an empty space is clicked
* only open context menu if an item is selected
2021-03-30 11:25:22 +02:00
Armin Schrenk 2be7a050a4 refine wording
[ci skip]
2021-03-30 09:47:11 +02:00
Martin Beyer 1c01228778 Removing LockOnSleep UI elements (not implemented yet) 2021-03-29 19:29:17 +02:00
Martin Beyer 1c35780d9e Cleaned up integrations implementation and added Regex for input 2021-03-29 19:10:05 +02:00
Armin Schrenk 96a612127c add reveal entry to context menu. 2021-03-29 16:23:24 +02:00
Armin Schrenk 0bdfb7c9f9 Distinct between "unlock" and "unlock now" in context menu:
* inject keychainManager
* bind/unbind passphraseStored property to selected vault
* rename bindings to show possible action
* removed unused binding
* initialize properties properly
2021-03-29 12:59:48 +02:00
Armin Schrenk f556301249 fix suggestion 2021-03-29 12:47:46 +02:00
f77b237e59 simplify private function
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2021-03-29 12:44:57 +02:00
Armin Schrenk 61b7a39aad change level of a log message from warn to debug
[ci skip]
2021-03-29 10:53:48 +02:00
Martin Beyer 2cee6e45f1 Merge remote-tracking branch 'upstream/develop' into feature/autoLock 2021-03-29 10:45:32 +02:00
Martin Beyer 1292548042 Implementing new settings and locking after a certain amount of minutes 2021-03-29 10:44:39 +02:00
Tobias HagemannandGitHub 16bd84ee62 Update README.md
[ci skip]
2021-03-26 10:46:49 +01:00
Tobias HagemannandGitHub 3d0a97fcdf Update README.md
[ci skip]
2021-03-25 17:06:15 +01:00
Elvys Soares 5ae2419e6d Improved grouped assertions
Signed-off-by: Elvys Soares <eas5@cin.ufpe.br>
2021-03-23 21:46:32 -03:00
Armin Schrenk 786d156b9f change wording in context menu entries 2021-03-23 15:08:23 +01:00
Armin Schrenk 629b6fb97d execute Service tasks on application thread 2021-03-23 12:53:17 +01:00
Armin Schrenk 17dc32bb79 lock vault on external unmount 2021-03-23 12:52:38 +01:00
Armin Schrenk c05e00d32a Change volume interface to observe mounts 2021-03-23 12:37:36 +01:00
Armin Schrenk 9dc8b2cb47 Use Bindings instead of properties 2021-03-23 10:50:04 +01:00
Armin Schrenk 0e32e96c7d made context menu great again:
* added entry to unlock / lock a vault
* added entry to show vault options
2021-03-22 17:10:45 +01:00
Armin Schrenk 97afadd7b9 fixes #1478 2021-03-22 12:58:45 +01:00
Armin Schrenk b199b65e38 closes #176 2021-03-22 12:51:07 +01:00
Tobias HagemannandGitHub 999abf3c13 Update README.md
[ci skip]
2021-03-22 12:24:54 +01:00
Sebastian Stenzel 2cdde54db6 updated mockito due to build errors caused by bytebuddy 2021-03-17 13:14:55 +01:00
Sebastian Stenzel 6941603cdd update to latest cryptolib/cryptofs beta 2021-03-17 13:03:55 +01:00
Sebastian Stenzel cdca4e047d Merge branch 'develop' into feature/vault-format-8 2021-03-17 12:55:47 +01:00
Sebastian StenzelandGitHub 31e938de6a Merge pull request #1587 from cryptomator/feature/jdk-16
Update to JDK 16
2021-03-17 12:29:29 +01:00
Sebastian Stenzel 4c10ab764a updated README 2021-03-17 09:33:43 +01:00
Sebastian Stenzel 4e075ab0ca using pattern-matching instanceof where applicable 2021-03-17 09:22:16 +01:00
Sebastian Stenzel f73ae9759f use new Stream.toList() where applicable 2021-03-17 09:16:44 +01:00
Sebastian Stenzel f6283b2f7e using text blocks where applicable 2021-03-17 09:12:54 +01:00
Sebastian Stenzel b23d2e4def allow use of reflection in codacy coverage reporter 2021-03-17 08:19:58 +01:00
Sebastian Stenzel 91a2943599 update to JDK 16 2021-03-17 08:16:26 +01:00
Sebastian Stenzel e32ce22d24 Merge branch 'develop' into feature/vault-format-8 2021-03-10 12:48:58 +01:00
Armin Schrenk e75c415b46 Load vault config on demand and don't cache it
* fixes issue with unlock after vault migration
2021-03-10 12:03:46 +01:00
Martin Beyer 8592e3667b Applying a SystemListener when the notify gets called with SLEEP 2021-03-09 18:55:35 +01:00
Martin Beyer 5a11d02fa8 Splitting the "Lock after X minutes" Checkbox in to two string resources. 2021-03-09 18:52:53 +01:00
Sebastian Stenzel 0ab28602d1 bumped cryptofs version 2021-03-05 17:33:58 +01:00
Sebastian Stenzel 62c8edff04 Choose key loading workflow depending on vaultconfig's key ID and allow KeyLoadingComponent to decide itself, what exceptions it can handle 2021-03-03 17:41:17 +01:00
Sebastian Stenzel d01c6268f8 remove unused field
[ci skip]
2021-03-02 16:16:24 +01:00
Sebastian Stenzel e083fd0bcc skip certain steps on forks 2021-03-02 11:55:20 +01:00
ff687f1bae fix: main/pom.xml to reduce vulnerabilities (#1569)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JAVA-ORGECLIPSEJETTY-1080611

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2021-03-02 08:43:10 +01:00
Sebastian Stenzel 557aaa2480 Removed unused imports
[ci skip]
2021-03-01 15:59:13 +01:00
Sebastian Stenzel 78e43d401d split package org.cryptoamtor.ui.unlock to allow for different kinds of unlock workflows 2021-03-01 15:58:00 +01:00
Sebastian Stenzel 7b08c5d287 Merge branch 'develop' into feature/vault-format-8
# Conflicts:
#	main/pom.xml
#	main/ui/src/main/resources/license/THIRD-PARTY.txt
[ci skip]
2021-02-26 10:10:50 +01:00
Sebastian Stenzel 84d2644f19 fix wrong versions that have been merged from main branch 2021-02-26 10:05:26 +01:00
Martin Beyer d13dfe3526 First take at UI implementation of a Auto-Lock Tab 2021-02-24 16:23:49 +01:00
Armin Schrenk f9a9d7b870 Merge tag '1.5.13' into develop
1.5.13
2021-02-24 16:21:07 +01:00
Armin Schrenk d75a8c4bba Merge branch 'release/1.5.13' 2021-02-24 16:21:06 +01:00
Martin Beyer 8fccb1b8f0 Added some Settings 2021-02-24 16:20:34 +01:00
Armin Schrenk d326c7c990 preparing 1.5.13 2021-02-24 16:17:35 +01:00
Armin SchrenkandGitHub e8e63ca4fc Merge pull request #1517 from cryptomator/feature/translations
New Crowdin updates
2021-02-24 16:12:30 +01:00
Martin Beyer c82e5577a1 New Tab AutoLock in VaultOptions 2021-02-24 15:17:23 +01:00
Armin SchrenkandGitHub 6878f2e94b Merge pull request #1556 from cryptomator/feature/win-auto-theme
Feature/win auto theme
2021-02-24 14:31:17 +01:00
Armin Schrenk a17b416262 only remove system theme change listener, if one was previously registered. 2021-02-24 12:54:56 +01:00
Armin Schrenk fed572694f Merge branch 'develop' into feature/win-auto-theme
# Conflicts:
#	main/pom.xml
2021-02-24 12:25:04 +01:00
Armin Schrenk fe3d67d937 update build workflow 2021-02-24 12:21:13 +01:00
Armin Schrenk 3dcdeb1033 refactor pom to store targeted jdk build version in variable 2021-02-24 12:19:12 +01:00
Armin Schrenk 0898158c5a remove bintray as source repository in pom 2021-02-24 12:18:38 +01:00
Armin Schrenk 81091d4bdf bump integrations-* repos and implement new interface methods 2021-02-24 12:18:24 +01:00
Martin Beyer c9b1b1baa5 Fixes #1291 (Windows Automatic Theme) 2021-02-23 17:26:50 +01:00
Tobias Hagemann c81f3bd972 updated dependencies 2021-02-23 13:43:07 +01:00
Sebastian Stenzel 9f86b74320 New translations strings.properties (Greek)
[ci skip]
2021-02-23 09:32:09 +01:00
Sebastian Stenzel 2d8390a0f3 New translations strings.properties (Chinese Simplified)
[ci skip]
2021-02-22 11:47:50 +01:00
Sebastian Stenzel 8d3671175f New translations strings.properties (Swedish)
[ci skip]
2021-02-21 16:16:10 +01:00
Sebastian Stenzel 366011a222 New translations strings.properties (Swedish)
[ci skip]
2021-02-21 15:45:39 +01:00
Sebastian Stenzel c0c2e21055 New translations strings.properties (Korean)
[ci skip]
2021-02-20 02:29:34 +01:00
Sebastian Stenzel 63be96a151 New translations strings.properties (Korean)
[ci skip]
2021-02-20 01:58:05 +01:00
Sebastian Stenzel fc1df4bd99 New translations strings.properties (Catalan)
[ci skip]
2021-02-19 21:46:49 +01:00
Sebastian Stenzel 7657330931 New translations strings.properties (German)
[ci skip]
2021-02-19 13:17:33 +01:00
Sebastian Stenzel 4e44de3c3b New translations strings.properties (Portuguese, Brazilian)
[ci skip]
2021-02-19 01:07:24 +01:00
Sebastian Stenzel 8d902a72ca New translations strings.properties (Turkish)
[ci skip]
2021-02-18 19:39:40 +01:00
Sebastian Stenzel 7378031a72 New translations strings.properties (Spanish)
[ci skip]
2021-02-18 17:15:20 +01:00
Sebastian Stenzel f898c07c27 New translations strings.properties (German)
[ci skip]
2021-02-18 16:41:50 +01:00
Sebastian Stenzel c8f02d1045 New translations strings.properties (Czech)
[ci skip]
2021-02-18 14:56:07 +01:00
Sebastian Stenzel 4cc13b986b New translations strings.properties (Slovak)
[ci skip]
2021-02-18 14:22:12 +01:00
Sebastian Stenzel af7b6780f3 New translations strings.properties (Italian)
[ci skip]
2021-02-18 14:22:10 +01:00
Sebastian Stenzel 4a17d01004 New translations strings.properties (French)
[ci skip]
2021-02-18 13:42:57 +01:00
Sebastian Stenzel 1d68408e21 New translations strings.properties (Japanese)
[ci skip]
2021-02-18 13:42:49 +01:00
Sebastian Stenzel 821b1ee660 New translations strings.properties (Russian)
[ci skip]
2021-02-18 13:42:45 +01:00
Sebastian Stenzel c6616bd467 New translations strings.properties (Slovak)
[ci skip]
2021-02-18 13:42:44 +01:00
Sebastian Stenzel 4bb2589f16 New translations strings.properties (Polish)
[ci skip]
2021-02-18 13:42:37 +01:00
Sebastian Stenzel 896c3b3565 New translations strings.properties (Norwegian Bokmal)
[ci skip]
2021-02-18 13:09:32 +01:00
Sebastian Stenzel 6ee717a693 New translations strings.properties (French)
[ci skip]
2021-02-18 13:09:31 +01:00
Sebastian Stenzel 07ec858e1e New translations strings.properties (Spanish)
[ci skip]
2021-02-18 13:09:29 +01:00
Sebastian Stenzel a7c1e43ebc New translations strings.properties (Arabic)
[ci skip]
2021-02-18 13:09:28 +01:00
Sebastian Stenzel ac1cd3b073 New translations strings.properties (Catalan)
[ci skip]
2021-02-18 13:09:27 +01:00
Sebastian Stenzel 7474519faf New translations strings.properties (Czech)
[ci skip]
2021-02-18 13:09:26 +01:00
Sebastian Stenzel 340e474c33 New translations strings.properties (German)
[ci skip]
2021-02-18 13:09:24 +01:00
Sebastian Stenzel c90ab75427 New translations strings.properties (Greek)
[ci skip]
2021-02-18 13:09:23 +01:00
Sebastian Stenzel 848b3f77d3 New translations strings.properties (Hungarian)
[ci skip]
2021-02-18 13:09:21 +01:00
Sebastian Stenzel fb8cdb31ca New translations strings.properties (Italian)
[ci skip]
2021-02-18 13:09:20 +01:00
Sebastian Stenzel ba5f8053fb New translations strings.properties (Japanese)
[ci skip]
2021-02-18 13:09:19 +01:00
Sebastian Stenzel 38165becca New translations strings.properties (Korean)
[ci skip]
2021-02-18 13:09:17 +01:00
Sebastian Stenzel f731ac5c90 New translations strings.properties (Dutch)
[ci skip]
2021-02-18 13:09:16 +01:00
Sebastian Stenzel 0291702736 New translations strings.properties (Polish)
[ci skip]
2021-02-18 13:09:15 +01:00
Sebastian Stenzel ab63929861 New translations strings.properties (Portuguese)
[ci skip]
2021-02-18 13:09:12 +01:00
Sebastian Stenzel ca36fb8af3 New translations strings.properties (Slovak)
[ci skip]
2021-02-18 13:09:11 +01:00
Sebastian Stenzel 8cb29ff83b New translations strings.properties (Swedish)
[ci skip]
2021-02-18 13:09:10 +01:00
Sebastian Stenzel 304fa67dd7 New translations strings.properties (Turkish)
[ci skip]
2021-02-18 13:09:09 +01:00
Sebastian Stenzel a80ec32d2c New translations strings.properties (Chinese Simplified)
[ci skip]
2021-02-18 13:09:07 +01:00
Sebastian Stenzel dc4f21162e New translations strings.properties (Chinese Traditional)
[ci skip]
2021-02-18 13:09:06 +01:00
Sebastian Stenzel 3b8d7e0241 New translations strings.properties (Portuguese, Brazilian)
[ci skip]
2021-02-18 13:09:05 +01:00
Sebastian Stenzel 6891c68cf1 New translations strings.properties (Norwegian Nynorsk)
[ci skip]
2021-02-18 13:09:01 +01:00
Sebastian Stenzel 18b26fbc58 New translations strings.properties (Latvian)
[ci skip]
2021-02-18 13:09:00 +01:00
Sebastian Stenzel 1db8da6b6b New translations strings.properties (Bosnian)
[ci skip]
2021-02-18 13:08:57 +01:00
Sebastian Stenzel 14bce7e91d New translations strings.properties (Russian)
[ci skip]
2021-02-18 13:08:56 +01:00
Sebastian Stenzel 2674bc6f23 New translations strings.properties (Punjabi)
[ci skip]
2021-02-18 13:08:55 +01:00
Tobias HagemannandGitHub 74e1d6f991 Merge pull request #1545 from cryptomator/feature/save-password-clarification
"Save Password" Clarification
2021-02-18 13:07:04 +01:00
Tobias Hagemann 25dba028f8 added deep-linking to vault options tabs, changed "password saved" label to hyperlink 2021-02-18 11:32:14 +01:00
Tobias Hagemann bf51cc13d1 renamed "save password" to "remember password", switched placements of "vault options" hyperlink/button and "password saved" label 2021-02-18 09:15:19 +01:00
Sebastian Stenzel 24be36cccb New translations strings.properties (Spanish)
[ci skip]
2021-02-15 18:19:27 +01:00
Sebastian Stenzel 797e15c775 New translations strings.properties (Chinese Traditional)
[ci skip]
2021-02-15 17:08:04 +01:00
Sebastian Stenzel 2395dbcae9 New translations strings.properties (Italian)
[ci skip]
2021-02-15 16:09:11 +01:00
Sebastian Stenzel efacbbe2b6 New translations strings.properties (Bosnian)
[ci skip]
2021-02-15 15:40:45 +01:00
Sebastian Stenzel 2518086e46 New translations strings.properties (Polish)
[ci skip]
2021-02-15 15:40:44 +01:00
Sebastian Stenzel c733247611 New translations strings.properties (Japanese)
[ci skip]
2021-02-15 15:40:42 +01:00
Sebastian Stenzel 4f44e71e13 New translations strings.properties (Czech)
[ci skip]
2021-02-15 15:40:40 +01:00
Sebastian Stenzel 02aa46ece8 New translations strings.properties (Bosnian)
[ci skip]
2021-02-15 15:09:58 +01:00
Sebastian Stenzel 635f64c847 New translations strings.properties (German)
[ci skip]
2021-02-15 15:09:56 +01:00
Sebastian Stenzel 0a8516ae9a New translations strings.properties (French)
[ci skip]
2021-02-15 15:09:55 +01:00
Sebastian Stenzel c1edb30472 New translations strings.properties (Bosnian)
[ci skip]
2021-02-15 14:41:51 +01:00
Sebastian Stenzel 93c78b63ca New translations strings.properties (Russian)
[ci skip]
2021-02-15 14:41:50 +01:00
Sebastian Stenzel 3ccae93757 New translations strings.properties (Norwegian Bokmal)
[ci skip]
2021-02-15 14:09:30 +01:00
Sebastian Stenzel 450dec2baf New translations strings.properties (French)
[ci skip]
2021-02-15 14:09:29 +01:00
Sebastian Stenzel 36121527ca New translations strings.properties (Spanish)
[ci skip]
2021-02-15 14:09:27 +01:00
Sebastian Stenzel f30451f7e1 New translations strings.properties (Catalan)
[ci skip]
2021-02-15 14:09:25 +01:00
Sebastian Stenzel 74d7f63f64 New translations strings.properties (Czech)
[ci skip]
2021-02-15 14:09:24 +01:00
Sebastian Stenzel 350a4ab762 New translations strings.properties (German)
[ci skip]
2021-02-15 14:09:23 +01:00
Sebastian Stenzel 0e0e75b89c New translations strings.properties (Greek)
[ci skip]
2021-02-15 14:09:22 +01:00
Sebastian Stenzel e76440d928 New translations strings.properties (Hungarian)
[ci skip]
2021-02-15 14:09:20 +01:00
Sebastian Stenzel 0cbace67a5 New translations strings.properties (Italian)
[ci skip]
2021-02-15 14:09:19 +01:00
Sebastian Stenzel 84a3082589 New translations strings.properties (Japanese)
[ci skip]
2021-02-15 14:09:17 +01:00
Sebastian Stenzel cdfcca06d1 New translations strings.properties (Korean)
[ci skip]
2021-02-15 14:09:16 +01:00
Sebastian Stenzel 630bfa4cee New translations strings.properties (Dutch)
[ci skip]
2021-02-15 14:09:15 +01:00
Sebastian Stenzel 90cb7beb1f New translations strings.properties (Polish)
[ci skip]
2021-02-15 14:09:14 +01:00
Sebastian Stenzel d37fa33278 New translations strings.properties (Slovak)
[ci skip]
2021-02-15 14:09:11 +01:00
Sebastian Stenzel eed9c906cc New translations strings.properties (Swedish)
[ci skip]
2021-02-15 14:09:09 +01:00
Sebastian Stenzel 2b7ed5f4dd New translations strings.properties (Turkish)
[ci skip]
2021-02-15 14:09:08 +01:00
Sebastian Stenzel 6a7a309a2b New translations strings.properties (Chinese Simplified)
[ci skip]
2021-02-15 14:09:07 +01:00
Sebastian Stenzel 0382069995 New translations strings.properties (Chinese Traditional)
[ci skip]
2021-02-15 14:09:05 +01:00
Sebastian Stenzel 7c5f40d7d1 New translations strings.properties (Portuguese, Brazilian)
[ci skip]
2021-02-15 14:09:04 +01:00
Sebastian Stenzel 09e9530660 New translations strings.properties (Russian)
[ci skip]
2021-02-15 14:08:57 +01:00
Sebastian Stenzel 947d25b335 New translations strings.properties (Punjabi)
[ci skip]
2021-02-15 14:08:56 +01:00
Sebastian Stenzel 80cbc6699a fixed label
[ci skip]
2021-02-15 13:53:05 +01:00
Sebastian Stenzel da0933fa92 New translations strings.properties (Punjabi)
[ci skip]
2021-02-08 04:36:28 +01:00
Sebastian Stenzel 9ad217ca55 Merge branch 'develop' into feature/vault-format-8
[ci skip]
2021-02-04 12:38:01 +01:00
Sebastian Stenzel adc9f32fb3 Merge branch 'master' into develop 2021-02-04 12:31:18 +01:00
Sebastian Stenzel 65369bdbff Merge branch 'release/1.5.12' 2021-02-04 12:27:42 +01:00
Sebastian Stenzel d18e618ef1 preparing 1.5.12 2021-02-04 12:27:02 +01:00
Armin Schrenk 970216dd59 Merge tag '1.5.12' into develop
1.5.12
2021-02-04 10:49:52 +01:00
Armin Schrenk 6840a649c7 Merge branch 'release/1.5.12' 2021-02-04 10:49:08 +01:00
Sebastian Stenzel 1a0bc92bde Merge branch 'develop' into feature/vault-format-8 2021-02-01 12:08:23 +01:00
Sebastian Stenzel e4709ed6fe Refactored FxmlFile and FxmlLoaderFactory 2021-02-01 12:06:38 +01:00
Sebastian Stenzel 21038212c1 remove unused property
[ci skip]
2021-02-01 11:54:21 +01:00
Sebastian Stenzel b15471b4ff add new (optional) "choose masterkey file" step to unlock dialog 2021-01-29 17:44:45 +01:00
Armin Schrenk 19ebc7e562 preparing 1.5.12 2021-01-27 15:30:56 +01:00
Armin SchrenkandGitHub 0fa052d2d1 Merge pull request #1518 from cryptomator/fix/1515-suppressed-messages
Fixed suppressed "invalid mountpoint" messages
2021-01-27 15:22:47 +01:00
Armin Schrenk 402861b9c0 Update third party licenses 2021-01-27 12:37:50 +01:00
Armin Schrenk aca1666dea closes #1479 2021-01-27 12:36:22 +01:00
Sebastian Stenzel ff17b60f56 Refactored UnlockWorkflow using new vault format 8 APIs 2021-01-27 12:09:09 +01:00
Sebastian Stenzel 3284578445 Merge branch 'develop' into feature/vault-format-8
# Conflicts:
#	main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
2021-01-25 21:42:13 +01:00
Sebastian Stenzel 70f6a4877c Make sure not to catch Errors 2021-01-25 21:38:46 +01:00
Sebastian Stenzel 85c5dc8dfb removed tmp workaround 2021-01-25 21:32:33 +01:00
Sebastian Stenzel 4b670a59a3 adjusted to new cryptolib/cryptofs API 2021-01-25 21:31:16 +01:00
JaniruTEC 23c113948f Replaced catch with wildcard catch and added additional logger call
See #1509 for further information and reasoning
2021-01-25 16:21:35 +01:00
JaniruTEC d86ea20c31 Fixed suppressed "invalid mountpoint" messages
Fixed #1515
2021-01-23 22:13:07 +01:00
Sebastian Stenzel 3f928cf958 Merge branch 'develop' into feature/vault-format-8
[ci skip]
2021-01-22 22:47:54 +01:00
Sebastian Stenzel 01929c2288 New translations strings.properties (Hungarian)
[ci skip]
2021-01-22 13:48:57 +01:00
Sebastian Stenzel 2634433b2c New translations strings.properties (Hungarian)
[ci skip]
2021-01-22 13:19:41 +01:00
Armin SchrenkandGitHub c1f44f76b9 Merge pull request #1512 from cryptomator/feature/refactor-reveal 2021-01-22 11:42:20 +01:00
Sebastian Stenzel b2f27c0a3d removed dead code
[ci skip]
2021-01-22 09:34:18 +01:00
Sebastian Stenzel b1d7cfc81b change scope of HostServiceRevealer to @FxApplicationScoped
because unlock always happens after application start anyway
2021-01-22 09:31:43 +01:00
Armin Schrenk 7cd5c66836 Refactor reveal() methods:
* new class HostServiceRevealer implemeting the Volume.Revealer-Interface
* this class is injected in vault service and always used for revealing
* removed Revealer parameter from several reveal methods
2021-01-21 17:25:23 +01:00
Armin Schrenk 95cef34234 remove unnecessary awtReavealer and trayModule 2021-01-21 16:09:16 +01:00
Armin Schrenk 8977440697 Rename RevealerFacade to Revealer 2021-01-21 15:29:04 +01:00
Armin Schrenk d5eb84a000 change reveal method in vault service:
* revealer is no member anymore
* reveal() takes as second argument revealer object
* several other classes hand over a revealer object
* added awt-revealer if application is not yet started
2021-01-21 15:26:35 +01:00
Sebastian Stenzel 81fb6b8794 get rid of RevealException 2021-01-21 15:06:37 +01:00
Armin Schrenk 92b390d5bb update third-party licenses 2021-01-21 12:44:32 +01:00
Armin Schrenk 8867532210 add javadoc 2021-01-21 12:44:11 +01:00
Armin Schrenk 77db435b4f Refactor reveal call stack to apply facade pattern. 2021-01-21 12:41:52 +01:00
Sebastian Stenzel cb2bc17283 New translations strings.properties (Hungarian)
[ci skip]
2021-01-21 11:06:22 +01:00
Tobias HagemannandGitHub aaa93239d0 Merge pull request #1516 from stacksnack/feature/translations
New translations strings.properties (Hungarian)
2021-01-21 11:03:27 +01:00
Sebastian Stenzel 93445e22d4 update library versions 2021-01-21 06:37:41 +01:00
stacksnackandGitHub 64897cfa21 New translations strings.properties (Hungarian)
Add hungarian translation
2021-01-20 21:37:41 +01:00
Armin Schrenk 7349a29831 Ensure that on windows systems always the gui chooses the mountpoint for webdav 2021-01-19 15:52:52 +01:00
Armin Schrenk 73554b4759 split WebDAV Volume mount method into servlet start and servlet mount 2021-01-19 15:52:20 +01:00
Armin Schrenk 0312f045aa add a logger to WebDAV-Volume 2021-01-19 15:28:50 +01:00
Armin Schrenk 1134c1b2ff closes #1471
hand the javafx hostservice showDocument() method from ui package to the underlying nio-adapter-libraries through
2021-01-19 15:27:46 +01:00
Sebastian Stenzel efebbc059a keep CTR+HMAC for now (until GCM is supported on all platforms) 2021-01-19 15:09:47 +01:00
Sebastian Stenzel 6e860d293a Merge branch 'develop' into feature/vault-format-8
[ci skip]
2021-01-19 15:05:15 +01:00
Armin Schrenk 8bb925fd93 fixes #1509 2021-01-18 18:41:21 +01:00
Sebastian Stenzel ca5d9eba36 New translations strings.properties (Norwegian Bokmal)
[ci skip]
2021-01-16 04:07:35 +01:00
Sebastian Stenzel 1a49e24d8a New translations strings.properties (Norwegian Bokmal)
[ci skip]
2021-01-16 03:37:26 +01:00
Sebastian Stenzel 24b0ed2502 New translations strings.properties (German)
[ci skip]
2021-01-14 18:18:01 +01:00
Sebastian Stenzel a226d5403a New translations strings.properties (Japanese)
[ci skip]
2021-01-14 14:02:17 +01:00
Sebastian Stenzel 0f3085cc73 fixes #1476 2021-01-14 09:56:57 +01:00
Sebastian Stenzel f0a040cf29 cleanup
[ci skip]
2021-01-14 09:56:36 +01:00
Sebastian StenzelandGitHub 0676748dde New Crowdin updates (#1466)
[ci skip]
2021-01-14 09:24:42 +01:00
Armin Schrenk 589b8384eb Only determine filenamelength limit if the the vault is mounted not read-only. 2021-01-11 14:15:37 +01:00
Julian RaufelderandGitHub 9e3947f337 Happy 2021 🎉 2021-01-11 10:08:35 +01:00
Tobias Hagemann 790cc4e772 simplified issue template
[ci skip]
2021-01-06 12:01:07 +01:00
Tobias Hagemann 72f4988632 fixes #1452 2021-01-05 14:14:14 +01:00
Sebastian StenzelandGitHub b3fa6bbf32 Merge pull request #1472 from cryptomator/feature/minimize
Add Minimize Button
2020-12-18 15:58:02 +01:00
Sebastian Stenzel c2dc487c79 make field volatile that might be modified concurrently 2020-12-18 13:37:23 +01:00
Sebastian Stenzel e74dd3be9e remove unused param 2020-12-18 13:36:16 +01:00
Sebastian Stenzel 1eeee61572 Add option to show minimize button despite tray icon being present
fixes #1179
2020-12-17 14:50:33 +01:00
Sebastian Stenzel 984b7a2d0e Removed @Named("trayMenuSupported") from FxApplication component 2020-12-17 14:46:52 +01:00
Sebastian Stenzel 45c714a123 Added checkbox in settings to start without a tray icon
references #1113, #1078, #1079, #1344
2020-12-16 16:53:49 +01:00
Sebastian Stenzel 96bb97d50a Added new setting showTrayIcon defaulting to new environment property cryptomator.showTrayIcon 2020-12-16 13:48:33 +01:00
Sebastian Stenzel 046acb9bbf mark fuse.experimental deprecated
[ci skip]
2020-12-16 13:33:50 +01:00
Armin SchrenkandGitHub ce4ec16c62 Update bug.md
Add info to also specify desktop environment on Linux systems in section system setup
2020-12-16 12:16:32 +01:00
Tobias HagemannandGitHub 96c8c9a9a9 Update bug.md 2020-12-16 09:07:39 +01:00
Tobias HagemannandGitHub 44db97327d Update feature.md 2020-12-16 09:06:11 +01:00
Sebastian Stenzel f441700c25 Merge branch 'master' into develop 2020-12-15 13:23:21 +01:00
Sebastian Stenzel 76b023bdbd Merge branch 'release/1.5.11' 2020-12-15 12:42:13 +01:00
Sebastian Stenzel 241d9781bb bump integrations-win version 2020-12-15 12:41:39 +01:00
Sebastian Stenzel ff94532560 Merge branch 'release/1.5.11' 2020-12-15 10:53:40 +01:00
Tobias Hagemann e949023321 updated third-party list [ci skip] 2020-12-15 10:37:27 +01:00
Sebastian Stenzel da31407d63 Preparing 1.5.11 2020-12-15 08:57:35 +01:00
Sebastian Stenzel 8c7c5da89a not sure what makes this "real" [ci skip] 2020-12-15 08:04:16 +01:00
Sebastian StenzelandGitHub e3a0e1ec5a New Crowdin updates (#1413)
[ci skip]
2020-12-14 17:38:49 +01:00
Sebastian Stenzel 4cee27d8dd bump webdav dependencies 2020-12-14 17:30:29 +01:00
Armin Schrenk b0fce66d77 Add OWASP Dependency-Check Maven Plugin
* only active if profile dependency check is active
* added suppresion.xml for false positives
* added tmp fix for jwt lib
2020-12-14 17:02:35 +01:00
Armin SchrenkandGitHub bf47ba0145 Merge pull request #1440 from cryptomator/feature/#1439-finish-migration
closes #1439
2020-12-14 15:41:52 +01:00
Armin Schrenk fc7b484c9a unwrap the Platform.runLater() statement because it is always executed on JavaFX thread 2020-12-14 15:34:03 +01:00
Armin Schrenk 4402c19d90 Simplifications and clean up 2020-12-14 11:47:02 +01:00
Armin Schrenk 4e89de9e47 activate force locking for dokany 2020-12-14 10:23:27 +01:00
Armin Schrenk a27c9d63d9 Bump dokany-nio-adapter version 2020-12-14 10:09:13 +01:00
Armin Schrenk da6aa0f002 bump integrations-win version 2020-12-11 16:23:57 +01:00
Armin Schrenk 0bf3533f45 Workaround for #1421 2020-12-11 13:25:02 +01:00
Sebastian Stenzel 295cb0ef6d fixes #1446, fixes #1415 2020-12-09 17:17:51 +01:00
Armin SchrenkandGitHub 6fac84e9c2 Replace "may" by "can"
... the end of a great and fierce war.
2020-12-09 15:39:49 +01:00
Sebastian Stenzel 77b2e67c49 updated labels 2020-12-09 15:16:45 +01:00
Armin SchrenkandGitHub c48c9b1568 Merge pull request #1416 from cryptomator/feature/#1228-forcedUnmountDialog
Feature/#1228 forced unmount dialog
2020-12-09 15:04:30 +01:00
Armin Schrenk 6da7eae2f8 Minor changes in lettering 2020-12-09 14:55:22 +01:00
Armin Schrenk aec56c48c5 Merge branch 'develop' into feature/#1228-forcedUnmountDialog
# Conflicts:
#	main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
2020-12-09 14:51:00 +01:00
Sebastian Stenzel c0a9a95e4f Adjusted to CryptoFS 2.0.0 2020-12-08 14:39:46 +01:00
Sebastian Stenzel 3b73544766 Simplified observation of visible stages 2020-12-08 12:56:44 +01:00
Sebastian Stenzel fbc471635a fixed test 2020-12-08 12:51:34 +01:00
Sebastian Stenzel 3c5b9ed3a6 remove rarely used, complex class 2020-12-08 12:51:11 +01:00
Sebastian Stenzel 8479122561 temp fix for vulnerability in upstream lib
[ci skip]
2020-12-08 08:30:16 +01:00
Tobias HagemannandGitHub 91c883d5cc Create config.yml 2020-12-07 18:11:25 +01:00
Sebastian StenzelandGitHub 1cd4da0796 Merge branch 'develop' into feature/#1228-forcedUnmountDialog 2020-12-07 17:55:29 +01:00
Armin SchrenkandGitHub 935aaccdae Update bug template
* Make introduction section more visible
* emphasize task to complete for successful bug ticket
2020-12-02 15:05:59 +01:00
Tobias Hagemann ab3dd779d2 partially reverted 900fdd7f, fixing "stutter" startup on macos 2020-12-02 14:59:17 +01:00
Armin Schrenk 1fbcf6d517 closes #1439 2020-11-27 15:53:33 +01:00
Armin SchrenkandGitHub 82538091c9 Merge pull request #1427 from cryptomator/feature/#1323-singleQuitWindow
Show only one quit dialogue at a time
2020-11-24 15:31:31 +01:00
Sebastian Stenzel 62ade6113b cleanup
[ci skip]
2020-11-24 15:07:23 +01:00
Sebastian Stenzel 4779bbf415 Don't bother FxApplication with stuff that is meant to be dealt with internally within QuitComponent 2020-11-24 15:04:09 +01:00
Armin Schrenk c6b786a771 Reworked implemenation by using dagger now:
* create and inject QuitResponse container
* QuitComponent manges now if quit window needs to be created
* controller only handles the most recent QuitResponse
2020-11-24 13:34:12 +01:00
Armin Schrenk 6381227897 Stylin:
* change css styleclass of dialogue headings to `label-large`
* remove newly introduced style class `label-medium`
2020-11-23 16:14:31 +01:00
Armin Schrenk e82167b5e6 correct wrong method name 2020-11-20 17:29:56 +01:00
Armin Schrenk dc4fd482b5 Filter any whitespaces when entering text in donationkey form. 2020-11-20 17:17:16 +01:00
Armin Schrenk a71f8b350e Show only one quit dialogue at a time 2020-11-20 16:12:49 +01:00
Armin Schrenk c9ec8fecef fix wrong method name 2020-11-20 16:09:31 +01:00
e72d32f2d1 Remove empty line
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2020-11-20 14:34:34 +01:00
2545aa2a7e Removed default clause from switch expression on enums
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2020-11-20 14:33:57 +01:00
Armin Schrenk 51aac15622 Changed forced lock description and clean up 2020-11-20 12:23:44 +01:00
Armin Schrenk afa0cfeafb Replace lock failed dialogue stub and integrate it into workflow 2020-11-20 12:23:18 +01:00
Armin Schrenk 19e24ba12c refactor structure of LockWorkflow class 2020-11-20 12:22:26 +01:00
Armin Schrenk c47ce2c730 clean up 2020-11-20 11:25:39 +01:00
Armin Schrenk 5d00b3dd76 activate ability to abort vault locking if it is mounted with dokany and still busy
references #1228
2020-11-19 17:20:08 +01:00
Armin Schrenk 4db57cc0dc Further improvements:
* adding Log messages
* change lock workflow to type of Task<Void>
* set vault state based solely on task state
* replaced dialogue stubs with real text
2020-11-19 16:31:16 +01:00
Armin Schrenk 432a9a27f1 Add forceLock Dialogue:
* integrate it in workflow if normal lock throws exception
* add stubs if also forced lock fails
2020-11-19 12:52:16 +01:00
Armin Schrenk 57bfa3276d Integrate new lockWorkflow into application (gui and tray) 2020-11-19 11:42:00 +01:00
Sebastian Stenzel 02fc9b263a re-added applicable choosers to exception text 2020-11-18 18:12:41 +01:00
Sebastian Stenzel 4c66f81736 simplified line 2020-11-18 18:09:30 +01:00
Sebastian Stenzel 2de151aebe use less generic name 2020-11-18 18:08:00 +01:00
Armin Schrenk c44911dcac Prepare strucutre and classes for lock workflow 2020-11-18 17:16:58 +01:00
Armin Schrenk bcf2a3d20c Setting default onFailed handler in lock tasks 2020-11-18 17:16:43 +01:00
JaniruTEC 422ce4a387 Moved actual choosing of MPCs to MountPointHelper 2020-11-18 16:27:43 +01:00
JaniruTEC 21387bd76c Renamed IrregularUnmountCleaner to MountPointHelper 2020-11-18 16:18:31 +01:00
JaniruTEC 52cd560cb2 Generifyed #chooseMountPoint() 2020-11-18 16:09:09 +01:00
JaniruTEC 6c440dfbbb Re-Added check for non-emtpy dirs 2020-11-18 15:53:33 +01:00
Sebastian Stenzel a074450452 Merge branch 'master' into develop 2020-11-18 15:03:37 +01:00
Sebastian Stenzel b781cf6f25 Merge branch 'hotfix/1.5.10' 2020-11-18 15:03:11 +01:00
Sebastian Stenzel 9b653f488b Merge branch 'develop' into hotfix/1.5.10 2020-11-18 15:02:33 +01:00
Armin Schrenk 17c580267f prevent race conditions 2020-11-18 14:35:16 +01:00
Sebastian Stenzel 490d1b8f87 cancel unlock workflow, if closing window with shortcut 2020-11-18 14:21:18 +01:00
Armin Schrenk b0ad86f16b Perform cleanup of tmp mount points dir only once. 2020-11-18 14:07:34 +01:00
Sebastian Stenzel df2b4ac086 cleanup 2020-11-18 13:17:35 +01:00
Sebastian Stenzel aee4ececba Merge branch 'fix/1409' into hotfix/1.5.10
fixes #1409
2020-11-18 13:16:36 +01:00
Sebastian Stenzel b2992aa6ae Merge branch 'fix/1408' into hotfix/1.5.10
fixes #1408

# Conflicts:
#	main/buildkit/pom.xml
#	main/commons/pom.xml
#	main/launcher/pom.xml
#	main/pom.xml
#	main/ui/pom.xml
2020-11-18 13:16:19 +01:00
Sebastian Stenzel 4c546d281a Bumped version 2020-11-18 13:15:33 +01:00
Sebastian Stenzel c90e445a67 Removed -Dcryptomator.mountPointsDir="/Volumes/" from macOS run profiles
[ci skip]
2020-11-18 13:10:01 +01:00
Sebastian Stenzel d3c2b0509e Lazily call IrregularUnmountCleaner from (and only from!) TemporaryMountPointChooser 2020-11-18 13:08:48 +01:00
Sebastian Stenzel c6d1c2ca6b added MacVolumeMountChooser and refactored "priority" of mount point choosers: now all priorities are set in MountPointChooserModule (as a map key) 2020-11-18 13:07:12 +01:00
Sebastian Stenzel 86dec80726 avoid weird iteration counter manipulation 2020-11-18 10:33:04 +01:00
JaniruTEC 9083976989 Fixed #1409 by addig an additonal check 2020-11-18 00:46:24 +01:00
JaniruTEC d9c5d76417 Fixed irregular quoting 2020-11-17 22:41:41 +01:00
JaniruTEC c19c3754c6 Fixed high-spirited concatenation 2020-11-17 22:27:41 +01:00
JaniruTEC 947e0e2369 Fixed #1408 2020-11-17 22:00:41 +01:00
JaniruTEC 6eca8f2e0c Added support for quote escaped values for mount arguments 2020-11-17 21:56:43 +01:00
Sebastian Stenzel 9f61ad1941 Merge branch 'master' into develop
[ci skip]
2020-11-17 17:03:40 +01:00
Sebastian Stenzel 41492a951a Merge branch 'release/1.5.9' 2020-11-17 17:02:14 +01:00
Tobias Hagemann 8ad20c27da Merge branch 'hotfix/1.5.8' 2020-08-28 15:57:54 +02:00
458 changed files with 9663 additions and 3908 deletions
+5 -4
View File
@@ -1,5 +1,6 @@
commit_message: "[ci skip]"
escape_special_characters: 0
commit_message: '[ci skip]'
files:
- source: /main/ui/src/main/resources/i18n/strings.properties
translation: /main/ui/src/main/resources/i18n/strings_%two_letters_code%.properties
- source: /src/main/resources/i18n/strings.properties
translation: /src/main/resources/i18n/strings_%two_letters_code%.properties
escape_quotes: 0
escape_special_characters: 0
+1 -1
View File
@@ -22,7 +22,7 @@ include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
* The use of sexual language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
+5 -13
View File
@@ -5,18 +5,12 @@ labels: type:bug
---
<!--
⚠️⚠️⚠️ READ CAREFULLY ⚠️⚠️⚠️
Please make sure to:
- Comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
- Search for existing similar issues first: https://github.com/cryptomator/cryptomator/issues?q=
Do you want to ask a QUESTION? Are you looking for SUPPORT?
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
By filing an issue, you are expected to comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
Of course, we also expect you to search for existing similar issues first! ;) https://github.com/cryptomator/cryptomator/issues?q=
⚠️ IMPORTANT: If you don't stick to this template, the issue will get closed. To proof that you read this, please remove the X from the following line:
⚠️ IMPORTANT: If you don't stick to this template, the issue will get closed.
-->
<!-- oooXooo -->
### Description
@@ -24,7 +18,7 @@ Of course, we also expect you to search for existing similar issues first! ;) ht
### System Setup
* Operating system and version: [Windows/macOS/Linux + Version]
* Operating system and version: [Windows/macOS/Linux + Version ( + Desktop Environment, if Linux)]
* Cryptomator version: [Shown in the settings]
* Volume type: [Dokany/FUSE/WebDAV, shown in the settings]
@@ -51,7 +45,6 @@ Of course, we also expect you to search for existing similar issues first! ;) ht
[Any additional information, log files, screenshots, configuration, or data that might be necessary to reproduce the issue.]
<!--
If you want to add the log file or screenshots, please add them as attachments. If your log file seems empty and doesn't show any errors, you may enable the debug mode first. Here is how to do that: https://community.cryptomator.org/t/how-do-i-enable-debug-mode/36
Then reproduce the problem to ensure all important information is contained in there. You may use test data or redact sensitive information from the log file.
@@ -60,5 +53,4 @@ Log file location:
- Windows: %appdata%/Cryptomator
- macOS: ~/Library/Logs/Cryptomator
- Linux: ~/.local/share/Cryptomator/logs
-->
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Help & Support
url: https://community.cryptomator.org/
about: You will find answers in our community forum
- name: User Manual
url: https://docs.cryptomator.org/
about: Read the Cryptomator documentation here
+4 -9
View File
@@ -5,14 +5,9 @@ labels: type:feature-request
---
<!--
Do you want to ask a QUESTION? Are you looking for SUPPORT?
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
By filing a feature request, you are expected to comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
Of course, we also expect you to search for existing similar feature requests first! ;)
Please make sure to:
- Comply with our code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
- Search for existing similar issues first: https://github.com/cryptomator/cryptomator/issues?q=
-->
### Summary
@@ -29,4 +24,4 @@ Of course, we also expect you to search for existing similar feature requests fi
### Additional Context
[Add any other context or screenshots about the feature request here.]
[Add any other context or screenshots about the feature request here.]
View File
+1 -1
View File
@@ -14,5 +14,5 @@ For _everything else_, please visit our official [Cryptomator Community](https:/
- Discussions about the apps
- [Development discussions](https://community.cryptomator.org/c/development)
- General questions
- Discussions regarding our design decissions
- Discussions regarding our design decisions
- Our roadmap
+51 -19
View File
@@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 14
java-version: 16
- uses: actions/cache@v1
with:
path: ~/.m2/repository
@@ -20,47 +20,46 @@ jobs:
restore-keys: |
${{ runner.os }}-maven-
- name: Ensure to use tagged version
run: mvn versions:set --file main/pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags'
run: mvn versions:set -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags'
if: startsWith(github.ref, 'refs/tags/')
- name: Build and Test
run: mvn -B install --file main/pom.xml -Pcoverage
- name: Run Codacy Coverage Reporter
run: |
curl -o ~/codacy-coverage-reporter.jar https://repo.maven.apache.org/maven2/com/codacy/codacy-coverage-reporter/7.1.0/codacy-coverage-reporter-7.1.0-assembly.jar
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check
- name: Upload code coverage report
id: codacyCoverageReporter
run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
continue-on-error: true
### TODO: move to matrix build:
- name: Assemble buildkit-linux.zip
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,linux
run: mvn -B clean package -DskipTests -Plinux
- name: Upload buildkit-linux.zip
uses: actions/upload-artifact@v1
with:
name: buildkit-linux.zip
path: main/buildkit/target/buildkit-linux.zip
path: target/buildkit-linux.zip
- name: Assemble buildkit-mac.zip
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,mac
run: mvn -B clean package -DskipTests -Pmac
- name: Upload buildkit-mac.zip
uses: actions/upload-artifact@v1
with:
name: buildkit-mac.zip
path: main/buildkit/target/buildkit-mac.zip
path: target/buildkit-mac.zip
- name: Assemble buildkit-win.zip
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,windows
run: mvn -B clean package -DskipTests -Pwindows
- name: Upload buildkit-win.zip
uses: actions/upload-artifact@v1
with:
name: buildkit-win.zip
path: main/buildkit/target/buildkit-win.zip
path: target/buildkit-win.zip
release:
name: Draft a Release on GitHub Releases
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'cryptomator/cryptomator'
steps:
- uses: actions/checkout@v2
- name: Download buildkit-linux.zip
uses: actions/download-artifact@v1
with:
@@ -76,6 +75,15 @@ jobs:
with:
name: buildkit-win.zip
path: .
- name: Create tarball
run: git archive --prefix="cryptomator-${{ github.ref }}/" -o "cryptomator-${{ github.ref }}.tar.gz" ${{ github.ref }}
- name: Sign tarball with key 615D449FE6E6A235
run: |
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-${{ github.ref }}.tar.gz"
env:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Create Release
id: create_release
uses: actions/create-release@v1
@@ -86,6 +94,21 @@ jobs:
release_name: ${{ github.ref }}
body: |
:construction: Work in Progress
TODO:
* [ ] add Linux appimage, zsync file and signature file
* [ ] add Windows installer and signature file
* [ ] add MacOs disk image and signature file
## What's new
## Bugfixes
## Misc
---
:scroll: A complete list of closed issues is available [here](LINK)
draft: true
prerelease: false
- name: Upload buildkit-linux.zip to GitHub Releases
@@ -114,4 +137,13 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: buildkit-win.zip
asset_name: buildkit-win.zip
asset_content_type: application/zip
asset_content_type: application/zip
- name: Upload tarball signature to GitHub Releases
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: "cryptomator-${{ github.ref }}.tar.gz.asc"
asset_name: "cryptomator-${{ github.ref }}.tar.gz.asc"
asset_content_type: application/octet-stream
+21 -9
View File
@@ -6,20 +6,32 @@ on:
jobs:
closeTemplateViolation:
name: Close bug reports that violate the issue template
name: Validate bug report against issue template
runs-on: ubuntu-latest
if: contains(github.event.issue.labels.*.name, 'type:bug')
steps:
- if: |
contains(github.event.issue.labels.*.name, 'type:bug')
&& (
!contains(github.event.issue.body, '<!-- oooooo -->')
|| !contains(github.event.issue.body, '### Description')
)
name: Close Issue
- name: Check "Description"
if: |
!contains(github.event.issue.body, env.MUST_CONTAIN)
|| contains(toJson(github.event.issue.body), env.MUST_NOT_CONTAIN)
run: exit 1
env:
MUST_CONTAIN: '### Description'
MUST_NOT_CONTAIN: '### Description\r\n\r\n[Summarize your problem.]\r\n\r\n### System Setup'
- name: Check "Steps to Reproduce"
if: |
!contains(github.event.issue.body, env.MUST_CONTAIN)
|| contains(toJson(github.event.issue.body), env.MUST_NOT_CONTAIN)
run: exit 1
env:
MUST_CONTAIN: '### Steps to Reproduce'
MUST_NOT_CONTAIN: '### Steps to Reproduce\r\n\r\n1. [First step]\r\n2. [Second step]\r\n3. [and so on…]\r\n\r\n#### Expected Behavior'
- name: Close issue if one of the checks failed
if: ${{ failure() }}
uses: peter-evans/close-issue@v1
with:
comment: |
This bug report did ignore our issue template. 😞
Auto-closing this issue, since it is most likely not useful.
_This decision was made by a bot. If you think the bot is wrong, let us know and we'll reopen this issue._
_This decision was made by a bot. If you think the bot is wrong, let us know and we'll reopen this issue._
+4 -1
View File
@@ -17,9 +17,12 @@ pom.xml.versionsBackup
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
.idea/**/shelf
.idea/dictionaries/**
!.idea/dictionaries/dict_*
.idea/compiler.xml
.idea/encodings.xml
.idea/jarRepositories.xml
.idea/uiDesigner.xml
.idea/**/libraries/
*.iml
+15
View File
@@ -0,0 +1,15 @@
<component name="ProjectDictionaryState">
<dictionary name="dict_de">
<words>
<w>tresorinhalt</w>
<w>tresorkonfigurationsdatei</w>
<w>tresorlaufwerk</w>
<w>tresorliste</w>
<w>tresorname</w>
<w>tresoroptionen</w>
<w>tresorstatistik</w>
<w>ungespeicherten</w>
<w>ungespeicherter</w>
</words>
</dictionary>
</component>
+106
View File
@@ -0,0 +1,106 @@
<component name="ProjectDictionaryState">
<dictionary name="dict_project">
<words>
<w>addvault</w>
<w>addvaultwizard</w>
<w>adoptopenjdk</w>
<w>affero</w>
<w>aquafx</w>
<w>autolock</w>
<w>autolocked</w>
<w>autolocking</w>
<w>baos</w>
<w>bkup</w>
<w>buildkit</w>
<w>changepassword</w>
<w>checkerframework</w>
<w>crit</w>
<w>crowdin</w>
<w>cryptofs</w>
<w>cryptomator</w>
<w>cryptomator's</w>
<w>cryptor</w>
<w>csprng</w>
<w>dcryptomator</w>
<w>dfuse</w>
<w>dirid</w>
<w>djdk</w>
<w>dokany</w>
<w>dragboard</w>
<w>easybind</w>
<w>encr</w>
<w>errorprone</w>
<w>failureaccess</w>
<w>fbidis</w>
<w>fldinst</w>
<w>fldrslt</w>
<w>fontawesomefx</w>
<w>gdrive</w>
<w>gvfs</w>
<w>hmmss</w>
<w>httpcomponents</w>
<w>httpcore</w>
<w>iclouddrive</w>
<w>jensd</w>
<w>jffi</w>
<w>keyloading</w>
<w>kibs</w>
<w>listcell</w>
<w>listenablefuture</w>
<w>lopp</w>
<w>mainwindow</w>
<w>masterkey</w>
<w>masterkeyfile</w>
<w>mibs</w>
<w>mountpath</w>
<w>mpc's</w>
<w>needsmigration</w>
<w>noncommercially</w>
<w>noël</w>
<w>nulab</w>
<w>oatomic</w>
<w>oauto</w>
<w>objectgraph</w>
<w>odefault</w>
<w>ogid</w>
<w>onoappledouble</w>
<w>ordonly</w>
<w>ouid</w>
<w>ovolname</w>
<w>passthrough</w>
<w>patreon</w>
<w>pcloud</w>
<w>probot</w>
<w>recoverykey</w>
<w>relicensing</w>
<w>removevault</w>
<w>revealer</w>
<w>serceman</w>
<w>setolabs</w>
<w>skymatic</w>
<w>socio</w>
<w>spof</w>
<w>stenzel</w>
<w>styleclass</w>
<w>sublicenses</w>
<w>sublicensing</w>
<w>systemkeychain</w>
<w>tada</w>
<w>tidelift</w>
<w>tmpmountpoint</w>
<w>tobiasdiez</w>
<w>toggler</w>
<w>traymenu</w>
<w>unknownerror</w>
<w>unlockable</w>
<w>vaultconfig</w>
<w>vaultlist</w>
<w>vaultname</w>
<w>vaultoptions</w>
<w>wrongfilealert</w>
<w>xattr</w>
<w>zillmann</w>
<w>zxcvbn</w>
</words>
</dictionary>
</component>
+1 -1
View File
@@ -1,7 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
+2 -2
View File
@@ -4,11 +4,11 @@
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/main/pom.xml" />
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="false" project-jdk-name="14" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="16" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Desktop.iml" filepath="$PROJECT_DIR$/.idea/Desktop.iml" />
</modules>
</component>
</project>
+2 -2
View File
@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Xss20m -Xmx512m" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+2 -2
View File
@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/.config/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<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.ipcPortPath=&quot;~/.config/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+2 -2
View File
@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Xss2m -Xmx512m" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+2 -2
View File
@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dfuse.experimental=&quot;true&quot; -Xss2m -Xmx512m" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&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" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+2 -3
View File
@@ -4,9 +4,8 @@
<env name="LD_LIBRARY_PATH" value="/usr/local/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.mountPointsDir=&quot;/Volumes/&quot; -Xss2m -Xmx512m -ea" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+2 -2
View File
@@ -4,8 +4,8 @@
<env name="LD_LIBRARY_PATH" value="/usr/local/lib" />
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.mountPointsDir=&quot;/Volumes/&quot; -Dfuse.experimental=&quot;true&quot; -Xss2m -Xmx512m -ea" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcPortPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipcPort.bin&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+26 -11
View File
@@ -1,8 +1,8 @@
[![cryptomator](cryptomator.png)](https://cryptomator.org/)
[![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?targetFile=main%2Fpom.xml)](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2a0adf3cec6a4143b91035d3924178f1)](https://www.codacy.com/app/cryptomator/cryptomator?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=cryptomator/cryptomator&amp;utm_campaign=Badge_Grade)
[![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)
[![Twitter](https://img.shields.io/badge/twitter-@Cryptomator-blue.svg?style=flat)](http://twitter.com/Cryptomator)
[![Crowdin](https://badges.crowdin.net/cryptomator/localized.svg)](https://translate.cryptomator.org/)
[![Latest Release](https://img.shields.io/github/release/cryptomator/cryptomator.svg)](https://github.com/cryptomator/cryptomator/releases/latest)
@@ -17,11 +17,26 @@ Cryptomator is provided free of charge as an open-source project despite the hig
### Gold Sponsors
[<img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="96">](https://www.gee-whiz.de/)
<table>
<tbody>
<tr>
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td>
<td><a href="https://proxy-hub.com/"><img src="https://cryptomator.org/img/sponsors/proxyhub.svg" alt="Proxy-Hub" height="80"></a></td>
</tr>
</tbody>
</table>
### Silver Sponsors
[![TheBestVPN](https://cryptomator.org/img/sponsors/thebestvpn.png)](https://thebestvpn.com/)
<table>
<tbody>
<tr>
<td><a href="https://thebestvpn.com/"><img src="https://cryptomator.org/img/sponsors/thebestvpn@2x.png" alt="TheBestVPN" height="64"></a></td>
</tr>
</tbody>
</table>
- [Jameson Lopp](https://www.lopp.net/)
---
@@ -33,7 +48,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
## Features
- Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
- Works with Dropbox, Google Drive, OneDrive, MEGA, pCloud, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
- Open Source means: No backdoors, control is better than trust
- Client-side: No accounts, no data shared with any online service
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
@@ -65,20 +80,20 @@ For more information on the security details visit [cryptomator.org](https://doc
### Dependencies
* JDK 14 (e.g. adoptopenjdk)
* JDK 16 (e.g. adoptopenjdk)
* 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
```
cd main
mvn clean install -Prelease,windows
# or mvn clean install -Prelease,mac
# or mvn clean install -Prelease,linux
mvn clean install
# or mvn clean install -Pwindows
# or mvn clean install -Pmac
# or mvn clean install -Plinux
```
This will build all the jars and bundle them together with their OS-specific dependencies under `main/buildkit/target`. This can now be used to build native packages.
This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages.
### Start Cryptomator
@@ -29,6 +29,13 @@
<outputDirectory></outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>target/</directory>
<includes>
<include>cryptomator-*.jar</include>
</includes>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet>
<directory>target/libs</directory>
<includes>
@@ -29,6 +29,13 @@
<outputDirectory></outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>target/</directory>
<includes>
<include>cryptomator-*.jar</include>
</includes>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet>
<directory>target/libs</directory>
<includes>
@@ -29,6 +29,13 @@
<outputDirectory></outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>target/</directory>
<includes>
<include>cryptomator-*.jar</include>
</includes>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet>
<directory>target/libs</directory>
<includes>
-216
View File
@@ -1,216 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.5.9</version>
</parent>
<artifactId>buildkit</artifactId>
<packaging>pom</packaging>
<name>Cryptomator Build Kit</name>
<description>Builds a package that can be built with Ant locally</description>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>launcher</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- copy resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>version.txt</include>
<include>ffi-version.txt</include>
<include>launcher-mac.sh</include>
<include>launcher-linux.sh</include>
<include>launcher-win.bat</include>
<include>LICENSE.txt</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- copy libraries to target/libs/: -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<excludeClassifiers>linux,mac,win</excludeClassifiers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>linux</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-linux</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-linux.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-linux</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-linux-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>linux</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mac</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-mac</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-mac.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-mac</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-mac-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>mac</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>windows</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-win</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-win.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-win</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-win-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>win</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
-86
View File
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.5.9</version>
</parent>
<artifactId>commons</artifactId>
<name>Cryptomator Commons</name>
<description>Shared utilities</description>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>fuse-nio-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>dokany-nio-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-api</artifactId>
</dependency>
<!-- JavaFx -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
</dependency>
<!-- EasyBind -->
<dependency>
<groupId>com.tobiasdiez</groupId>
<artifactId>easybind</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<!-- Google -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- DI -->
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,7 +0,0 @@
package org.cryptomator.common;
public interface Constants {
String MASTERKEY_FILENAME = "masterkey.cryptomator";
}
@@ -1,79 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common;
import com.google.common.base.Throwables;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
public final class LazyInitializer {
private LazyInitializer() {
}
/**
* Same as {@link #initializeLazily(AtomicReference, SupplierThrowingException, Class)} except that no checked exception may be thrown by the factory function.
*
* @param <T> Type of the value
* @param reference A reference to a maybe not yet initialized value.
* @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
* @return The initialized value
*/
public static <T> T initializeLazily(AtomicReference<T> reference, Supplier<T> factory) {
SupplierThrowingException<T, RuntimeException> factoryThrowingRuntimeExceptions = () -> factory.get();
return initializeLazily(reference, factoryThrowingRuntimeExceptions, RuntimeException.class);
}
/**
* Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
*
* @param <T> Type of the value
* @param <E> Type of the any expected exception that may occur during initialization
* @param reference A reference to a maybe not yet initialized value.
* @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
* @param exceptionType Expected exception type.
* @return The initialized value
* @throws E Exception thrown by the factory function.
*/
public static <T, E extends Exception> T initializeLazily(AtomicReference<T> reference, SupplierThrowingException<T, E> factory, Class<E> exceptionType) throws E {
final T existing = reference.get();
if (existing != null) {
return existing;
} else {
try {
return reference.updateAndGet(invokeFactoryIfNull(factory));
} catch (InitializationException e) {
Throwables.throwIfUnchecked(e.getCause());
Throwables.throwIfInstanceOf(e.getCause(), exceptionType);
throw e;
}
}
}
private static <T, E extends Exception> UnaryOperator<T> invokeFactoryIfNull(SupplierThrowingException<T, E> factory) throws InitializationException {
return currentValue -> {
if (currentValue == null) {
try {
return factory.get();
} catch (Exception e) {
throw new InitializationException(e);
}
} else {
return currentValue;
}
};
}
private static class InitializationException extends RuntimeException {
public InitializationException(Throwable cause) {
super(cause);
}
}
}
@@ -1,64 +0,0 @@
package org.cryptomator.common.mountpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public class IrregularUnmountCleaner {
public static Logger LOG = LoggerFactory.getLogger(IrregularUnmountCleaner.class);
public static void removeIrregularUnmountDebris(Path dirContainingMountPoints) {
IOException cleanupFailed = new IOException("Cleanup failed");
try {
LOG.debug("Performing cleanup of mountpoint dir {}.", dirContainingMountPoints);
for (Path p : Files.newDirectoryStream(dirContainingMountPoints)) {
try {
var attr = Files.readAttributes(p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
if (attr.isOther() && attr.isDirectory()) { // yes, this is possible with windows junction points -.-
Files.delete(p);
} else if (attr.isDirectory()) {
deleteEmptyDir(p);
} else if (attr.isSymbolicLink()) {
deleteDeadLink(p);
} else {
LOG.debug("Found non-directory element in mountpoint dir: {}", p);
}
} catch (IOException e) {
cleanupFailed.addSuppressed(e);
}
}
if (cleanupFailed.getSuppressed().length > 0) {
throw cleanupFailed;
}
} catch (IOException e) {
LOG.warn("Unable to perform cleanup of mountpoint dir {}.", dirContainingMountPoints, e);
}
}
private static void deleteEmptyDir(Path dir) throws IOException {
assert Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS);
try {
Files.delete(dir); // attempt to delete dir non-recursively (will fail, if there are contents)
} catch (DirectoryNotEmptyException e) {
LOG.info("Found non-empty directory in mountpoint dir: {}", dir);
}
}
private static void deleteDeadLink(Path symlink) throws IOException {
assert Files.isSymbolicLink(symlink);
if (Files.notExists(symlink)) { // following link: target does not exist
Files.delete(symlink);
}
}
}
@@ -1,19 +0,0 @@
package org.cryptomator.common.settings;
public enum KeychainBackend {
GNOME("org.cryptomator.linux.keychain.SecretServiceKeychainAccess"),
KDE("org.cryptomator.linux.keychain.KDEWalletKeychainAccess"),
MAC_SYSTEM_KEYCHAIN("org.cryptomator.macos.keychain.MacSystemKeychainAccess"),
WIN_SYSTEM_KEYCHAIN("org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess");
private final String providerClass;
KeychainBackend(String providerClass) {
this.providerClass = providerClass;
}
public String getProviderClass() {
return providerClass;
}
}
@@ -1,60 +0,0 @@
package org.cryptomator.common.vaults;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import java.nio.file.Path;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
public abstract class AbstractVolume implements Volume {
private final SortedSet<MountPointChooser> choosers;
protected Path mountPoint;
//Cleanup
private boolean cleanupRequired;
private MountPointChooser usedChooser;
public AbstractVolume(SortedSet<MountPointChooser> choosers) {
this.choosers = choosers;
}
protected Path determineMountPoint() throws InvalidMountPointException {
SortedSet<MountPointChooser> checkedChoosers = new TreeSet<>(); //Natural order
for (MountPointChooser chooser : this.choosers) {
if (!chooser.isApplicable(this)) {
continue;
}
Optional<Path> chosenPath = chooser.chooseMountPoint(this);
checkedChoosers.add(chooser); //Consider a chooser checked if it's #chooseMountPoint() method was called
if (chosenPath.isEmpty()) {
//Chooser was applicable, but couldn't find a feasible mountpoint
continue;
}
this.cleanupRequired = chooser.prepare(this, chosenPath.get()); //Fail entirely if an Exception occurs
this.usedChooser = chooser;
return chosenPath.get();
}
//SortedSet#stream() should return a sorted stream (that's what it's docs and the docs of #spliterator() say, even if they are not 100% clear for me.)
//We want to keep that order, that's why we use ImmutableSet#toImmutableSet() to collect (even if it doesn't implement SortedSet, it's docs promise use encounter ordering.)
String checked = Joiner.on(", ").join(checkedChoosers.stream().map((mpc) -> mpc.getClass().getTypeName()).collect(ImmutableSet.toImmutableSet()));
throw new InvalidMountPointException(String.format("No feasible MountPoint found! Checked %s", checked.isBlank() ? "<No applicable MPC>" : checked));
}
protected void cleanupMountPoint() {
if (this.cleanupRequired) {
this.usedChooser.cleanup(this, this.mountPoint);
}
}
@Override
public Optional<Path> getMountPoint() {
return Optional.ofNullable(mountPoint);
}
}
@@ -1,34 +0,0 @@
package org.cryptomator.common.vaults;
public enum VaultState {
/**
* No vault found at the provided path
*/
MISSING,
/**
* Vault requires migration to a newer vault format
*/
NEEDS_MIGRATION,
/**
* Vault ready to be unlocked
*/
LOCKED,
/**
* Vault in transition between two other states
*/
PROCESSING,
/**
* Vault is unlocked
*/
UNLOCKED,
/**
* Unknown state due to preceeding unrecoverable exceptions.
*/
ERROR;
}
-48
View File
@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.5.9</version>
</parent>
<artifactId>launcher</artifactId>
<name>Cryptomator Launcher</name>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>ui</artifactId>
</dependency>
<!-- Libs -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- DI -->
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>
-424
View File
@@ -1,424 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.5.9</version>
<packaging>pom</packaging>
<name>Cryptomator</name>
<organization>
<name>cryptomator.org</name>
<url>https://cryptomator.org</url>
</organization>
<developers>
<developer>
<name>Sebastian Stenzel</name>
<email>sebastian.stenzel@gmail.com</email>
<timezone>+1</timezone>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>1.9.13</cryptomator.cryptofs.version>
<cryptomator.integrations.version>0.1.6</cryptomator.integrations.version>
<cryptomator.integrations.win.version>0.1.0-beta1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>0.1.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>0.1.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.2.5</cryptomator.fuse.version>
<cryptomator.dokany.version>1.2.0</cryptomator.dokany.version>
<cryptomator.webdav.version>1.0.13</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>15</javafx.version>
<commons-lang3.version>3.11</commons-lang3.version>
<jwt.version>3.11.0</jwt.version>
<easybind.version>2.1.0</easybind.version>
<guava.version>30.0-jre</guava.version>
<dagger.version>2.29.1</dagger.version>
<gson.version>2.8.6</gson.version>
<slf4j.version>1.7.30</slf4j.version>
<logback.version>1.2.3</logback.version>
<!-- test dependencies -->
<junit.jupiter.version>5.7.0</junit.jupiter.version>
<mockito.version>3.6.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
</properties>
<repositories>
<repository>
<id>jcenter</id>
<url>https://jcenter.bintray.com</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<!-- modules -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>launcher</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>${cryptomator.cryptofs.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>fuse-nio-adapter</artifactId>
<version>${cryptomator.fuse.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>dokany-nio-adapter</artifactId>
<version>${cryptomator.dokany.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter</artifactId>
<version>${cryptomator.webdav.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-api</artifactId>
<version>${cryptomator.integrations.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-win</artifactId>
<version>${cryptomator.integrations.win.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-mac</artifactId>
<version>${cryptomator.integrations.mac.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-linux</artifactId>
<version>${cryptomator.integrations.linux.version}</version>
</dependency>
<!-- JavaFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>
<groupId>com.tobiasdiez</groupId>
<artifactId>easybind</artifactId>
<version>${easybind.version}</version>
</dependency>
<!-- Google -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>${dagger.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- JUnit / Mockito / Hamcrest -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>${hamcrest.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- common dependencies for all modules -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<modules>
<module>commons</module>
<module>ui</module>
<module>launcher</module>
</modules>
<profiles>
<profile>
<id>release</id>
<modules>
<module>buildkit</module>
</modules>
</profile>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-mac</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-linux</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-win</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<!-- adds Implementation-Version which can be read during runtime -->
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/*_*</exclude>
<exclude>**/Dagger*</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>14</release>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${dagger.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-115
View File
@@ -1,115 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.5.9</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons</artifactId>
</dependency>
<!-- JavaFx -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
</dependency>
<!-- EasyBind -->
<dependency>
<groupId>com.tobiasdiez</groupId>
<artifactId>easybind</artifactId>
</dependency>
<!-- Google -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- DI -->
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
<!-- Zxcvbn -->
<dependency>
<groupId>com.nulab-inc</groupId>
<artifactId>zxcvbn</artifactId>
<version>1.3.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<executions>
<execution>
<id>add-third-party</id>
<goals>
<goal>add-third-party</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
<includedScopes>compile</includedScopes>
<excludedGroups>org\.cryptomator</excludedGroups>
<licenseMerges>
<licenseMerge>Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0</licenseMerge>
<licenseMerge>MIT License|The MIT License (MIT)|The MIT License|MIT license</licenseMerge>
<licenseMerge>LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1</licenseMerge>
<licenseMerge>GPLv2|GNU General Public License Version 2</licenseMerge>
<licenseMerge>GPLv2+CE|CDDL + GPLv2 with classpath exception</licenseMerge>
</licenseMerges>
<fileTemplate>${project.basedir}/src/license/template.ftl</fileTemplate>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,99 +0,0 @@
package org.cryptomator.ui.mainwindow;
import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
@MainWindowScoped
public class VaultListController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(VaultListController.class);
private final ObservableList<Vault> vaults;
private final ObjectProperty<Vault> selectedVault;
private final VaultListCellFactory cellFactory;
private final AddVaultWizardComponent.Builder addVaultWizard;
private final RemoveVaultComponent.Builder removeVault;
private final BooleanBinding noVaultSelected;
private final BooleanBinding emptyVaultList;
public ListView<Vault> vaultList;
@Inject
VaultListController(ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVault) {
this.vaults = vaults;
this.selectedVault = selectedVault;
this.cellFactory = cellFactory;
this.addVaultWizard = addVaultWizard;
this.removeVault = removeVault;
this.noVaultSelected = selectedVault.isNull();
this.emptyVaultList = Bindings.isEmpty(vaults);
EasyBind.subscribe(selectedVault, this::selectedVaultDidChange);
}
public void initialize() {
vaultList.setItems(vaults);
vaultList.setCellFactory(cellFactory);
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
vaults.addListener((ListChangeListener.Change<? extends Vault> c) -> {
while (c.next()) {
if (c.wasAdded()) {
Vault anyAddedVault = c.getAddedSubList().get(0);
vaultList.getSelectionModel().select(anyAddedVault);
}
}
});
}
private void selectedVaultDidChange(Vault newValue) {
if (newValue != null) {
VaultListManager.redetermineVaultState(newValue);
}
}
@FXML
public void didClickAddVault() {
addVaultWizard.build().showAddVaultWizard();
}
@FXML
public void didClickRemoveVault() {
Vault v = selectedVault.get();
if (v != null) {
removeVault.vault(v).build().showRemoveVault();
} else {
LOG.debug("Cannot remove a vault if none is selected.");
}
}
// Getter and Setter
public BooleanBinding emptyVaultListProperty() {
return emptyVaultList;
}
public boolean isEmptyVaultList() {
return emptyVaultList.get();
}
public BooleanBinding noVaultSelectedProperty() {
return noVaultSelected;
}
public boolean isNoVaultSelected() {
return noVaultSelected.get();
}
}
@@ -1,46 +0,0 @@
package org.cryptomator.ui.preferences;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Deprecated
class AutoStartMacStrategy implements AutoStartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(AutoStartMacStrategy.class);
private final AutoStartProvider autoStartProvider;
public AutoStartMacStrategy(AutoStartProvider autoStartProvider) {
this.autoStartProvider = autoStartProvider;
}
@Override
public CompletionStage<Boolean> isAutoStartEnabled() {
return CompletableFuture.completedFuture(autoStartProvider.isEnabled());
}
@Override
public void enableAutoStart() throws TogglingAutoStartFailedException {
try {
autoStartProvider.enable();
LOG.debug("Added login item.");
} catch (ToggleAutoStartFailedException e) {
throw new TogglingAutoStartFailedException("Failed to add login item.");
}
}
@Override
public void disableAutoStart() throws TogglingAutoStartFailedException {
try {
autoStartProvider.disable();
LOG.debug("Removed login item.");
} catch (ToggleAutoStartFailedException e) {
throw new TogglingAutoStartFailedException("Failed to remove login item.");
}
}
}
@@ -1,27 +0,0 @@
package org.cryptomator.ui.preferences;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import java.util.Optional;
@Deprecated
@Module
abstract class AutoStartModule {
@Provides
@PreferencesScoped
public static Optional<AutoStartStrategy> provideAutoStartStrategy(Optional<AutoStartProvider> autoStartProvider) {
if (SystemUtils.IS_OS_MAC_OSX && autoStartProvider.isPresent()) {
return Optional.of(new AutoStartMacStrategy(autoStartProvider.get()));
} else if (SystemUtils.IS_OS_WINDOWS) {
Optional<String> exeName = ProcessHandle.current().info().command();
return exeName.map(AutoStartWinStrategy::new);
} else {
return Optional.empty();
}
}
}
@@ -1,25 +0,0 @@
package org.cryptomator.ui.preferences;
import java.util.concurrent.CompletionStage;
@Deprecated
public interface AutoStartStrategy {
CompletionStage<Boolean> isAutoStartEnabled();
void enableAutoStart() throws TogglingAutoStartFailedException;
void disableAutoStart() throws TogglingAutoStartFailedException;
class TogglingAutoStartFailedException extends Exception {
public TogglingAutoStartFailedException(String message) {
super(message);
}
public TogglingAutoStartFailedException(String message, Throwable cause) {
super(message, cause);
}
}
}
@@ -1,209 +0,0 @@
package org.cryptomator.ui.preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* OS specific class to check, en- and disable the auto start on Windows.
* <p>
* Two strategies are implemented for this feature, the first uses the registry and the second one the autostart folder.
* <p>
* The registry strategy checks/add/removes at the registry key {@value HKCU_AUTOSTART_KEY} an entry for Cryptomator.
* The folder strategy checks/add/removes at the location {@value WINDOWS_START_MENU_ENTRY}.
* <p>
* To check if the feature is active, both strategies are applied.
* To enable the feature, first the registry is tried and only on failure the autostart folder is used.
* To disable it, first it is determined by an internal state, which strategies must be used and in the second step those are executed.
*
* @apiNote This class is not thread safe, hence it should be avoided to call its methods simultaniously by different threads.
* @deprecated To be moved to integration-win project
*/
@Deprecated
class AutoStartWinStrategy implements AutoStartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(AutoStartWinStrategy.class);
private static final String HKCU_AUTOSTART_KEY = "\"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\"";
private static final String AUTOSTART_VALUE = "Cryptomator";
private static final String WINDOWS_START_MENU_ENTRY = "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Cryptomator.lnk";
private final String exePath;
private boolean activatedUsingFolder;
private boolean activatedUsingRegistry;
public AutoStartWinStrategy(String exePath) {
this.exePath = exePath;
this.activatedUsingFolder = false;
this.activatedUsingRegistry = false;
}
@Override
public CompletionStage<Boolean> isAutoStartEnabled() {
return isAutoStartEnabledUsingRegistry().thenCombine(isAutoStartEnabledUsingFolder(), (bReg, bFolder) -> bReg || bFolder);
}
private CompletableFuture<Boolean> isAutoStartEnabledUsingFolder() {
Path autoStartEntry = Path.of(System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY);
this.activatedUsingFolder = Files.exists(autoStartEntry);
return CompletableFuture.completedFuture(activatedUsingFolder);
}
private CompletableFuture<Boolean> isAutoStartEnabledUsingRegistry() {
ProcessBuilder regQuery = new ProcessBuilder("reg", "query", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE);
try {
Process proc = regQuery.start();
return proc.onExit().thenApply(p -> {
this.activatedUsingRegistry = p.exitValue() == 0;
return activatedUsingRegistry;
});
} catch (IOException e) {
LOG.debug("Failed to query {} from registry key {}", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(false);
}
}
@Override
public void enableAutoStart() throws TogglingAutoStartFailedException {
try {
enableAutoStartUsingRegistry().thenAccept((Void v) -> this.activatedUsingRegistry = true).exceptionallyCompose(e -> {
LOG.debug("Falling back to using autostart folder.");
return this.enableAutoStartUsingFolder();
}).thenAccept((Void v) -> this.activatedUsingFolder = true).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new TogglingAutoStartFailedException("Execution of enabling auto start setting was interrupted.");
} catch (ExecutionException e) {
throw new TogglingAutoStartFailedException("Enabling auto start failed both using registry and auto start folder.");
}
}
private CompletableFuture<Void> enableAutoStartUsingRegistry() {
ProcessBuilder regAdd = new ProcessBuilder("reg", "add", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE, //
"/t", "REG_SZ", //
"/d", "\"" + exePath + "\"", //
"/f");
try {
Process proc = regAdd.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Added {} to registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Registry could not be edited to set auto start.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Adding registry value failed."));
}
}
private CompletableFuture<Void> enableAutoStartUsingFolder() {
String autoStartFolderEntry = System.getProperty("user.home") + WINDOWS_START_MENU_ENTRY;
String createShortcutCommand = "$s=(New-Object -COM WScript.Shell).CreateShortcut('" + autoStartFolderEntry + "');$s.TargetPath='" + exePath + "';$s.Save();";
ProcessBuilder shortcutAdd = new ProcessBuilder("cmd", "/c", "Start powershell " + createShortcutCommand);
try {
Process proc = shortcutAdd.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Created file {} for auto start.", autoStartFolderEntry);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Adding entry to auto start folder failed.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Adding entry to auto start folder failed."));
}
}
@Override
public void disableAutoStart() throws TogglingAutoStartFailedException {
if (activatedUsingRegistry) {
disableAutoStartUsingRegistry().whenComplete((voit, ex) -> {
if (ex == null) {
this.activatedUsingRegistry = false;
}
});
}
if (activatedUsingFolder) {
disableAutoStartUsingFolder().whenComplete((voit, ex) -> {
if (ex == null) {
this.activatedUsingFolder = false;
}
});
}
if (activatedUsingRegistry || activatedUsingFolder) {
throw new TogglingAutoStartFailedException("Disabling auto start failed using registry and/or auto start folder.");
}
}
public CompletableFuture<Void> disableAutoStartUsingRegistry() {
ProcessBuilder regRemove = new ProcessBuilder("reg", "delete", HKCU_AUTOSTART_KEY, //
"/v", AUTOSTART_VALUE, //
"/f");
try {
Process proc = regRemove.start();
boolean finishedInTime = waitForProcessOrCancel(proc, 5, TimeUnit.SECONDS);
if (finishedInTime && proc.exitValue() == 0) {
LOG.debug("Removed {} from registry key {}.", AUTOSTART_VALUE, HKCU_AUTOSTART_KEY);
return CompletableFuture.completedFuture(null);
} else {
throw new IOException("Process exited with error code " + proc.exitValue());
}
} catch (IOException e) {
LOG.debug("Registry could not be edited to remove auto start.", e);
return CompletableFuture.failedFuture(new SystemCommandException("Removing registry value failed."));
}
}
private CompletableFuture<Void> disableAutoStartUsingFolder() {
try {
Files.delete(Path.of(WINDOWS_START_MENU_ENTRY));
LOG.debug("Successfully deleted {}.", WINDOWS_START_MENU_ENTRY);
return CompletableFuture.completedFuture(null);
} catch (NoSuchFileException e) {
//that is also okay
return CompletableFuture.completedFuture(null);
} catch (IOException e) {
LOG.debug("Failed to delete entry from auto start folder.", e);
return CompletableFuture.failedFuture(e);
}
}
private static boolean waitForProcessOrCancel(Process proc, int timeout, TimeUnit timeUnit) {
boolean finishedInTime = false;
try {
finishedInTime = proc.waitFor(timeout, timeUnit);
} catch (InterruptedException e) {
LOG.error("Timeout while reading registry", e);
Thread.currentThread().interrupt();
} finally {
if (!finishedInTime) {
proc.destroyForcibly();
}
}
return finishedInTime;
}
private class SystemCommandException extends RuntimeException {
public SystemCommandException(String msg) {
super(msg);
}
}
}
@@ -1,29 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.ui.traymenu;
import dagger.Subcomponent;
import java.awt.SystemTray;
@TrayMenuScoped
@Subcomponent
public interface TrayMenuComponent {
TrayIconController trayIconController();
default void addIconToSystemTray() {
assert SystemTray.isSupported();
trayIconController().initializeTrayIcon();
}
@Subcomponent.Builder
interface Builder {
TrayMenuComponent build();
}
}
@@ -1,139 +0,0 @@
package org.cryptomator.ui.unlock;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FXMLLoaderFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@Module(subcomponents = {ForgetPasswordComponent.class})
abstract class UnlockModule {
private static final Logger LOG = LoggerFactory.getLogger(UnlockModule.class);
public enum PasswordEntry {
PASSWORD_ENTERED,
CANCELED
}
@Provides
@UnlockScoped
static UserInteractionLock<PasswordEntry> providePasswordEntryLock() {
return new UserInteractionLock<>(null);
}
@Provides
@Named("savedPassword")
@UnlockScoped
static Optional<char[]> provideStoredPassword(KeychainManager keychain, @UnlockWindow Vault vault) {
if (!keychain.isSupported()) {
return Optional.empty();
} else {
try {
return Optional.ofNullable(keychain.loadPassphrase(vault.getId()));
} catch (KeychainAccessException e) {
LOG.error("Failed to load entry from system keychain.", e);
return Optional.empty();
}
}
}
@Provides
@UnlockScoped
static AtomicReference<char[]> providePassword(@Named("savedPassword") Optional<char[]> storedPassword) {
return new AtomicReference(storedPassword.orElse(null));
}
@Provides
@Named("savePassword")
@UnlockScoped
static AtomicBoolean provideSavePasswordFlag(@Named("savedPassword") Optional<char[]> storedPassword) {
return new AtomicBoolean(storedPassword.isPresent());
}
@Provides
@UnlockWindow
@UnlockScoped
static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@UnlockWindow
@UnlockScoped
static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Named("unlockWindowOwner") Optional<Stage> owner) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(false);
if (owner.isPresent()) {
stage.initOwner(owner.get());
stage.initModality(Modality.WINDOW_MODAL);
} else {
stage.initModality(Modality.APPLICATION_MODAL);
}
return stage;
}
@Provides
@FxmlScene(FxmlFile.UNLOCK)
@UnlockScoped
static Scene provideUnlockScene(@UnlockWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/unlock.fxml");
}
@Provides
@FxmlScene(FxmlFile.UNLOCK_SUCCESS)
@UnlockScoped
static Scene provideUnlockSuccessScene(@UnlockWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/unlock_success.fxml");
}
@Provides
@FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT)
@UnlockScoped
static Scene provideInvalidMountPointScene(@UnlockWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/unlock_invalid_mount_point.fxml");
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(UnlockController.class)
abstract FxController bindUnlockController(UnlockController controller);
@Binds
@IntoMap
@FxControllerKey(UnlockSuccessController.class)
abstract FxController bindUnlockSuccessController(UnlockSuccessController controller);
@Binds
@IntoMap
@FxControllerKey(UnlockInvalidMountPointController.class)
abstract FxController bindUnlockInvalidMountPointController(UnlockInvalidMountPointController controller);
}
@@ -1,241 +0,0 @@
package org.cryptomator.ui.unlock;
import dagger.Lazy;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.vaults.MountPointRequirement;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume.VolumeException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.unlock.UnlockModule.PasswordEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.NotDirectoryException;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* A multi-step task that consists of background activities as well as user interaction.
* <p>
* This class runs the unlock process and controls when to display which UI.
*/
@UnlockScoped
public class UnlockWorkflow extends Task<Boolean> {
private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class);
private final Stage window;
private final Vault vault;
private final VaultService vaultService;
private final AtomicReference<char[]> password;
private final AtomicBoolean savePassword;
private final Optional<char[]> savedPassword;
private final UserInteractionLock<PasswordEntry> passwordEntryLock;
private final KeychainManager keychain;
private final Lazy<Scene> unlockScene;
private final Lazy<Scene> successScene;
private final Lazy<Scene> invalidMountPointScene;
private final ErrorComponent.Builder errorComponent;
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<PasswordEntry> passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, ErrorComponent.Builder errorComponent) {
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
this.password = password;
this.savePassword = savePassword;
this.savedPassword = savedPassword;
this.passwordEntryLock = passwordEntryLock;
this.keychain = keychain;
this.unlockScene = unlockScene;
this.successScene = successScene;
this.invalidMountPointScene = invalidMountPointScene;
this.errorComponent = errorComponent;
setOnFailed(event -> {
Throwable throwable = event.getSource().getException();
if (throwable instanceof InvalidMountPointException) {
handleInvalidMountPoint((InvalidMountPointException) throwable);
} else {
handleGenericError(throwable);
}
});
}
@Override
protected Boolean call() throws InterruptedException, IOException, VolumeException, InvalidMountPointException {
try {
if (attemptUnlock()) {
handleSuccess();
return true;
} else {
cancel(false); // set Tasks state to cancelled
return false;
}
} finally {
wipePassword(password.get());
wipePassword(savedPassword.orElse(null));
}
}
private boolean attemptUnlock() throws InterruptedException, IOException, VolumeException, InvalidMountPointException {
boolean proceed = password.get() != null || askForPassword(false) == PasswordEntry.PASSWORD_ENTERED;
while (proceed) {
try {
vault.unlock(CharBuffer.wrap(password.get()));
return true;
} catch (InvalidPassphraseException e) {
proceed = askForPassword(true) == PasswordEntry.PASSWORD_ENTERED;
}
}
return false;
}
private PasswordEntry askForPassword(boolean animateShake) throws InterruptedException {
Platform.runLater(() -> {
window.setScene(unlockScene.get());
window.show();
Window owner = window.getOwner();
if (owner != null) {
window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
} else {
window.centerOnScreen();
}
if (animateShake) {
Animations.createShakeWindowAnimation(window).play();
}
});
return passwordEntryLock.awaitInteraction();
}
private void handleSuccess() {
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
if (savePassword.get()) {
savePasswordToSystemkeychain();
}
switch (vault.getVaultSettings().actionAfterUnlock().get()) {
case ASK -> Platform.runLater(() -> {
window.setScene(successScene.get());
window.show();
});
case REVEAL -> {
Platform.runLater(window::close);
vaultService.reveal(vault);
}
case IGNORE -> Platform.runLater(window::close);
}
}
private void savePasswordToSystemkeychain() {
if (keychain.isSupported()) {
try {
keychain.storePassphrase(vault.getId(), CharBuffer.wrap(password.get()));
} catch (KeychainAccessException e) {
LOG.error("Failed to store passphrase in system keychain.", e);
}
}
}
private void handleInvalidMountPoint(InvalidMountPointException impExc) {
MountPointRequirement requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!", impExc)).getMountPointRequirement();
assert requirement != MountPointRequirement.NONE; //An invalid MountPoint with no required MountPoint doesn't seem sensible
assert requirement != MountPointRequirement.PARENT_OPT_MOUNT_POINT; //Not implemented anywhere (yet)
Throwable cause = impExc.getCause();
//Cause is either null (cause the IMPE was thrown directly, e.g. because no MPC succeeded)
//or the cause was not an Exception (but some other kind of Throwable)
//Either way: Handle as generic error
if (!(cause instanceof Exception)) {
handleGenericError(impExc);
return;
}
//From here on handle the cause, not the caught exception
if (cause instanceof NotDirectoryException) {
if (requirement == MountPointRequirement.PARENT_NO_MOUNT_POINT) {
LOG.error("Unlock failed. Parent folder is missing: {}", cause.getMessage());
} else {
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
}
showInvalidMountPointScene();
return;
}
if (cause instanceof FileAlreadyExistsException) {
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
showInvalidMountPointScene();
return;
}
if (cause instanceof DirectoryNotEmptyException) {
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
showInvalidMountPointScene();
return;
}
//Everything else (especially IOException) results in a generic error
//This must be done after the other exceptions because they extend IOException...
handleGenericError(cause);
}
private void showInvalidMountPointScene() {
Platform.runLater(() -> {
window.setScene(invalidMountPointScene.get());
});
}
private void handleGenericError(Throwable e) {
LOG.error("Unlock failed for technical reasons.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
private void wipePassword(char[] pw) {
if (pw != null) {
Arrays.fill(pw, ' ');
}
}
@Override
protected void scheduled() {
vault.setState(VaultState.PROCESSING);
}
@Override
protected void succeeded() {
vault.setState(VaultState.UNLOCKED);
}
@Override
protected void failed() {
vault.setState(VaultState.LOCKED);
}
@Override
protected void cancelled() {
vault.setState(VaultState.LOCKED);
}
}
@@ -1,13 +0,0 @@
package org.cryptomator.ui.vaultoptions;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
@VaultOptionsScoped
public class VaultOptionsController implements FxController {
@Inject
VaultOptionsController() {}
}
@@ -1,161 +0,0 @@
# Locale Specific CSS files such as CJK, RTL,...
# Generics
## Button
generic.button.apply=Primjeni
generic.button.back=Nazad
generic.button.cancel=Odustani
generic.button.change=Izmjeni
generic.button.close=Zatvori
generic.button.copy=Kopiraj
generic.button.copied=Kopirano!
generic.button.done=Gotovo
generic.button.next=Sljedeće
generic.button.print=Ispis
## Error
generic.error.title=Došlo je do neočekivane greške
generic.error.instruction=Ovo se nije trebalo dogoditi. Molimo da prijavite opis greške ispod, kao i korake koji su do greške doveli.
# Defaults
defaults.vault.vaultName=Sef
# Tray Menu
traymenu.showMainWindow=Pokaži
traymenu.showPreferencesWindow=Postavke
traymenu.lockAllVaults=Zaključaj sve
traymenu.quitApplication=Odustani
traymenu.vault.unlock=Otključaj
traymenu.vault.lock=Zaključaj
traymenu.vault.reveal=Otkrij
# Add Vault Wizard
addvaultwizard.title=Dodaj sef
## Welcome
addvaultwizard.welcome.newButton=Kreiraj novi sef
addvaultwizard.welcome.existingButton=Otvori postojeći sef
## New
### Name
addvaultwizard.new.nameInstruction=Izaberi naziv za sef
addvaultwizard.new.namePrompt=Ime sefa
### Location
addvaultwizard.new.locationInstruction=Gdje bi Cryptomator trebao pohraniti enkriptovane fajlove iz vaseg sefa?
addvaultwizard.new.locationLabel=Mjesto pohrane
addvaultwizard.new.locationPrompt=
addvaultwizard.new.directoryPickerLabel=Prilagođena lokacija
addvaultwizard.new.directoryPickerButton=Odaberi…
addvaultwizard.new.directoryPickerTitle=Izaberi folder
addvaultwizard.new.fileAlreadyExists=Sef ne može biti kreiran na odabranoj lokaciji jer neki objekti tu već postoje.
addvaultwizard.new.locationDoesNotExist=Sef ne može biti kreiran na odabranoj lokaciji jer najmanje jedna komponenta lokacije ne postoji.
addvaultwizard.new.invalidName=Naziv sefa neispravan. Molimo razmislite o drugom nazivu.
### Password
addvaultwizard.new.createVaultBtn=Kreiraj novi sef
addvaultwizard.new.generateRecoveryKeyChoice=Nećete moći pristupiti svojim podacima bez lozinke. Želite li ključ za oporavak u slučaju da izgubite lozinku?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Da molim, što sigurno, sigurno je
addvaultwizard.new.generateRecoveryKeyChoice.no=Ne, hvala, neću izgubiti lozinku
### Information
addvault.new.readme.storageLocation.fileName=VAŽNO.rtf
addvault.new.readme.storageLocation.1=⚠️ POHRANI DATOTEKE U SEF ⚠️
addvault.new.readme.storageLocation.2=Ovo je mjesto pohrane vašeg sefa.
addvault.new.readme.storageLocation.3=NEMOJ
addvault.new.readme.storageLocation.4=• izmijeniti bilo koju datoteku u ovom direktoriju ili
addvault.new.readme.storageLocation.5=• zalijepiti bilo koje datoteke za enkripciju u ovaj folder.
addvault.new.readme.storageLocation.6=Ako želite enkriptovati datoteke i pregledati sadržaj sefa, učinite sljedeće:
addvault.new.readme.storageLocation.7=1. Dodajte ovaj sef u Cryptomator.
addvault.new.readme.storageLocation.8=2. Otključajte sef u Cryptomator-u.
addvault.new.readme.storageLocation.9=3. Otvorite pristupnu lokaciju klikom na dugme "Otkrivanje".
addvault.new.readme.storageLocation.10=Ako vam je potrebna pomoć, posjetite dokumentaciju:%s
addvault.new.readme.accessLocation.fileName=DOBRO DOŠLI.rtf
addvault.new.readme.accessLocation.1=🔐️ ENKRIPTOVAN PROSTOR 🔐️
addvault.new.readme.accessLocation.2=Ovo je pristupna lokacija vašeg sefa.
addvault.new.readme.accessLocation.3=Sve datoteke dodane u ovaj prostor enkriptovati će Cryptomator. Na njemu možete raditi kao na bilo kojem drugom pogonu / folderu. Ovo je samo dešifrirani prikaz njegovog sadržaja, vaše datoteke stalno ostaju enkriptovane na vašem disku.
addvault.new.readme.accessLocation.4=Slobodno uklonite ovu datoteku.
## Existing
addvaultwizard.existing.instruction=Odaberite datoteku "masterkey.cryptomator" vašeg postojećeg sefa.
addvaultwizard.existing.chooseBtn=Odaberi…
addvaultwizard.existing.filePickerTitle=Odaberite Masterkey Datoteku
## Success
addvaultwizard.success.nextStepsInstructions=Dodan je sef "%s".\nZa pristup ili dodavanje sadržaja morate otključati ovaj sef. Možete ga otključati u bilo kojem kasnijem trenutku.
addvaultwizard.success.unlockNow=Otključaj sada
# Remove Vault
removeVault.title=Ukloni Sef
removeVault.information=Ovo će samo natjerati Cryptomator da zaboravi na ovaj sef. Možete ga dodati ponovo kasnije. Nijedna enkriptovana datoteka neće se izbrisati s vašeg diska.
removeVault.confirmBtn=Ukloni Sef
# Change Password
changepassword.title=Promjeni lozinku
changepassword.enterOldPassword=Unesi trenutnu lozinku za "%s"
changepassword.finalConfirmation=Razumijem da neću moći pristupiti svojim podacima ako zaboravim lozinku
# Forget Password
forgetPassword.title=Zaboravili ste šifru
forgetPassword.information=Ovo će izbrisati sačuvanu lozinku ovog sefa iz vaše sistemske pohrane.
forgetPassword.confirmBtn=Zaboravili ste šifru
# Unlock
unlock.title=Otključaj sef
unlock.passwordPrompt=Unesite lozinku za "%s":
unlock.savePassword=Snimi lozinku
unlock.unlockBtn=Otključaj
## Success
unlock.success.message=Uspješno ste otključali "%s"! Vaš sef je sada dostupan.
unlock.success.rememberChoice=Zapamtite izbor, ne pokazujte ovo ponovo
unlock.success.revealBtn=Otkrij sef
## Failure
unlock.error.heading=Sef nije moguće otključati
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Tačka postavljanja "%s" nije direktorij, nije prazna ili ne postoji.
# Migration
## Start
## Run
## Sucess
migration.success.unlockNow=Otključaj sada
## Missing file system capabilities
## Impossible
# Preferences
preferences.title=Postavke
## General
## Volume
## Updates
## Donation Key
## About
# Vault Statistics
## Read
## Write
# Main Window
main.closeBtn.tooltip=Zatvori
main.preferencesBtn.tooltip=Postavke
## Drag 'n' Drop
## Vault List
main.vaultlist.addVaultBtn=Dodaj sef
## Vault Detail
### Welcome
### Locked
main.vaultDetail.unlockNowBtn=Otključaj sada
### Unlocked
main.vaultDetail.lockBtn=Zaključaj
main.vaultDetail.bytesPerSecondRead=Čitaj:
main.vaultDetail.bytesPerSecondWritten=Piši:
main.vaultDetail.stats=Statistika sefa
### Missing
### Needs Migration
# Wrong File Alert
# Vault Options
## General
vaultOptions.general.vaultName=Ime sefa
## Mount
vaultOptions.mount.mountPoint.directoryPickerButton=Odaberi…
## Master Key
vaultOptions.masterkey.changePasswordBtn=Promjeni lozinku
# Recovery Key
# New Password
# Quit
@@ -1,137 +0,0 @@
# Locale Specific CSS files such as CJK, RTL,...
# Generics
## Button
generic.button.apply=Terapkan
generic.button.back=Kembali
generic.button.cancel=Batalkan
generic.button.change=Ganti
generic.button.close=Tutup
generic.button.copy=Salin
generic.button.copied=Tersalin!
generic.button.done=Selesai
generic.button.next=Lanjut
generic.button.print=Cetak
## Error
generic.error.title=Telah terjadi kesalahan tak terduga
generic.error.instruction=Ini harusnya tidak terjadi. Harap laporkan pesan kesalahan dibawah dan tulis deskripsi kenapa ini bisa terjadi.
# Defaults
defaults.vault.vaultName=Brankas
# Tray Menu
traymenu.showMainWindow=Tampilkan
traymenu.showPreferencesWindow=Preferensi
traymenu.lockAllVaults=Gembok Semua
traymenu.quitApplication=Keluar
traymenu.vault.unlock=Buka Gembok
traymenu.vault.lock=Gembok
traymenu.vault.reveal=Perlihatkan
# Add Vault Wizard
addvaultwizard.title=Tambah Brankas
## Welcome
addvaultwizard.welcome.newButton=Buat Brankas Baru
addvaultwizard.welcome.existingButton=Buka Brankas yg Sudah Ada
## New
### Name
addvaultwizard.new.nameInstruction=Buat sebuah nama untuk brankas
addvaultwizard.new.namePrompt=Nama Brankas
### Location
addvaultwizard.new.locationInstruction=Dimana Cryptomator seharusnya menyimpan brankas berkas terenkripsi kamu?
addvaultwizard.new.locationLabel=Lokasi penyimpanan
addvaultwizard.new.locationPrompt=
addvaultwizard.new.directoryPickerLabel=Sesuaikan Lokasi
addvaultwizard.new.directoryPickerButton=Pilih…
addvaultwizard.new.directoryPickerTitle=Pilih Folder
addvaultwizard.new.fileAlreadyExists=Brankas tidak dibuat disini karena beberapa item telah ada disini.
addvaultwizard.new.locationDoesNotExist=Brankas tidak bisa dibuat disini karena setidaknya satu jalur komponen tidak ada.
addvaultwizard.new.invalidName=Nama brankas salah. Harap pilih nama folder yang umum.
### Password
addvaultwizard.new.createVaultBtn=Buat Brankas
addvaultwizard.new.generateRecoveryKeyChoice=Kamu tidak dapat mengakses data tanpa kata sandi kamu. Apa kamu ingin sebuah kunci pemulihan buat jaga-jaga?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ya, sedia payung sebelum hujan
addvaultwizard.new.generateRecoveryKeyChoice.no=Tidak, terima kasih, Saya tidak akan kehilangan kata sandi saya
### Information
addvault.new.readme.storageLocation.fileName=PENTING.rtf
addvault.new.readme.storageLocation.1=⚠️ BERKAS BRANKAS ⚠️
addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan brankas kamu.
addvault.new.readme.storageLocation.3=JANGAN
addvault.new.readme.storageLocation.6=Jika kamu ingin mengenkripsi berkas dan melihat isi brankas, lakukan hal berikut:
addvault.new.readme.storageLocation.7=1. Tambahkan brankas ini ke Cryptomator.
addvault.new.readme.storageLocation.8=2. Buka gembok brankas di Cryptomator.
addvault.new.readme.storageLocation.9=3. Buka akses lokasi dengan mengklik tombol "Perlihatkan".
addvault.new.readme.storageLocation.10=Jika kamu butuh bantuan, kunjungi dokumentasi: %s
addvault.new.readme.accessLocation.fileName=SELAMAT_DATANG.rtf
addvault.new.readme.accessLocation.1=🔐️ ISI TERENKRIPSI 🔐️
addvault.new.readme.accessLocation.2=Ini adalah lokasi akses brankas kamu.
## Existing
addvaultwizard.existing.chooseBtn=Pilih…
## Success
# Remove Vault
# Change Password
# Forget Password
# Unlock
unlock.unlockBtn=Buka Gembok
## Success
## Failure
### Invalid Mount Point
# Migration
## Start
## Run
## Sucess
## Missing file system capabilities
## Impossible
# Preferences
preferences.title=Preferensi
## General
## Volume
## Updates
## Donation Key
## About
# Vault Statistics
## Read
## Write
# Main Window
main.closeBtn.tooltip=Tutup
main.preferencesBtn.tooltip=Preferensi
## Drag 'n' Drop
## Vault List
main.vaultlist.addVaultBtn=Tambah Brankas
## Vault Detail
### Welcome
### Locked
### Unlocked
main.vaultDetail.lockBtn=Gembok
### Missing
### Needs Migration
# Wrong File Alert
# Vault Options
## General
vaultOptions.general.vaultName=Nama Brankas
## Mount
vaultOptions.mount.mountPoint.directoryPickerButton=Pilih…
## Master Key
# Recovery Key
# New Password
passwordStrength.messageLabel.0=Sangat lemah
passwordStrength.messageLabel.1=Lemah
passwordStrength.messageLabel.2=Cukup
passwordStrength.messageLabel.3=Kuat
passwordStrength.messageLabel.4=Sangat kuat
# Quit
quit.prompt=Keluar aplikasi? Terdapat brankas yg belum digembok.
quit.lockAndQuit=Gembok dan Keluar
+601
View File
@@ -0,0 +1,601 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.6.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
<name>cryptomator.org</name>
<url>https://cryptomator.org</url>
</organization>
<developers>
<developer>
<name>Sebastian Stenzel</name>
<email>sebastian.stenzel@gmail.com</email>
<timezone>+1</timezone>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>16</project.jdk.version>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.1.0-beta8</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.0.0-rc1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.0-beta1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.1</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.1</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.4</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>16</javafx.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jwt.version>3.17.0</jwt.version>
<easybind.version>2.2</easybind.version>
<guava.version>30.1.1-jre</guava.version>
<dagger.version>2.37</dagger.version>
<gson.version>2.8.6</gson.version>
<slf4j.version>1.7.31</slf4j.version>
<logback.version>1.2.3</logback.version>
<!-- test dependencies -->
<junit.jupiter.version>5.7.2</junit.jupiter.version>
<mockito.version>3.11.2</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
</properties>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>${cryptomator.cryptofs.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>fuse-nio-adapter</artifactId>
<version>${cryptomator.fuse.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>dokany-nio-adapter</artifactId>
<version>${cryptomator.dokany.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter</artifactId>
<version>${cryptomator.webdav.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-api</artifactId>
<version>${cryptomator.integrations.version}</version>
</dependency>
<!-- JavaFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>
<groupId>com.tobiasdiez</groupId>
<artifactId>easybind</artifactId>
<version>${easybind.version}</version>
</dependency>
<!-- Zxcvbn -->
<dependency>
<groupId>com.nulab-inc</groupId>
<artifactId>zxcvbn</artifactId>
<version>1.3.0</version>
</dependency>
<!-- Google -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>${dagger.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- JUnit / Mockito / Hamcrest -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.0.3</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>${project.jdk.version}</release>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${dagger.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!-- adds Implementation-Version which can be read during runtime -->
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/package</directory>
<includes>
<include>version.txt</include>
<include>ffi-version.txt</include>
<include>launcher-mac.sh</include>
<include>launcher-linux.sh</include>
<include>launcher-win.bat</include>
<include>LICENSE.txt</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<excludeClassifiers>linux,mac,win</excludeClassifiers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<executions>
<execution>
<id>add-third-party</id>
<goals>
<goal>add-third-party</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
<includedScopes>compile</includedScopes>
<excludedGroups>org\.cryptomator</excludedGroups>
<licenseMerges>
<licenseMerge>Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0</licenseMerge>
<licenseMerge>MIT License|The MIT License (MIT)|The MIT License|MIT license</licenseMerge>
<licenseMerge>LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1</licenseMerge>
<licenseMerge>GPLv2|GNU General Public License Version 2</licenseMerge>
<licenseMerge>GPLv2+CE|CDDL + GPLv2 with classpath exception</licenseMerge>
</licenseMerges>
<fileTemplate>${project.basedir}/src/license/template.ftl</fileTemplate>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/*_*</exclude>
<exclude>**/Dagger*</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>dependency-check</id>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<cveValidForHours>24</cveValidForHours>
<failBuildOnCVSS>0</failBuildOnCVSS>
<skipTestScope>true</skipTestScope>
<detail>true</detail>
<suppressionFile>suppression.xml</suppressionFile>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-mac</artifactId>
<version>${cryptomator.integrations.mac.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-mac</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-mac.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-mac</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-mac-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>mac</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>linux</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-linux</artifactId>
<version>${cryptomator.integrations.linux.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-linux</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-linux.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-linux</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-linux-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>linux</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>windows</id>
<activation>
<os>
<family>windows</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-win</artifactId>
<version>${cryptomator.integrations.win.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble-win</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly-win.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
<finalName>buildkit-win</finalName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-win-libs</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeGroupIds>org.openjfx</includeGroupIds>
<classifier>win</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
@@ -15,6 +15,8 @@ import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultListModule;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,6 +27,8 @@ import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -34,7 +38,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Module(subcomponents = {VaultComponent.class}, includes = {KeychainModule.class})
@Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class})
public abstract class CommonsModule {
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
@@ -42,15 +46,34 @@ public abstract class CommonsModule {
private static final int NUM_CORE_BG_THREADS = 6;
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
@SuppressWarnings("SpellCheckingInspection")
@Provides
@Singleton
@Named("licensePublicKey")
static String provideLicensePublicKey() {
// in PEM format without the dash-escaped begin/end lines
return "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB7NfnqiZbg2KTmoflmZ71PbXru7oW" //
+ "fmnV2yv3eDjlDfGruBrqz9TtXBZV/eYWt31xu1osIqaT12lKBvZ511aaAkIBeOEV" //
+ "gwcBIlJr6kUw7NKzeJt7r2rrsOyQoOG2nWc/Of/NBqA3mIZRHk5Aq1YupFdD26QE" //
+ "r0DzRyj4ixPIt38CQB8=";
return """
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB7NfnqiZbg2KTmoflmZ71PbXru7oW\
fmnV2yv3eDjlDfGruBrqz9TtXBZV/eYWt31xu1osIqaT12lKBvZ511aaAkIBeOEV\
gwcBIlJr6kUw7NKzeJt7r2rrsOyQoOG2nWc/Of/NBqA3mIZRHk5Aq1YupFdD26QE\
r0DzRyj4ixPIt38CQB8=\
""";
}
@Provides
@Singleton
static SecureRandom provideCSPRNG() {
try {
return SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e);
}
}
@Provides
@Singleton
static MasterkeyFileAccess provideMasterkeyFileAccess(SecureRandom csprng) {
return new MasterkeyFileAccess(Constants.PEPPER, csprng);
}
@Provides
@@ -66,12 +89,6 @@ public abstract class CommonsModule {
return settingsProvider.get();
}
@Provides
@Singleton
static ObservableList<Vault> provideVaultList(VaultListManager vaultListManager) {
return vaultListManager.getVaultList();
}
@Provides
@Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
@@ -0,0 +1,10 @@
package org.cryptomator.common;
public interface Constants {
String MASTERKEY_FILENAME = "masterkey.cryptomator";
String MASTERKEY_BACKUP_SUFFIX = ".bkup";
String VAULTCONFIG_FILENAME = "vault.cryptomator";
byte[] PEPPER = new byte[0];
}
@@ -21,14 +21,13 @@ import java.util.stream.StreamSupport;
public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final String USER_HOME = System.getProperty("user.home");
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
private static final char PATH_LIST_SEP = ':';
private static final int DEFAULT_MIN_PW_LENGTH = 8;
@Inject
public Environment() {
LOG.debug("user.home: {}", System.getProperty("user.home"));
LOG.debug("java.library.path: {}", System.getProperty("java.library.path"));
LOG.debug("user.language: {}", System.getProperty("user.language"));
LOG.debug("user.region: {}", System.getProperty("user.region"));
@@ -40,6 +39,7 @@ public class Environment {
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
}
@@ -75,6 +75,11 @@ public class Environment {
return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
}
public boolean showTrayIcon() {
return Boolean.getBoolean("cryptomator.showTrayIcon");
}
@Deprecated // TODO: remove as soon as custom mount path works properly on Win+Fuse
public boolean useExperimentalFuse() {
return Boolean.getBoolean("fuse.experimental");
}
@@ -92,16 +97,21 @@ public class Environment {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);
}
// visible for testing
Stream<Path> getPaths(String propertyName) {
// visible for testing
public Path getHomeDir() {
return getPath("user.home").orElseThrow();
}
// visible for testing
public Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
}
private Path replaceHomeDir(Path path) {
if (path.startsWith(RELATIVE_HOME_DIR)) {
return ABSOLUTE_HOME_DIR.resolve(RELATIVE_HOME_DIR.relativize(path));
return getHomeDir().resolve(RELATIVE_HOME_DIR.relativize(path));
} else {
return path;
}
@@ -113,8 +123,8 @@ public class Environment {
return Stream.empty();
} else {
Iterable<String> iter = Splitter.on(separator).split(value);
Spliterator<String> spliter = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
return StreamSupport.stream(spliter, false);
Spliterator<String> spliterator = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
return StreamSupport.stream(spliterator, false);
}
}
}
@@ -33,8 +33,8 @@ class LicenseChecker {
try {
byte[] keyBytes = BaseEncoding.base64().decode(pemEncodedPublicKey);
PublicKey key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(keyBytes));
if (key instanceof ECPublicKey) {
return (ECPublicKey) key;
if (key instanceof ECPublicKey k) {
return k;
} else {
throw new IllegalStateException("Key not an EC public key.");
}
@@ -60,17 +60,17 @@ public class SemVerComparator implements Comparator<String> {
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
int subversionComparisionResult = 0;
int subversionComparisonResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
subversionComparisionResult = v1 - v2;
subversionComparisonResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
subversionComparisionResult = vComps1[i].compareTo(vComps2[i]);
subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
}
if (subversionComparisionResult != 0) {
return subversionComparisionResult;
if (subversionComparisonResult != 0) {
return subversionComparisonResult;
}
}
@@ -59,8 +59,10 @@ public class KeychainManager implements KeychainAccessProvider {
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().changePassphrase(key, passphrase);
setPassphraseStored(key, true);
if (isPassphraseStored(key)) {
getKeychainOrFail().changePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
}
@Override
@@ -68,6 +70,11 @@ public class KeychainManager implements KeychainAccessProvider {
return keychain.getValue() != null;
}
@Override
public boolean isLocked() {
return keychain.getValue() == null || keychain.get().isLocked();
}
/**
* Checks if the keychain knows a passphrase for the given key.
* <p>
@@ -34,11 +34,11 @@ public class KeychainModule {
@Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> {
var selectedProviderClass = settings.keychainBackend().get().getProviderClass();
var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findAny().orElse(null);
return selectedProvider.orElse(fallbackProvider);
}, settings.keychainBackend());
}, settings.keychainProvider());
}
}
@@ -8,9 +8,7 @@ import javax.inject.Inject;
import java.nio.file.Path;
import java.util.Optional;
public class AvailableDriveLetterChooser implements MountPointChooser {
public static final int PRIORITY = 200;
class AvailableDriveLetterChooser implements MountPointChooser {
private final WindowsDriveLetters windowsDriveLetters;
@@ -28,9 +26,4 @@ public class AvailableDriveLetterChooser implements MountPointChooser {
public Optional<Path> chooseMountPoint(Volume caller) {
return this.windowsDriveLetters.getAvailableDriveLetterPath();
}
@Override
public int getPriority() {
return PRIORITY;
}
}
@@ -9,9 +9,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
public class CustomDriveLetterChooser implements MountPointChooser {
public static final int PRIORITY = 100;
class CustomDriveLetterChooser implements MountPointChooser {
private final VaultSettings vaultSettings;
@@ -29,9 +27,4 @@ public class CustomDriveLetterChooser implements MountPointChooser {
public Optional<Path> chooseMountPoint(Volume caller) {
return this.vaultSettings.getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get);
}
@Override
public int getPriority() {
return PRIORITY;
}
}
@@ -20,9 +20,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
public class CustomMountPointChooser implements MountPointChooser {
public static final int PRIORITY = 0;
class CustomMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
@@ -94,8 +92,4 @@ public class CustomMountPointChooser implements MountPointChooser {
}
}
@Override
public int getPriority() {
return PRIORITY;
}
}
@@ -0,0 +1,42 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
import javax.inject.Inject;
import java.nio.file.Path;
import java.util.Optional;
class MacVolumeMountChooser implements MountPointChooser {
private static final Path VOLUME_PATH = Path.of("/Volumes");
private final VaultSettings vaultSettings;
private final MountPointHelper helper;
@Inject
public MacVolumeMountChooser(VaultSettings vaultSettings, MountPointHelper helper) {
this.vaultSettings = vaultSettings;
this.helper = helper;
}
@Override
public boolean isApplicable(Volume caller) {
return SystemUtils.IS_OS_MAC;
}
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return Optional.of(helper.chooseTemporaryMountPoint(vaultSettings, VOLUME_PATH));
}
@Override
public boolean prepare(Volume caller, Path mountPoint) {
// https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
// In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
// starting with version 3.5.0, FUSE will create non-existent mount points automatically.
// Therefore we don't need to prepare anything.
return false;
}
}
@@ -1,6 +1,6 @@
package org.cryptomator.common.mountpoint;
import com.google.common.base.Preconditions;
import dagger.multibindings.IntKey;
import org.cryptomator.common.vaults.Volume;
import java.nio.file.Path;
@@ -12,13 +12,13 @@ import java.util.SortedSet;
* preparation of a mountpoint or an exception otherwise.<br>
* <p>All <i>MountPointChoosers (MPCs)</i> need to implement this class and must be added to
* the pool of possible MPCs by the {@link MountPointChooserModule MountPointChooserModule.}
* The MountPointChooserModule will sort them according to their {@link #getPriority() priority.}
* The MountPointChooserModule will sort them according to their {@link IntKey IntKey priority.}
* The priority must be defined by the developer to reflect a useful execution order.<br>
* A specific priority <b>must not</b> be assigned to more than one MPC at a time;
* the result of having two MPCs with equal priority is undefined.
*
* <p>MPCs are executed by a {@link Volume} in ascending order of their priority
* (smaller priorities are tried first) to find and prepare a suitable mountpoint for the volume.
* <p>MPCs are executed by a {@link Volume} in descending order of their priority
* (higher priorities are tried first) to find and prepare a suitable mountpoint for the volume.
* The volume has access to a {@link SortedSet} of MPCs in this specific order,
* that is provided by the Module. The Set contains all available Choosers, even if they
* are not {@link #isApplicable(Volume) applicable} for the Vault/Volume. The Volume must
@@ -47,7 +47,7 @@ import java.util.SortedSet;
* If the preparation succeeds {@link #cleanup(Volume, Path)} can be used after unmount to do any
* remaining cleanup.
*/
public interface MountPointChooser extends Comparable<MountPointChooser> {
public interface MountPointChooser {
/**
* Called by the {@link Volume} to determine whether this MountPointChooser is
@@ -135,35 +135,4 @@ public interface MountPointChooser extends Comparable<MountPointChooser> {
//NO-OP
}
/**
* Called by the {@link MountPointChooserModule} to sort the available MPCs
* and determine their execution order.
* The priority must be defined by the developer to reflect a useful execution order.
* MPCs with lower priorities will be placed at lower indices in the resulting
* {@link SortedSet} and will be executed with higher probability.<br>
* A specific priority <b>must not</b> be assigned to more than one MPC at a time;
* the result of having two MPCs with equal priority is undefined.
*
* @return the priority of this MPC.
*/
int getPriority();
/**
* Called by the {@link Volume} to determine the execution order of the registered MPCs.
* <b>Implementations usually may not override this method.</b> This default implementation
* sorts the MPCs in ascending order of their {@link #getPriority() priority.}<br>
* <br>
* <b>Original description:</b>
* <p>{@inheritDoc}
*
* @implNote This default implementation sorts the MPCs in ascending order
* of their {@link #getPriority() priority.}
*/
@Override
default int compareTo(MountPointChooser other) {
Preconditions.checkNotNull(other, "Other must not be null!");
//Sort by priority (ascending order)
return Integer.compare(this.getPriority(), other.getPriority());
}
}
@@ -1,15 +1,18 @@
package org.cryptomator.common.mountpoint;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
import dagger.multibindings.IntKey;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.PerVault;
import javax.inject.Named;
import java.util.Set;
import java.util.SortedSet;
import java.util.Comparator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Dagger-Module for {@link MountPointChooser MountPointChoosers.}<br>
@@ -21,30 +24,41 @@ import java.util.SortedSet;
public abstract class MountPointChooserModule {
@Binds
@IntoSet
@IntoMap
@IntKey(1000)
@PerVault
public abstract MountPointChooser bindCustomMountPointChooser(CustomMountPointChooser chooser);
@Binds
@IntoSet
@IntoMap
@IntKey(900)
@PerVault
public abstract MountPointChooser bindCustomDriveLetterChooser(CustomDriveLetterChooser chooser);
@Binds
@IntoSet
@IntoMap
@IntKey(800)
@PerVault
public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
@Binds
@IntoSet
@IntoMap
@IntKey(101)
@PerVault
public abstract MountPointChooser bindMacVolumeMountChooser(MacVolumeMountChooser chooser);
@Binds
@IntoMap
@IntKey(100)
@PerVault
public abstract MountPointChooser bindTemporaryMountPointChooser(TemporaryMountPointChooser chooser);
@Provides
@PerVault
@Named("orderedMountPointChoosers")
public static SortedSet<MountPointChooser> provideOrderedMountPointChoosers(Set<MountPointChooser> choosers) {
//Sort by natural order. The natural order is defined by MountPointChooser#compareTo
return ImmutableSortedSet.copyOf(choosers);
public static Iterable<MountPointChooser> provideOrderedMountPointChoosers(Map<Integer, MountPointChooser> choosers) {
SortedMap<Integer, MountPointChooser> sortedChoosers = new TreeMap<>(Comparator.reverseOrder());
sortedChoosers.putAll(choosers);
return Iterables.unmodifiableIterable(sortedChoosers.values());
}
}
@@ -0,0 +1,120 @@
package org.cryptomator.common.mountpoint;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Optional;
@Singleton
class MountPointHelper {
public static Logger LOG = LoggerFactory.getLogger(MountPointHelper.class);
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
private final Optional<Path> tmpMountPointDir;
private volatile boolean unmountDebrisCleared = false;
@Inject
public MountPointHelper(Environment env) {
this.tmpMountPointDir = env.getMountPointsDir();
}
public Path chooseTemporaryMountPoint(VaultSettings vaultSettings, Path parentDir) {
String basename = vaultSettings.mountName().get();
//regular
Path mountPoint = parentDir.resolve(basename);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id
mountPoint = parentDir.resolve(basename + " (" + vaultSettings.getId() + ")");
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id and count
for (int i = 1; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
mountPoint = parentDir.resolve(basename + "_(" + vaultSettings.getId() + ")_" + i);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
}
LOG.error("Failed to find feasible mountpoint at {}{}{}_x. Giving up after {} attempts.", parentDir, File.separator, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES);
return null;
}
public synchronized void clearIrregularUnmountDebrisIfNeeded() {
if (unmountDebrisCleared || tmpMountPointDir.isEmpty()) {
return; // nothing to do
}
if (Files.exists(tmpMountPointDir.get(), LinkOption.NOFOLLOW_LINKS)) {
clearIrregularUnmountDebris(tmpMountPointDir.get());
}
unmountDebrisCleared = true;
}
private void clearIrregularUnmountDebris(Path dirContainingMountPoints) {
IOException cleanupFailed = new IOException("Cleanup failed");
try {
LOG.debug("Performing cleanup of mountpoint dir {}.", dirContainingMountPoints);
for (Path p : Files.newDirectoryStream(dirContainingMountPoints)) {
try {
var attr = Files.readAttributes(p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
if (attr.isOther() && attr.isDirectory()) { // yes, this is possible with windows junction points -.-
Files.delete(p);
} else if (attr.isDirectory()) {
deleteEmptyDir(p);
} else if (attr.isSymbolicLink()) {
deleteDeadLink(p);
} else {
LOG.debug("Found non-directory element in mountpoint dir: {}", p);
}
} catch (IOException e) {
cleanupFailed.addSuppressed(e);
}
}
if (cleanupFailed.getSuppressed().length > 0) {
throw cleanupFailed;
}
} catch (IOException e) {
LOG.warn("Unable to perform cleanup of mountpoint dir {}.", dirContainingMountPoints, e);
} finally {
unmountDebrisCleared = true;
}
}
private void deleteEmptyDir(Path dir) throws IOException {
assert Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS);
try {
ensureIsEmpty(dir);
Files.delete(dir); // attempt to delete dir non-recursively (will fail, if there are contents)
} catch (DirectoryNotEmptyException e) {
LOG.info("Found non-empty directory in mountpoint dir: {}", dir);
}
}
private void deleteDeadLink(Path symlink) throws IOException {
assert Files.isSymbolicLink(symlink);
if (Files.notExists(symlink)) { // following link: target does not exist
Files.delete(symlink);
}
}
private void ensureIsEmpty(Path dir) throws IOException {
if (Files.newDirectoryStream(dir).iterator().hasNext()) {
throw new DirectoryNotEmptyException(dir.toString());
}
}
}
@@ -1,6 +1,5 @@
package org.cryptomator.common.mountpoint;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
@@ -8,27 +7,24 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
public class TemporaryMountPointChooser implements MountPointChooser {
public static final int PRIORITY = 300;
class TemporaryMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(TemporaryMountPointChooser.class);
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
private final VaultSettings vaultSettings;
private final Environment environment;
private final MountPointHelper helper;
@Inject
public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment) {
public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment, MountPointHelper helper) {
this.vaultSettings = vaultSettings;
this.environment = environment;
this.helper = helper;
}
@Override
@@ -42,41 +38,15 @@ public class TemporaryMountPointChooser implements MountPointChooser {
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
return this.environment.getMountPointsDir().map(this::choose);
}
private Path choose(Path parent) {
String basename = this.vaultSettings.mountName().get();
//regular
Path mountPoint = parent.resolve(basename);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id
mountPoint = parent.resolve(basename + " (" +vaultSettings.getId() + ")");
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id and count
for (int i = 1; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
mountPoint = parent.resolve(basename + "_(" +vaultSettings.getId() + ")_"+i);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
}
LOG.error("Failed to find feasible mountpoint at {}{}{}_x. Giving up after {} attempts.", parent, File.separator, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES);
return null;
assert environment.getMountPointsDir().isPresent();
//clean leftovers of not-regularly unmounted vaults
//see https://github.com/cryptomator/cryptomator/issues/1013 and https://github.com/cryptomator/cryptomator/issues/1061
helper.clearIrregularUnmountDebrisIfNeeded();
return this.environment.getMountPointsDir().map(dir -> this.helper.chooseTemporaryMountPoint(this.vaultSettings, dir));
}
@Override
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
// https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
// In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
// starting with version 3.5.0, FUSE will create non-existent mount points automatically.
if (SystemUtils.IS_OS_MAC && mountPoint.getParent().equals(Paths.get("/Volumes"))) {
return false;
}
try {
switch (caller.getMountPointRequirement()) {
case PARENT_NO_MOUNT_POINT -> {
@@ -114,8 +84,4 @@ public class TemporaryMountPointChooser implements MountPointChooser {
}
}
@Override
public int getPriority() {
return PRIORITY;
}
}
@@ -9,6 +9,7 @@
package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
@@ -29,7 +30,7 @@ public class Settings {
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
public static final boolean DEFAULT_CHECK_FOR_UDPATES = false;
public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
public static final boolean DEFAULT_START_HIDDEN = false;
public static final int DEFAULT_PORT = 42427;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
@@ -37,13 +38,15 @@ public class Settings {
public static final boolean DEFAULT_DEBUG_MODE = false;
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = SystemUtils.IS_OS_WINDOWS ? KeychainBackend.WIN_SYSTEM_KEYCHAIN : SystemUtils.IS_OS_MAC ? KeychainBackend.MAC_SYSTEM_KEYCHAIN : KeychainBackend.GNOME;
@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 NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
private static final String DEFAULT_LICENSE_KEY = "";
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
@@ -51,16 +54,20 @@ public class Settings {
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
private final ObjectProperty<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
private final ObjectProperty<KeychainBackend> keychainBackend = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_BACKEND);
private final ObjectProperty<String> keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
private final BooleanProperty showTrayIcon;
private Consumer<Settings> saveCmd;
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings() {
Settings(Environment env) {
this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
directories.addListener(this::somethingChanged);
askedForUpdateCheck.addListener(this::somethingChanged);
checkForUpdates.addListener(this::somethingChanged);
@@ -71,9 +78,11 @@ public class Settings {
debugMode.addListener(this::somethingChanged);
preferredVolumeImpl.addListener(this::somethingChanged);
theme.addListener(this::somethingChanged);
keychainBackend.addListener(this::somethingChanged);
keychainProvider.addListener(this::somethingChanged);
userInterfaceOrientation.addListener(this::somethingChanged);
licenseKey.addListener(this::somethingChanged);
showMinimizeButton.addListener(this::somethingChanged);
showTrayIcon.addListener(this::somethingChanged);
}
void setSaveCmd(Consumer<Settings> saveCmd) {
@@ -132,7 +141,7 @@ public class Settings {
return theme;
}
public ObjectProperty<KeychainBackend> keychainBackend() { return keychainBackend; }
public ObjectProperty<String> keychainProvider() { return keychainProvider; }
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
return userInterfaceOrientation;
@@ -141,4 +150,12 @@ public class Settings {
public StringProperty licenseKey() {
return licenseKey;
}
public BooleanProperty showMinimizeButton() {
return showMinimizeButton;
}
public BooleanProperty showTrayIcon() {
return showTrayIcon;
}
}
@@ -9,19 +9,29 @@ import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.geometry.NodeOrientation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
private final Environment env;
@Inject
public SettingsJsonAdapter(Environment env) {
this.env = env;
}
@Override
public void write(JsonWriter out, Settings value) throws IOException {
@@ -38,8 +48,10 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
out.name("theme").value(value.theme().get().name());
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
out.name("keychainBackend").value(value.keychainBackend().get().name());
out.name("keychainProvider").value(value.keychainProvider().get());
out.name("licenseKey").value(value.licenseKey().get());
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
out.name("showTrayIcon").value(value.showTrayIcon().get());
out.endObject();
}
@@ -53,7 +65,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
@Override
public Settings read(JsonReader in) throws IOException {
Settings settings = new Settings();
Settings settings = new Settings(env);
in.beginObject();
while (in.hasNext()) {
@@ -70,8 +82,10 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "preferredVolumeImpl" -> settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
case "keychainBackend" -> settings.keychainBackend().set(parseKeychainBackend(in.nextString()));
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
case "licenseKey" -> settings.licenseKey().set(in.nextString());
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
@@ -110,15 +124,6 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
}
}
private KeychainBackend parseKeychainBackend(String backendName) {
try {
return KeychainBackend.valueOf(backendName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid keychain backend {}. Defaulting to {}.", backendName, Settings.DEFAULT_KEYCHAIN_BACKEND);
return Settings.DEFAULT_KEYCHAIN_BACKEND;
}
}
private NodeOrientation parseUiOrientation(String uiOrientationName) {
try {
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
@@ -137,5 +142,4 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
in.endArray();
return result;
}
}
@@ -8,13 +8,13 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import com.google.common.base.Suppliers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.cryptomator.common.Environment;
import org.cryptomator.common.LazyInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,14 +48,15 @@ public class SettingsProvider implements Supplier<Settings> {
private static final long SAVE_DELAY_MS = 1000;
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
private final AtomicReference<Settings> settings = new AtomicReference<>();
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
private final SettingsJsonAdapter settingsJsonAdapter;
private final Environment env;
private final ScheduledExecutorService scheduler;
private final Gson gson;
@Inject
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
this.settingsJsonAdapter = settingsJsonAdapter;
this.env = env;
this.scheduler = scheduler;
this.gson = new GsonBuilder() //
@@ -66,11 +67,11 @@ public class SettingsProvider implements Supplier<Settings> {
@Override
public Settings get() {
return LazyInitializer.initializeLazily(settings, this::load);
return settings.get();
}
private Settings load() {
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings());
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings(env));
settings.setSaveCmd(this::scheduleSave);
return settings;
}
@@ -90,7 +91,7 @@ public class SettingsProvider implements Supplier<Settings> {
}
} catch (NoSuchFileException e) {
return Stream.empty();
} catch (IOException e) {
} catch (IOException | JsonParseException e) {
LOG.warn("Exception while loading settings from " + path, e);
return Stream.empty();
}
@@ -100,7 +101,7 @@ public class SettingsProvider implements Supplier<Settings> {
if (settings == null) {
return;
}
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
settingsPath.ifPresent(path -> {
Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
@@ -8,7 +8,7 @@ public enum UiTheme {
AUTOMATIC("preferences.general.theme.automatic");
public static UiTheme[] applicableValues() {
if (SystemUtils.IS_OS_MAC) {
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
return values();
} else {
return new UiTheme[]{LIGHT, DARK};
@@ -24,8 +24,6 @@ import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
/**
* The settings specific to a single vault.
@@ -33,12 +31,14 @@ import java.util.stream.Collectors;
public class VaultSettings {
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
public static final boolean DEFAULT_USES_READONLY_MODE = false;
public static final String DEFAULT_MOUNT_FLAGS = "";
public static final int DEFAULT_FILENAME_LENGTH_LIMIT = -1;
public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
private static final Random RNG = new Random();
@@ -47,14 +47,15 @@ public class VaultSettings {
private final StringProperty displayName = new SimpleStringProperty();
private final StringProperty winDriveLetter = new SimpleStringProperty();
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
private final StringProperty customMountPath = new SimpleStringProperty();
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
private final IntegerProperty filenameLengthLimit = new SimpleIntegerProperty(DEFAULT_FILENAME_LENGTH_LIMIT);
private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
private final StringBinding mountName;
public VaultSettings(String id) {
@@ -63,7 +64,7 @@ public class VaultSettings {
}
Observable[] observables() {
return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, filenameLengthLimit, actionAfterUnlock};
return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, maxCleartextFilenameLength, actionAfterUnlock, autoLockWhenIdle, autoLockIdleSeconds};
}
public static VaultSettings withRandomId() {
@@ -152,8 +153,8 @@ public class VaultSettings {
return mountFlags;
}
public IntegerProperty filenameLengthLimit() {
return filenameLengthLimit;
public IntegerProperty maxCleartextFilenameLength() {
return maxCleartextFilenameLength;
}
public ObjectProperty<WhenUnlocked> actionAfterUnlock() {
@@ -164,6 +165,14 @@ public class VaultSettings {
return actionAfterUnlock.get();
}
public BooleanProperty autoLockWhenIdle() {
return autoLockWhenIdle;
}
public IntegerProperty autoLockIdleSeconds() {
return autoLockIdleSeconds;
}
/* Hashcode/Equals */
@Override
@@ -173,8 +182,7 @@ public class VaultSettings {
@Override
public boolean equals(Object obj) {
if (obj instanceof VaultSettings && obj.getClass().equals(this.getClass())) {
VaultSettings other = (VaultSettings) obj;
if (obj instanceof VaultSettings other && obj.getClass().equals(this.getClass())) {
return Objects.equals(this.id, other.id);
} else {
return false;
@@ -29,8 +29,10 @@ class VaultSettingsJsonAdapter {
out.name("customMountPath").value(value.customMountPath().get());
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
out.name("mountFlags").value(value.mountFlags().get());
out.name("filenameLengthLimit").value(value.filenameLengthLimit().get());
out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
out.name("autoLockWhenIdle").value(value.autoLockWhenIdle().get());
out.name("autoLockIdleSeconds").value(value.autoLockIdleSeconds().get());
out.endObject();
}
@@ -42,12 +44,14 @@ class VaultSettingsJsonAdapter {
String customMountPath = null;
String winDriveLetter = null;
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
int filenameLengthLimit = VaultSettings.DEFAULT_FILENAME_LENGTH_LIMIT;
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
in.beginObject();
while (in.hasNext()) {
@@ -64,8 +68,10 @@ class VaultSettingsJsonAdapter {
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
case "mountFlags" -> mountFlags = in.nextString();
case "filenameLengthLimit" -> filenameLengthLimit = in.nextInt();
case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
in.skipValue();
@@ -88,8 +94,10 @@ class VaultSettingsJsonAdapter {
vaultSettings.customMountPath().set(customMountPath);
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
vaultSettings.mountFlags().set(mountFlags);
vaultSettings.filenameLengthLimit().set(filenameLengthLimit);
vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
return vaultSettings;
}
@@ -0,0 +1,46 @@
package org.cryptomator.common.vaults;
import com.google.common.collect.Iterables;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import java.nio.file.Path;
import java.util.Optional;
public abstract class AbstractVolume implements Volume {
private final Iterable<MountPointChooser> choosers;
protected Path mountPoint;
private boolean cleanupRequired;
private MountPointChooser usedChooser;
public AbstractVolume(Iterable<MountPointChooser> choosers) {
this.choosers = choosers;
}
protected Path determineMountPoint() throws InvalidMountPointException {
var applicableChoosers = Iterables.filter(choosers, c -> c.isApplicable(this));
for (var chooser : applicableChoosers) {
Optional<Path> chosenPath = chooser.chooseMountPoint(this);
if (chosenPath.isEmpty()) { // chooser couldn't find a feasible mountpoint
continue;
}
this.cleanupRequired = chooser.prepare(this, chosenPath.get());
this.usedChooser = chooser;
return chosenPath.get();
}
throw new InvalidMountPointException(String.format("No feasible MountPoint found by choosers: %s", applicableChoosers));
}
protected void cleanupMountPoint() {
if (this.cleanupRequired) {
this.usedChooser.cleanup(this, this.mountPoint);
}
}
@Override
public Optional<Path> getMountPoint() {
return Optional.ofNullable(mountPoint);
}
}
@@ -0,0 +1,60 @@
package org.cryptomator.common.vaults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ObservableList;
import java.time.Instant;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Singleton
public class AutoLocker {
private static final Logger LOG = LoggerFactory.getLogger(AutoLocker.class);
private final ScheduledExecutorService scheduler;
private final ObservableList<Vault> vaultList;
@Inject
public AutoLocker(ScheduledExecutorService scheduler, ObservableList<Vault> vaultList) {
this.scheduler = scheduler;
this.vaultList = vaultList;
}
public void init() {
scheduler.scheduleAtFixedRate(this::tick, 0, 1, TimeUnit.MINUTES);
}
private void tick() {
vaultList.stream() // all vaults
.filter(Vault::isUnlocked) // unlocked vaults
.filter(this::exceedsIdleTime) // idle vaults
.forEach(this::autolock);
}
private void autolock(Vault vault) {
try {
vault.lock(false);
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
} catch (Volume.VolumeException | LockNotCompletedException e) {
LOG.error("Autolocking failed.", e);
}
}
private boolean exceedsIdleTime(Vault vault) {
assert vault.isUnlocked();
// TODO: shouldn't we read these properties from within FX Application Thread?
if (vault.getVaultSettings().autoLockWhenIdle().get()) {
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
return deadline.isBefore(Instant.now());
} else {
return false;
}
}
}
@@ -5,16 +5,15 @@ import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.dokany.DokanyMountFailedException;
import org.cryptomator.frontend.dokany.Mount;
import org.cryptomator.frontend.dokany.MountFactory;
import org.cryptomator.frontend.dokany.MountFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.SortedSet;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
public class DokanyVolume extends AbstractVolume {
@@ -23,15 +22,13 @@ public class DokanyVolume extends AbstractVolume {
private static final String FS_TYPE_NAME = "CryptomatorFS";
private final VaultSettings vaultSettings;
private final MountFactory mountFactory;
private Mount mount;
@Inject
public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
public DokanyVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
this.vaultSettings = vaultSettings;
this.mountFactory = new MountFactory(executorService);
}
@Override
@@ -40,12 +37,11 @@ public class DokanyVolume extends AbstractVolume {
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
String mountName = vaultSettings.mountName().get();
try {
this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip());
} catch (MountFailedException e) {
this.mount = MountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip(), onExitAction);
} catch (DokanyMountFailedException e) {
if (vaultSettings.getCustomMountPath().isPresent()) {
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
}
@@ -54,19 +50,35 @@ public class DokanyVolume extends AbstractVolume {
}
@Override
public void reveal() throws VolumeException {
boolean success = mount.reveal();
if (!success) {
throw new VolumeException("Reveal failed.");
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@Override
public void unmount() {
mount.close();
public void unmount() throws VolumeException {
try {
mount.unmount();
} catch (IllegalStateException e) {
throw new VolumeException("Unmount Failed.", e);
}
cleanupMountPoint();
}
@Override
public void unmountForced() {
mount.unmountForced();
cleanupMountPoint();
}
@Override
public boolean supportsForcedUnmount() {
return true;
}
@Override
public boolean isSupported() {
return DokanyVolume.isSupportedStatic();
@@ -1,13 +1,13 @@
package org.cryptomator.common.vaults;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterators;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.frontend.fuse.mount.CommandFailedException;
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
import org.cryptomator.frontend.fuse.mount.FuseMountException;
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
import org.cryptomator.frontend.fuse.mount.Mount;
@@ -18,48 +18,66 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.nio.file.Path;
import java.util.SortedSet;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
public class FuseVolume extends AbstractVolume {
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
private static final Pattern NON_WHITESPACE_OR_QUOTED = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); // Thanks to https://stackoverflow.com/a/366532
private Mount mount;
@Inject
public FuseVolume(@Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
public FuseVolume(@Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
mount(fs.getPath("/"), mountFlags);
mount(fs.getPath("/"), mountFlags, onExitAction);
}
private void mount(Path root, String mountFlags) throws VolumeException {
private void mount(Path root, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
try {
Mounter mounter = FuseMountFactory.getMounter();
EnvironmentVariables envVars = EnvironmentVariables.create() //
.withFlags(splitFlags(mountFlags)).withMountPoint(mountPoint) //
.withFlags(splitFlags(mountFlags)) //
.withMountPoint(mountPoint) //
.withFileNameTranscoder(mounter.defaultFileNameTranscoder()) //
.build();
this.mount = mounter.mount(root, envVars);
} catch (CommandFailedException | FuseNotSupportedException e) {
this.mount = mounter.mount(root, envVars, onExitAction);
} catch ( FuseMountException | FuseNotSupportedException e) {
throw new VolumeException("Unable to mount Filesystem", e);
}
}
private String[] splitFlags(String str) {
return Splitter.on(' ').splitToList(str).toArray(String[]::new);
List<String> flags = new ArrayList<>();
var matches = Iterators.peekingIterator(NON_WHITESPACE_OR_QUOTED.matcher(str).results().iterator());
while (matches.hasNext()) {
String flag = matches.next().group();
// check if flag is missing its argument:
if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(1) != null) { // next is "double quoted"
// next is "double quoted" and flag is missing its argument
flag += matches.next().group(1);
} else if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(2) != null) {
// next is 'single quoted' and flag is missing its argument
flag += matches.next().group(2);
}
flags.add(flag);
}
return flags.toArray(String[]::new);
}
@Override
public void reveal() throws VolumeException {
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.revealInFileManager();
} catch (CommandFailedException e) {
LOG.debug("Revealing the vault in file manger failed: " + e.getMessage());
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@@ -73,8 +91,7 @@ public class FuseVolume extends AbstractVolume {
public synchronized void unmountForced() throws VolumeException {
try {
mount.unmountForced();
mount.close();
} catch (CommandFailedException e) {
} catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
@@ -84,8 +101,7 @@ public class FuseVolume extends AbstractVolume {
public synchronized void unmount() throws VolumeException {
try {
mount.unmount();
mount.close();
} catch (CommandFailedException e) {
} catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
@@ -0,0 +1,12 @@
package org.cryptomator.common.vaults;
public class LockNotCompletedException extends Exception {
public LockNotCompletedException(String reason) {
super(reason);
}
public LockNotCompletedException(Throwable cause) {
super(cause);
}
}
@@ -10,7 +10,6 @@ package org.cryptomator.common.vaults;
import com.google.common.base.Strings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.LazyInitializer;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume.VolumeException;
@@ -18,10 +17,12 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,28 +37,29 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@PerVault
public class Vault {
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
private static final int UNLIMITED_FILENAME_LENGTH = Integer.MAX_VALUE;
private final VaultSettings vaultSettings;
private final Provider<Volume> volumeProvider;
private final StringBinding defaultMountFlags;
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
private final ObjectProperty<VaultState> state;
private final VaultState state;
private final ObjectProperty<Exception> lastKnownException;
private final VaultStats stats;
private final StringBinding displayName;
@@ -75,7 +77,7 @@ public class Vault {
private volatile Volume volume;
@Inject
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
this.vaultSettings = vaultSettings;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
@@ -100,44 +102,37 @@ public class Vault {
// Commands
// ********************************************************************************/
private CryptoFileSystem getCryptoFileSystem(CharSequence passphrase) throws NoSuchFileException, IOException, InvalidPassphraseException, CryptoException {
return LazyInitializer.initializeLazily(cryptoFileSystem, () -> unlockCryptoFileSystem(passphrase), IOException.class);
}
private CryptoFileSystem unlockCryptoFileSystem(CharSequence passphrase) throws NoSuchFileException, IOException, InvalidPassphraseException, CryptoException {
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
} else if(vaultSettings.maxCleartextFilenameLength().get() == -1) {
LOG.debug("Determining cleartext filename length limitations...");
var checker = new FileSystemCapabilityChecker();
int shorteningThreshold = getUnverifiedVaultConfig().allegedShorteningThreshold();
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
if (ciphertextLimit < shorteningThreshold) {
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
vaultSettings.maxCleartextFilenameLength().set(cleartextLimit);
} else {
vaultSettings.maxCleartextFilenameLength().setValue(UNLIMITED_FILENAME_LENGTH);
}
}
if (vaultSettings.filenameLengthLimit().get() == -1) {
LOG.debug("Determining file name length limitations...");
int limit = new FileSystemCapabilityChecker().determineSupportedFileNameLength(getPath());
vaultSettings.filenameLengthLimit().set(limit);
LOG.info("Storing file name length limit of {}", limit);
if (vaultSettings.maxCleartextFilenameLength().get() < UNLIMITED_FILENAME_LENGTH) {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength().get());
}
assert vaultSettings.filenameLengthLimit().get() > 0;
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withPassphrase(passphrase) //
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMasterkeyFilename(MASTERKEY_FILENAME) //
.withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) //
.withMaxNameLength(vaultSettings.filenameLengthLimit().get()) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags());
}
public synchronized void lock(boolean forced) throws VolumeException {
if (forced && volume.supportsForcedUnmount()) {
volume.unmountForced();
} else {
volume.unmount();
}
private void destroyCryptoFileSystem() {
LOG.trace("Trying to close associated CryptoFS...");
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
if (fs != null) {
try {
@@ -148,24 +143,67 @@ public class Vault {
}
}
public void reveal() throws VolumeException {
volume.reveal();
public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
if (cryptoFileSystem.get() != null) {
throw new IllegalStateException("Already unlocked.");
}
CryptoFileSystem fs = createCryptoFileSystem(keyLoader);
boolean success = false;
try {
cryptoFileSystem.set(fs);
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
success = true;
} finally {
if (!success) {
destroyCryptoFileSystem();
}
}
}
private void lockOnVolumeExit(Throwable t) {
LOG.info("Unmounted vault '{}'", getDisplayName());
destroyCryptoFileSystem();
state.set(VaultState.Value.LOCKED);
if (t != null) {
LOG.warn("Unexpected unmount and lock of vault " + getDisplayName(), t);
}
}
public synchronized void lock(boolean forced) throws VolumeException, LockNotCompletedException {
//initiate unmount
if (forced && volume.supportsForcedUnmount()) {
volume.unmountForced();
} else {
volume.unmount();
}
//wait for lockOnVolumeExit to be executed
try {
boolean locked = state.awaitState(VaultState.Value.LOCKED, 3000, TimeUnit.MILLISECONDS);
if (!locked) {
throw new LockNotCompletedException("Locking of vault " + this.getDisplayName() + " still in progress.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new LockNotCompletedException(e);
}
}
public void reveal(Volume.Revealer vaultRevealer) throws VolumeException {
volume.reveal(vaultRevealer);
}
// ******************************************************************************
// Observable Properties
// *******************************************************************************
public ObjectProperty<VaultState> stateProperty() {
public VaultState stateProperty() {
return state;
}
public VaultState getState() {
return state.get();
}
public void setState(VaultState value) {
state.setValue(value);
public VaultState.Value getState() {
return state.getValue();
}
public ObjectProperty<Exception> lastKnownExceptionProperty() {
@@ -185,7 +223,7 @@ public class Vault {
}
public boolean isLocked() {
return state.get() == VaultState.LOCKED;
return state.get() == VaultState.Value.LOCKED;
}
public BooleanBinding processingProperty() {
@@ -193,7 +231,7 @@ public class Vault {
}
public boolean isProcessing() {
return state.get() == VaultState.PROCESSING;
return state.get() == VaultState.Value.PROCESSING;
}
public BooleanBinding unlockedProperty() {
@@ -201,7 +239,7 @@ public class Vault {
}
public boolean isUnlocked() {
return state.get() == VaultState.UNLOCKED;
return state.get() == VaultState.Value.UNLOCKED;
}
public BooleanBinding missingProperty() {
@@ -209,7 +247,7 @@ public class Vault {
}
public boolean isMissing() {
return state.get() == VaultState.MISSING;
return state.get() == VaultState.Value.MISSING;
}
public BooleanBinding needsMigrationProperty() {
@@ -217,7 +255,7 @@ public class Vault {
}
public boolean isNeedsMigration() {
return state.get() == VaultState.NEEDS_MIGRATION;
return state.get() == VaultState.Value.NEEDS_MIGRATION;
}
public BooleanBinding unknownErrorProperty() {
@@ -225,7 +263,7 @@ public class Vault {
}
public boolean isUnknownError() {
return state.get() == VaultState.ERROR;
return state.get() == VaultState.Value.ERROR;
}
public StringBinding displayNameProperty() {
@@ -241,7 +279,7 @@ public class Vault {
}
public String getAccessPoint() {
if (state.get() == VaultState.UNLOCKED) {
if (state.getValue() == VaultState.Value.UNLOCKED) {
assert volume != null;
return volume.getMountPoint().orElse(Path.of("")).toString();
} else {
@@ -289,6 +327,12 @@ public class Vault {
return stats;
}
public UnverifiedVaultConfig getUnverifiedVaultConfig() throws IOException {
Path configPath = getPath().resolve(org.cryptomator.common.Constants.VAULTCONFIG_FILENAME);
String token = Files.readString(configPath, StandardCharsets.US_ASCII);
return VaultConfig.decode(token);
}
public Observable[] observables() {
return new Observable[]{state};
}
@@ -345,8 +389,7 @@ public class Vault {
@Override
public boolean equals(Object obj) {
if (obj instanceof Vault && obj.getClass().equals(this.getClass())) {
final Vault other = (Vault) obj;
if (obj instanceof Vault other && obj.getClass().equals(this.getClass())) {
return Objects.equals(this.vaultSettings, other.vaultSettings);
} else {
return false;
@@ -26,7 +26,7 @@ public interface VaultComponent {
Builder vaultSettings(VaultSettings vaultSettings);
@BindsInstance
Builder initialVaultState(VaultState vaultState);
Builder initialVaultState(VaultState.Value vaultState);
@BindsInstance
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
@@ -22,10 +22,10 @@ class VaultListChangeListener implements ListChangeListener<Vault> {
public void onChanged(Change<? extends Vault> c) {
while (c.next()) {
if (c.wasAdded()) {
List<VaultSettings> addedSettings = c.getAddedSubList().stream().map(Vault::getVaultSettings).collect(Collectors.toList());
List<VaultSettings> addedSettings = c.getAddedSubList().stream().map(Vault::getVaultSettings).toList();
vaultSettingsList.addAll(c.getFrom(), addedSettings);
} else if (c.wasRemoved()) {
List<VaultSettings> removedSettings = c.getRemoved().stream().map(Vault::getVaultSettings).collect(Collectors.toList());
List<VaultSettings> removedSettings = c.getRemoved().stream().map(Vault::getVaultSettings).toList();
vaultSettingsList.removeAll(removedSettings);
}
}
@@ -11,6 +11,7 @@ package org.cryptomator.common.vaults;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.migration.Migrators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,51 +21,51 @@ import javax.inject.Singleton;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
@Singleton
public class VaultListManager {
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder;
private final ObservableList<Vault> vaultList;
private final String defaultVaultName;
@Inject
public VaultListManager(VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
this.vaultList = vaultList;
this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
this.vaultList = FXCollections.observableArrayList(Vault::observables);
addAll(settings.getDirectories());
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
autoLocker.init();
}
public ObservableList<Vault> getVaultList() {
return vaultList;
}
public Vault add(Path pathToVault) throws NoSuchFileException {
public Vault add(Path pathToVault) throws IOException {
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
if (!CryptoFileSystemProvider.containsVault(normalizedPathToVault, MASTERKEY_FILENAME)) {
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory");
}
Optional<Vault> alreadyExistingVault = get(normalizedPathToVault);
if (alreadyExistingVault.isPresent()) {
return alreadyExistingVault.get();
} else {
Vault newVault = create(newVaultSettings(normalizedPathToVault));
vaultList.add(newVault);
return newVault;
}
return get(normalizedPathToVault) //
.orElseGet(() -> {
Vault newVault = create(newVaultSettings(normalizedPathToVault));
vaultList.add(newVault);
return newVault;
});
}
private VaultSettings newVaultSettings(Path path) {
@@ -79,7 +80,7 @@ public class VaultListManager {
}
private void addAll(Collection<VaultSettings> vaultSettings) {
Collection<Vault> vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList());
Collection<Vault> vaults = vaultSettings.stream().map(this::create).toList();
vaultList.addAll(vaults);
}
@@ -94,43 +95,45 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
try {
VaultState vaultState = determineVaultState(vaultSettings.path().get());
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
compBuilder.initialVaultState(vaultState);
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
compBuilder.initialVaultState(VaultState.ERROR);
compBuilder.initialVaultState(ERROR);
compBuilder.initialErrorCause(e);
}
return compBuilder.build().vault();
}
public static VaultState redetermineVaultState(Vault vault) {
VaultState previousState = vault.getState();
public static VaultState.Value redetermineVaultState(Vault vault) {
VaultState state = vault.stateProperty();
VaultState.Value previousState = state.getValue();
return switch (previousState) {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
try {
VaultState determinedState = determineVaultState(vault.getPath());
vault.setState(determinedState);
var determinedState = determineVaultState(vault.getPath());
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
vault.setState(VaultState.ERROR);
state.set(ERROR);
vault.setLastKnownException(e);
yield VaultState.ERROR;
yield ERROR;
}
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
};
}
private static VaultState determineVaultState(Path pathToVault) throws IOException {
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
return VaultState.MISSING;
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
return VaultState.NEEDS_MIGRATION;
} else {
return VaultState.LOCKED;
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
if (!Files.exists(pathToVault)) {
return VaultState.Value.MISSING;
}
return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
case VAULT -> VaultState.Value.LOCKED;
case UNRELATED -> VaultState.Value.MISSING;
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? VaultState.Value.NEEDS_MIGRATION : VaultState.Value.MISSING;
};
}
}
@@ -0,0 +1,19 @@
package org.cryptomator.common.vaults;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@Module
public class VaultListModule {
@Provides
@Singleton
public ObservableList<Vault> provideVaultList() {
return FXCollections.observableArrayList(Vault::observables);
}
}
@@ -40,12 +40,6 @@ public class VaultModule {
return new AtomicReference<>();
}
@Provides
@PerVault
public ObjectProperty<VaultState> provideVaultState(VaultState initialState) {
return new SimpleObjectProperty<>(initialState);
}
@Provides
@Named("lastKnownException")
@PerVault
@@ -53,7 +47,6 @@ public class VaultModule {
return new SimpleObjectProperty<>(initialErrorCause);
}
@Provides
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
@@ -101,11 +94,10 @@ public class VaultModule {
if (readOnly.get()) {
flags.append(" -ordonly");
}
flags.append(" -ovolname=").append(mountName.get());
flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
flags.append(" -oatomic_o_trunc");
flags.append(" -oauto_xattr");
flags.append(" -oauto_cache");
flags.append(" -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
@@ -146,7 +138,7 @@ public class VaultModule {
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse_main.c#L53-L62 for syntax guide
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse.c#L295-L319 for options (-o <...>)
// see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were choosen
// see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were chosen
private String getWindowsFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
assert SystemUtils.IS_OS_WINDOWS;
StringBuilder flags = new StringBuilder();
@@ -156,9 +148,9 @@ public class VaultModule {
//See: https://github.com/billziss-gh/winfsp/issues/319
if (!readOnly.get()) {
flags.append(" -ouid=-1");
flags.append(" -ogid=-1");
flags.append(" -ogid=11");
}
flags.append(" -ovolname=").append(mountName.get());
flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
//Dokany requires this option to be set, WinFSP doesn't seem to share this peculiarity,
//but the option exists. Let's keep this here in case we need it.
// flags.append(" -oThreadCount=").append(5);
@@ -0,0 +1,141 @@
package org.cryptomator.common.vaults;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValueBase;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@PerVault
public class VaultState extends ObservableValueBase<VaultState.Value> implements ObservableObjectValue<VaultState.Value> {
private static final Logger LOG = LoggerFactory.getLogger(VaultState.class);
public enum Value {
/**
* No vault found at the provided path
*/
MISSING,
/**
* Vault requires migration to a newer vault format
*/
NEEDS_MIGRATION,
/**
* Vault ready to be unlocked
*/
LOCKED,
/**
* Vault in transition between two other states
*/
PROCESSING,
/**
* Vault is unlocked
*/
UNLOCKED,
/**
* Unknown state due to preceding unrecoverable exceptions.
*/
ERROR;
}
private final AtomicReference<Value> value;
private final Lock lock = new ReentrantLock();
private final Condition valueChanged = lock.newCondition();
@Inject
public VaultState(VaultState.Value initialValue) {
this.value = new AtomicReference<>(initialValue);
}
@Override
public Value get() {
return getValue();
}
@Override
public Value getValue() {
return value.get();
}
/**
* Transitions from <code>fromState</code> to <code>toState</code>.
*
* @param fromState Previous state
* @param toState New state
* @return <code>true</code> if successful
*/
public boolean transition(Value fromState, Value toState) {
Preconditions.checkArgument(fromState != toState, "fromState must be different than toState");
boolean success = value.compareAndSet(fromState, toState);
if (success) {
fireValueChangedEvent();
} else {
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
}
return success;
}
public void set(Value newState) {
var oldState = value.getAndSet(newState);
if (oldState != newState) {
fireValueChangedEvent();
}
}
/**
* Waits for the specified time, until the desired state is reached.
*
* @param desiredState what state to wait for
* @param time the maximum time to wait
* @param unit the time unit of the {@code time} argument
* @return {@code false} if the waiting time detectably elapsed before reaching {@code desiredState}
* @throws InterruptedException if the current thread is interrupted
*/
public boolean awaitState(Value desiredState, long time, TimeUnit unit) throws InterruptedException {
lock.lock();
try {
long remaining = TimeUnit.NANOSECONDS.convert(time, unit);
while (value.get() != desiredState) {
if (remaining <= 0L) {
return false;
}
remaining = valueChanged.awaitNanos(remaining);
}
return true;
} finally {
lock.unlock();
}
}
private void signal() {
lock.lock();
try {
valueChanged.signalAll();
} finally {
lock.unlock();
}
}
@Override
protected void fireValueChangedEvent() {
signal();
if (Platform.isFxApplicationThread()) {
super.fireValueChangedEvent();
} else {
Platform.runLater(super::fireValueChangedEvent);
}
}
}
@@ -13,9 +13,11 @@ import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@@ -26,22 +28,23 @@ public class VaultStats {
private static final Logger LOG = LoggerFactory.getLogger(VaultStats.class);
private final AtomicReference<CryptoFileSystem> fs;
private final ObjectProperty<VaultState> state;
private final VaultState state;
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
private final LongProperty bytesPerSecondEncrypted = new SimpleLongProperty();
private final LongProperty bytesPerSecondDecrypted = new SimpleLongProperty();
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
private final LongProperty toalBytesRead = new SimpleLongProperty();
private final LongProperty toalBytesWritten = new SimpleLongProperty();
private final LongProperty totalBytesRead = new SimpleLongProperty();
private final LongProperty totalBytesWritten = new SimpleLongProperty();
private final LongProperty totalBytesEncrypted = new SimpleLongProperty();
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
private final LongProperty filesWritten = new SimpleLongProperty();
private final ObjectProperty<Instant> lastActivity = new SimpleObjectProperty<>();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
VaultStats(AtomicReference<CryptoFileSystem> fs, VaultState state, ExecutorService executor) {
this.fs = fs;
this.state = state;
this.updateService = new UpdateStatsService();
@@ -52,13 +55,16 @@ public class VaultStats {
}
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
if (VaultState.UNLOCKED.equals(state.get())) {
if (VaultState.Value.UNLOCKED == state.get()) {
assert fs.get() != null;
LOG.debug("start recording stats");
updateService.restart();
Platform.runLater(() -> {
lastActivity.set(Instant.now());
updateService.restart();
});
} else {
LOG.debug("stop recording stats");
updateService.cancel();
Platform.runLater(() -> updateService.cancel());
}
}
@@ -69,13 +75,19 @@ public class VaultStats {
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
bytesPerSecondDecrypted.set(stats.map(CryptoFileSystemStats::pollBytesDecrypted).orElse(0L));
bytesPerSecondEncrypted.set(stats.map(CryptoFileSystemStats::pollBytesEncrypted).orElse(0L));
toalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
totalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
var oldAccessCount = filesRead.get() + filesWritten.get();
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
var newAccessCount = filesRead.get() + filesWritten.get();
// check for any I/O activity
if (newAccessCount > oldAccessCount) {
lastActivity.set(Instant.now());
}
}
private double getCacheHitRate(CryptoFileSystemStats stats) {
@@ -134,7 +146,7 @@ public class VaultStats {
return bytesPerSecondEncrypted;
}
public long getBytesPerSecondEnrypted() {
public long getBytesPerSecondEncrypted() {
return bytesPerSecondEncrypted.get();
}
@@ -152,13 +164,13 @@ public class VaultStats {
return cacheHitRate.get();
}
public LongProperty toalBytesReadProperty() {return toalBytesRead;}
public LongProperty totalBytesReadProperty() {return totalBytesRead;}
public long getTotalBytesRead() { return toalBytesRead.get();}
public long getTotalBytesRead() { return totalBytesRead.get();}
public LongProperty toalBytesWrittenProperty() {return toalBytesWritten;}
public LongProperty totalBytesWrittenProperty() {return totalBytesWritten;}
public long getTotalBytesWritten() { return toalBytesWritten.get();}
public long getTotalBytesWritten() { return totalBytesWritten.get();}
public LongProperty totalBytesEncryptedProperty() {return totalBytesEncrypted;}
@@ -175,4 +187,12 @@ public class VaultStats {
public LongProperty filesWritten() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
public ObjectProperty<Instant> lastActivityProperty() {
return lastActivity;
}
public Instant getLastActivity() {
return lastActivity.get();
}
}
@@ -7,10 +7,12 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Takes a Volume and usess it to mount an unlocked vault
* Takes a Volume and uses it to mount an unlocked vault
*/
public interface Volume {
@@ -22,7 +24,7 @@ public interface Volume {
boolean isSupported();
/**
* Gets the coresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
* Gets the corresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
*
* @return the type of implementation as defined by the {@link VolumeImpl VolumeImpl enum}
*/
@@ -32,9 +34,17 @@ public interface Volume {
* @param fs
* @throws IOException
*/
void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException, InvalidMountPointException;
void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws IOException, VolumeException, InvalidMountPointException;
void reveal() throws VolumeException;
/**
* Reveals the mounted volume.
* <p>
* The given {@code revealer} might be used to do it, but not necessarily.
*
* @param revealer An object capable of revealing the location of the mounted vault to view the content (e.g. in the default file browser).
* @throws VolumeException
*/
void reveal(Revealer revealer) throws VolumeException;
void unmount() throws VolumeException;
@@ -79,4 +89,14 @@ public interface Volume {
}
/**
* Hides and unifies the different Revealer implementations in the different nio-adapters.
*/
@FunctionalInterface
interface Revealer {
void reveal(Path p) throws VolumeException;
}
}
@@ -17,6 +17,8 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class WebDavVolume implements Volume {
@@ -25,21 +27,29 @@ public class WebDavVolume implements Volume {
private final Provider<WebDavServer> serverProvider;
private final VaultSettings vaultSettings;
private final Settings settings;
private final WindowsDriveLetters windowsDriveLetters;
private WebDavServer server;
private WebDavServletController servlet;
private Mounter.Mount mount;
private Path mountPoint;
private Consumer<Throwable> onExitAction;
@Inject
public WebDavVolume(Provider<WebDavServer> serverProvider, VaultSettings vaultSettings, Settings settings) {
public WebDavVolume(Provider<WebDavServer> serverProvider, VaultSettings vaultSettings, Settings settings, WindowsDriveLetters windowsDriveLetters) {
this.serverProvider = serverProvider;
this.vaultSettings = vaultSettings;
this.settings = settings;
this.windowsDriveLetters = windowsDriveLetters;
}
@Override
public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException {
public void mount(CryptoFileSystem fs, String mountFlags, Consumer<Throwable> onExitAction) throws VolumeException {
startServlet(fs);
mountServlet();
this.onExitAction = onExitAction;
}
private void startServlet(CryptoFileSystem fs) {
if (server == null) {
server = serverProvider.get();
}
@@ -50,32 +60,38 @@ public class WebDavVolume implements Volume {
String urlConformMountName = acceptable.negate().collapseFrom(vaultSettings.mountName().get(), '_');
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + urlConformMountName);
servlet.start();
mount();
}
private void mount() throws VolumeException {
private void mountServlet() throws VolumeException {
if (servlet == null) {
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
}
//on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specific one or there is no free.
Supplier<String> driveLetterSupplier;
if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
driveLetterSupplier = () -> windowsDriveLetters.getAvailableDriveLetter().orElse(null);
} else {
driveLetterSupplier = () -> vaultSettings.winDriveLetter().get();
}
MountParams mountParams = MountParams.create() //
.withWindowsDriveLetter(vaultSettings.winDriveLetter().get()) //
.withWindowsDriveLetter(driveLetterSupplier.get()) //
.withPreferredGvfsScheme(settings.preferredGvfsScheme().get().getPrefix())//
.withWebdavHostname(getLocalhostAliasOrNull()) //
.build();
try {
this.mount = servlet.mount(mountParams); // might block this thread for a while
} catch (Mounter.CommandFailedException e) {
e.printStackTrace();
throw new VolumeException(e);
}
}
@Override
public void reveal() throws VolumeException {
public void reveal(Revealer revealer) throws VolumeException {
try {
mount.reveal();
} catch (Mounter.CommandFailedException e) {
e.printStackTrace();
mount.reveal(revealer::reveal);
} catch (Exception e) {
throw new VolumeException(e);
}
}
@@ -88,6 +104,7 @@ public class WebDavVolume implements Volume {
throw new VolumeException(e);
}
cleanup();
onExitAction.accept(null);
}
@Override
@@ -98,11 +115,12 @@ public class WebDavVolume implements Volume {
throw new VolumeException(e);
}
cleanup();
onExitAction.accept(null);
}
@Override
public Optional<Path> getMountPoint() {
return Optional.ofNullable(mountPoint); //TODO
return mount.getMountPoint();
}
@Override

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