Compare commits

..
Author SHA1 Message Date
Sebastian Stenzel 6225d8d1f0 Merge branch 'develop' into release/1.11.0 2023-10-20 14:00:42 +02:00
Sebastian Stenzel ed975b459e bump dependencies 2023-10-20 14:00:08 +02:00
Armin SchrenkandGitHub 800b2440b3 Merge pull request #3157 from cryptomator/feature/3113-network-timeout
Time restrict network requests
2023-10-20 12:58:08 +02:00
Sebastian Stenzel f50a49e7a3 prepare 1.11.0 2023-10-20 12:11:06 +02:00
Sebastian Stenzel 92ece4dfb6 increased request timeout to 10s 2023-10-20 12:06:10 +02:00
Sebastian Stenzel 174225c60e add timeout to OAuth2 requests 2023-10-20 12:03:07 +02:00
Sebastian Stenzel 3071410a22 Merge branch 'develop' into feature/3113-network-timeout 2023-10-20 12:00:16 +02:00
Sebastian StenzelandGitHub 5c5777ffc5 Merge pull request #3091 from cryptomator/feature/simplify-out-of-bounds
simplify out-of-bounds check
2023-10-20 11:56:36 +02:00
Sebastian StenzelandGitHub 8dd8b93656 Merge pull request #3041 from cryptomator/feature/new-hub-keyloading
Adjusted to Hub 1.3.x API
2023-10-20 11:55:28 +02:00
Armin Schrenk 3b2d455b4a Update dependencies 2023-10-19 17:31:52 +02:00
Sebastian Stenzel b0dfa22903 removed unused class 2023-10-19 12:49:19 +02:00
Sebastian Stenzel 88b1c28d88 Merge branch 'develop' into feature/new-hub-keyloading 2023-10-19 11:33:13 +02:00
Sebastian Stenzel 5b85d7859a bump webdav-nio-adapter version 2023-10-19 11:28:54 +02:00
Sebastian Stenzel 468eed58d5 cleanup 2023-10-19 10:00:14 +02:00
Sebastian Stenzel 34c17be474 add request timeouts 2023-10-19 10:00:03 +02:00
Sebastian Stenzel 5dedd6b1c1 renamed class again 2023-10-18 11:30:22 +02:00
Sebastian Stenzel 25e8e81686 support apiBaseUrl in hub config 2023-10-18 11:08:42 +02:00
Sebastian Stenzel 1f7ab03bbb undo JEP 443 changes due to bug in javac
leads to invalid class files when built via Maven due to specific combination of javac arguments
2023-10-18 11:01:26 +02:00
Sebastian Stenzel 86f3cb7288 applied suggestions from code review 2023-10-18 09:40:34 +02:00
Sebastian Stenzel a1b8bf23b4 Merge branch 'develop' into feature/new-hub-keyloading 2023-10-18 09:40:03 +02:00
Sebastian StenzelandGitHub 1f21d5ea4c Merge pull request #3129 from cryptomator/feature/jdk21
JDK 21
2023-10-18 09:39:12 +02:00
Sebastian Stenzel f8ff7201d7 Merge branch 'develop' into feature/new-hub-keyloading 2023-10-18 08:50:34 +02:00
Armin Schrenk 3567c036c3 remove unused imports 2023-10-17 17:25:38 +02:00
Armin Schrenk 0cf1f087ad Closes #3113 2023-10-17 11:36:50 +02:00
Armin Schrenk 209f60727e fix f4ad7aa43d 2023-10-16 16:38:12 +02:00
Armin SchrenkandGitHub 560a979e0e Merge pull request #3131 from cryptomator/feature/sign-all-dlls
Feature: Sign all dlls to pass Microsoft Windows Smart App control
2023-10-16 16:21:08 +02:00
Sebastian Stenzel 1debe4c7c8 explicitly set Logback ConfiguratorRank 2023-10-16 16:20:03 +02:00
Sebastian Stenzel 2e2aa77727 update dependencies 2023-10-16 16:19:11 +02:00
Sebastian Stenzel 162ebf6545 Merge branch 'develop' into feature/jdk21 2023-10-16 16:12:10 +02:00
Armin SchrenkandGitHub 2d503a0edb Merge pull request #3148 from cryptomator/feature/propagate-deleted-discussions
Propagate deleted discussions to the error database
2023-10-13 17:01:45 +02:00
Julian Raufelder 7f7f0a099a Propagate deleted discussions to the error database 2023-10-12 14:11:54 +02:00
Julian Raufelder 109f5d1faa Update the error-db when a discussion is deleted 2023-10-12 10:56:14 +02:00
Armin Schrenk 9b55f6fc56 Refactor extraction and singing in seperate steps 2023-10-11 12:55:05 +02:00
Sebastian Stenzel 83879f5cfe revert 8e9d54b & 6c5ee14 due to bug in JDK 21
see https://bugs.openjdk.org/browse/JDK-8313323
2023-10-09 16:19:56 +02:00
Armin SchrenkandGitHub 083ac5a7e0 Merge pull request #3133 from purejava/build-arch
Improve AppImage build script
2023-10-09 13:10:05 +02:00
dependabot[bot]andGitHub a7eba377ba Bump org.mockito:mockito-core from 5.5.0 to 5.6.0 (#3142) 2023-10-09 09:58:47 +00:00
dependabot[bot]andGitHub 4ee1e6d9f1 Bump the github-actions group with 1 update (#3135) 2023-10-04 20:24:43 +00:00
Ralph Plawetzki 24a63c10d0 Build for aarch64 too 2023-09-30 14:54:15 +02:00
Armin Schrenk 0d805b2d43 clean up 2023-09-28 18:34:38 +02:00
Armin Schrenk 7bc47fe6d7 Really fixes #3130 2023-09-28 18:33:26 +02:00
Armin Schrenk ae50846257 Revert ce466e7715 2023-09-28 18:33:01 +02:00
Armin Schrenk ce466e7715 fixes #3130 2023-09-28 13:47:51 +02:00
Armin Schrenk 15885545b5 supress some output 2023-09-27 11:13:52 +02:00
Armin Schrenk 29fedcd390 integrate code from test script into github workflow 2023-09-27 10:24:59 +02:00
Armin Schrenk fadd6b761f add cert import 2023-09-26 17:30:18 +02:00
Armin Schrenk d857335901 update test script:
* use variables
* use signtool
* find newest signtool version
2023-09-26 16:43:00 +02:00
Armin Schrenk 93b4cbfb2c add test script 2023-09-26 13:42:18 +02:00
Armin Schrenk 625334c6c8 fix windows build script 2023-09-25 16:02:31 +02:00
Sebastian Stenzel 943374856e build using JDK 21 GA 2023-09-25 12:18:01 +02:00
Sebastian Stenzel e1d7fd92ad Merge branch 'develop' into feature/jdk21
# Conflicts:
#	pom.xml
2023-09-25 12:15:14 +02:00
Sebastian Stenzel c10dc74818 update integrations-linux to 1.4.0-beta1 2023-09-25 12:12:28 +02:00
Tobias Hagemann 92d9f2c18d try to fix mac build script to be compatible with create-dmg 1.2.0 2023-09-20 15:27:17 +02:00
Armin SchrenkandGitHub d7488b6984 fix wrong pom version 2023-09-20 15:16:18 +02:00
Tobias Hagemann 88ad3cd724 Merge branch 'develop' into feature/new-hub-keyloading 2023-09-20 14:38:13 +02:00
Armin Schrenk ba9e0b0c12 Merge branch 'main' into develop 2023-09-20 11:34:37 +02:00
Armin Schrenk bbe7255901 Merge branch 'release/1.10.1' 2023-09-20 11:33:39 +02:00
Armin Schrenk 51e55d3e3b finalize 1.10.1 2023-09-20 11:09:37 +02:00
Armin Schrenk f704dc0de9 prepare 1.10.1 2023-09-20 11:07:50 +02:00
CryptobotandGitHub e858280f30 New Crowdin updates (#3110)
New Crowdin updates (#3110)

New translations strings.properties

Bulgarian; Chinese Traditional; 

[ci skip]
2023-09-20 11:05:33 +02:00
Armin SchrenkandGitHub f9b170204a Merge pull request #3118 from cryptomator/feature/3117-disable-update-check
Disable Update Check
2023-09-20 11:02:14 +02:00
Armin SchrenkandGitHub 0ed7415a0d Merge pull request #3109 from cryptomator/feature/error-dialog-request-with-errorcode
Enhance user agent string when downloading error code database
2023-09-20 11:02:01 +02:00
Armin Schrenk 7a0d50ecca removed unnecessary import 2023-09-20 10:48:26 +02:00
Armin SchrenkandGitHub 19e035aa33 Merge pull request #3123 from cryptomator/feature/context-menu-focus
Fix addVaultButton misbehavior
2023-09-19 20:56:56 +02:00
Jan-Peter Klein 830970cb75 improved format and naming 2023-09-19 16:29:12 +02:00
Jan-Peter Klein 9b2987d0a2 improved function naming 2023-09-19 16:26:55 +02:00
Armin Schrenk 76a4062f8b update org.cryptomator.integrations:integrations-win from 1.2.2 to 1.2.3 2023-09-19 15:12:45 +02:00
Jan-Peter Klein 113717f955 addVaultButton context menu event filter removed 2023-09-19 15:12:40 +02:00
Jan-Peter Klein c40ad58028 detached context menu from button to fix misbehavior 2023-09-19 15:03:22 +02:00
Jan-Peter Klein d6a7efcb7f added OS_NAME, OS_VERSION and OS_ARCH to user-agent 2023-09-19 14:00:45 +02:00
Jan-Peter Klein 910b89d6ce Merge branch 'develop' into feature/error-dialog-request-with-errorcode 2023-09-19 13:19:40 +02:00
Tobias Hagemann 5554dfdd89 fixes #3121 2023-09-19 12:22:24 +02:00
Tobias Hagemann 1657cd50fc fixes #3112 2023-09-19 12:12:27 +02:00
Sebastian Stenzel 57bf5e11b5 Merge branch 'develop' into feature/simplify-out-of-bounds 2023-09-18 19:33:25 +02:00
Sebastian Stenzel 594ca47c85 Merge branch 'develop' into feature/jdk21
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/mac-dmg.yml
2023-09-18 19:33:06 +02:00
Sebastian Stenzel 6c5ee14c73 rename unused var 2023-09-18 19:29:35 +02:00
Sebastian Stenzel 8e9d54b44b rename unused var 2023-09-18 19:27:28 +02:00
Sebastian Stenzel 70805665bf use features of Java 21 with --enable-preview 2023-09-18 19:21:56 +02:00
Sebastian Stenzel ac243a706e improve readability 2023-09-18 19:13:03 +02:00
Sebastian Stenzel aa382ce80d update coffeelibs jdk to 21 2023-09-18 19:10:54 +02:00
b7da508ccc Update src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java
Co-authored-by: Bas Ruigrok <Rexbas@proton.me>
2023-09-18 13:19:58 +02:00
Sebastian Stenzel 47bd0ca647 disable update check for PPA builds 2023-09-18 13:10:19 +02:00
Sebastian Stenzel c5d6c0ce98 add cryptomator.disableUpdateCheck property 2023-09-18 13:03:09 +02:00
Sebastian Stenzel 6017d6b7a9 bump webdav-nio-adapter
containing fixes for CVE-2023-40167, CVE-2023-2976, CVE-2023-37895
2023-09-18 12:34:49 +02:00
Sebastian Stenzel e31e06b288 added @VisibleForTesting 2023-09-18 10:13:51 +02:00
Sebastian Stenzel 84ac803a7d reordered properties
[ci skip]
2023-09-18 10:10:50 +02:00
Armin SchrenkandGitHub bcb970afb1 Update bug.yml
Add missing FUSE option in volume type selection
2023-09-13 13:59:50 +02:00
Jan-Peter Klein 9d640b57ce added build number to user-agent header 2023-09-13 12:31:53 +02:00
Jan-Peter Klein 4c83617847 fixed build error 2023-09-12 18:07:30 +02:00
Jan-Peter Klein 5d7906972b added user-agent header with <product> / <product-version> to HttpRequest 2023-09-12 17:59:08 +02:00
Jan-Peter Klein 666cd4a4f0 enhanced request URI with URLEncoder for errorCode 2023-09-12 17:14:45 +02:00
Jan-Peter Klein eb4d39e8b4 introduce 'errorCode' parameter in request 2023-09-12 13:34:55 +02:00
Armin Schrenk a1ad89a2e2 Merge branch 'main' into develop [ci skip] 2023-09-11 09:56:35 +02:00
Armin Schrenk 38108299fa Merge branch 'release/1.10.0' 2023-09-11 09:55:14 +02:00
Armin Schrenk 189e2679f1 finalize 1.10.0 2023-09-11 09:53:35 +02:00
Armin Schrenk 22ceb57052 Merge branch 'develop' into release/1.10.0 2023-09-11 09:51:13 +02:00
Armin Schrenk 0130c7cce6 fix local windows build script [ci skip] 2023-09-11 09:50:41 +02:00
CryptobotandGitHub 18b6484731 New Crowdin updates (#3095)
New translations strings.properties

Arabic; Chinese Simplified; Filipino; German; Japanese; Norwegian Bokmal; Polish; Portuguese, Brazilian; Spanish; Swedish; Ukrainian; 

[ci skip]
2023-09-11 09:46:49 +02:00
Sebastian Stenzel f19ff59005 add suffix to uploaded artifacts 2023-09-06 16:47:29 +02:00
Sebastian Stenzel 8786b5ee2a can't use env vars in matrix builds 2023-09-06 16:43:58 +02:00
Sebastian Stenzel c7e23f9646 build AppImage for aarch64 2023-09-06 16:40:00 +02:00
Sebastian Stenzel 4043e3f71f build using JDK 21 2023-09-06 16:22:41 +02:00
Tobias Hagemann 08887f1147 Merge branch 'develop' into feature/new-hub-keyloading 2023-09-06 14:18:11 +02:00
Sebastian Stenzel 13a0578ab7 fix checksum file format 2023-09-06 13:05:01 +02:00
Sebastian Stenzel 888bf04ec2 fix workflow files 2023-09-06 13:01:48 +02:00
Sebastian Stenzel d0822041ec renamed workflow file 2023-09-06 12:58:08 +02:00
Sebastian Stenzel e65a57bba9 harmonize workflow files
in regards to JDK distribution and OpenJFX downloads
2023-09-06 12:57:41 +02:00
Armin SchrenkandGitHub 2bb1b09789 Merge pull request #3097 from cryptomator/dependabot/maven/maven-dependencies-a3030d6477
Bump the maven-dependencies group with 3 updates
2023-09-04 14:00:36 +02:00
dependabot[bot]andGitHub b83dad4e24 Bump the maven-dependencies group with 3 updates
Bumps the maven-dependencies group with 3 updates: org.slf4j:slf4j-api, [com.google.dagger:dagger](https://github.com/google/dagger) and [com.google.dagger:dagger-compiler](https://github.com/google/dagger).


Updates `org.slf4j:slf4j-api` from 2.0.7 to 2.0.9

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

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

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-dependencies
- dependency-name: com.google.dagger:dagger
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-dependencies
- dependency-name: com.google.dagger:dagger-compiler
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 06:48:51 +00:00
Armin Schrenk 4bb28f4286 Merge branch 'develop' into release/1.10.0 2023-09-01 16:14:16 +02:00
CryptobotandGitHub 721cdf20e7 New Crowdin updates (#3071)
New translations strings.properties

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

[ci skip]
2023-09-01 16:13:46 +02:00
Armin SchrenkandGitHub cf248c5cbc Merge pull request #3080 from purejava/fix-3079
Bring up the main window from the tray on pressing "Show" on the tray…
2023-09-01 16:06:15 +02:00
Armin Schrenk 151ca75e5f Merge branch 'develop' into release/1.10.0 2023-09-01 15:43:44 +02:00
mindmonkandGitHub 0e52b7aff4 Merge pull request #3082 from cryptomator/feature/add-vault-button-to-dropdown
UI Enhancement: Improved 'Add Vault' Button Functionality
2023-08-31 15:39:12 +02:00
Jan-Peter Klein 3a0199a1e5 updated menuItemExisting to display folder icon instead of plus icon 2023-08-31 12:17:57 +02:00
Jan-Peter Klein 50e96095ba moved the context menu creation code to the fxml file 2023-08-31 11:49:49 +02:00
Jan-Peter Klein 2f5bce6229 Merge branch 'develop' into feature/add-vault-button-to-dropdown 2023-08-30 12:06:41 +02:00
Sebastian Stenzel 62f50ce366 rename groups
[ci skip]
2023-08-29 07:52:37 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
864c1c9c0d Bump the all group with 1 update (#3089)
Bumps the all group with 1 update: [org.mockito:mockito-core](https://github.com/mockito/mockito).

- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v5.4.0...v5.5.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-29 07:48:37 +02:00
Sebastian Stenzel cfd433b328 revert to simple of of bounds check
as suggested in https://github.com/cryptomator/cryptomator/pull/3017#discussion_r1264527894 with minor adjustments
2023-08-29 07:26:46 +02:00
Tobias Hagemann ad97338f9b fixes #2517 2023-08-28 15:13:24 +02:00
Armin SchrenkandGitHub 5d596f4fd0 Merge pull request #3017 from Rexbas/out_of_bounds_fix
Reset position when out of display bounds
2023-08-28 14:58:52 +02:00
Rexbas aa14d79642 Restore a closed minimized window at (-32000, -32000) to the last saved position
Fixes #3079, second scenario.
2023-08-23 18:57:04 +02:00
Rexbas 745d435a57 Remove displayConfiguration from settings 2023-08-23 18:57:04 +02:00
Rexbas 094a7c6a20 Check display bounds onShowing event 2023-08-23 18:57:04 +02:00
Rexbas aedbefc38a Reset window position when out of display bounds 2023-08-23 18:57:00 +02:00
dependabot[bot]andGitHub 0cd3cf9e1c Merge pull request #3081 from cryptomator/dependabot/maven/all-f18bb33e46 2023-08-23 13:51:07 +00:00
Jan-Peter Klein ef24141713 new stage titles for addNewVault and addExistingVault 2023-08-23 13:36:41 +02:00
Jan-Peter Klein 820d4ff573 disabled right click on add vault button and enhanced naming 2023-08-23 13:17:38 +02:00
Jan-Peter Klein f024f0ee5d Merge branch 'develop' into feature/add-vault-button-to-dropdown 2023-08-21 13:06:13 +02:00
Jan-Peter Klein a038b34e29 implemented enhanced styling for MenuItems 2023-08-21 13:05:42 +02:00
dependabot[bot]andGitHub 4cc516def6 Bump the all group with 2 updates
Bumps the all group with 2 updates: [com.nulab-inc:zxcvbn](https://github.com/nulab/zxcvbn4j) and [org.owasp:dependency-check-maven](https://github.com/jeremylong/DependencyCheck).


Updates `com.nulab-inc:zxcvbn` from 1.8.0 to 1.8.2
- [Release notes](https://github.com/nulab/zxcvbn4j/releases)
- [Changelog](https://github.com/nulab/zxcvbn4j/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nulab/zxcvbn4j/compare/1.8.0...1.8.2)

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

---
updated-dependencies:
- dependency-name: com.nulab-inc:zxcvbn
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: org.owasp:dependency-check-maven
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 07:01:49 +00:00
Ralph Plawetzki bb1095eb78 Bring up the main window from the tray on pressing "Show" on the tray menu for Windows
Fixes #3079
2023-08-19 19:14:46 +02:00
Armin Schrenk 4c11190be9 Improve error template 2023-08-18 15:11:19 +02:00
Tobias Hagemann 8a7a5eeea1 fixed grammar 2023-08-16 17:17:27 +02:00
Jan-Peter Klein 34328c10bf changed add vault icon to caret down and set contentDisplay RIGHT 2023-08-15 16:31:42 +02:00
Jan-Peter Klein c4d503c8d5 removed unused import 2023-08-15 16:24:07 +02:00
Jan-Peter Klein 5fdf5141a5 removed addVaultWelcome stuff 2023-08-15 14:47:47 +02:00
Jan-Peter Klein 9be00a2693 removed didClickAddVault and added SHORTCUT_O 2023-08-15 14:34:06 +02:00
Jan-Peter Klein 17778f32b9 expanded functionality: implemented showAddNewVaultWizard and showAddExistingVaultWizard functions 2023-08-15 14:30:55 +02:00
Armin Schrenk bcbdad99bc Closes #1929 2023-08-14 17:26:44 +02:00
Jan-Peter Klein 1e6d2bb6fb enhanced ui by introducing context menu for 'add vault' button 2023-08-14 17:15:17 +02:00
Armin Schrenk 57fee3e428 Adding plugin to display junit tests properly in console 2023-08-14 13:44:47 +02:00
Armin Schrenk 4ea45e937a extract mvn build plugin versions to own properties 2023-08-14 10:50:05 +02:00
Armin SchrenkandGitHub dc80d79da0 Merge pull request #3073 from cryptomator/dependabot/maven/all-f66be4cce2
Bump the all group with 20 updates
2023-08-14 10:42:34 +02:00
Armin Schrenk 57f68376fe Merge branch 'develop' into release/1.10.0 2023-08-14 10:28:13 +02:00
dependabot[bot]andGitHub f47cfa60c5 Bump the all group with 20 updates
Bumps the all group with 20 updates:

| Package | Update |
| --- | --- |
| [ch.qos.logback:logback-core](https://github.com/qos-ch/logback) | 1.4.7 to 1.4.11 |
| [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) | 1.4.7 to 1.4.11 |
| org.apache.commons:commons-lang3 | 3.12.0 to 3.13.0 |
| [io.github.coffeelibs:tiny-oauth2-client](https://github.com/coffeelibs/tiny-oauth2-client) | 0.5.1 to 0.6.0 |
| [com.nulab-inc:zxcvbn](https://github.com/nulab/zxcvbn4j) | 1.7.0 to 1.8.0 |
| [com.google.guava:guava](https://github.com/google/guava) | 32.0.1-jre to 32.1.2-jre |
| [com.google.dagger:dagger](https://github.com/google/dagger) | 2.45 to 2.47 |
| [com.google.dagger:dagger-compiler](https://github.com/google/dagger) | 2.45 to 2.47 |
| [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) | 5.9.3 to 5.10.0 |
| [org.mockito:mockito-core](https://github.com/mockito/mockito) | 5.3.1 to 5.4.0 |
| [com.google.jimfs:jimfs](https://github.com/google/jimfs) | 1.2 to 1.3.0 |
| [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) | 23.0.0 to 24.0.1 |
| [org.cryptomator:integrations-mac](https://github.com/cryptomator/integrations-mac) | 1.2.0 to 1.2.1 |
| [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | 3.10.1 to 3.11.0 |
| [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) | 3.3.0 to 3.3.1 |
| [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) | 3.3.0 to 3.6.0 |
| [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | 3.0.0-M7 to 3.1.2 |
| [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | 2.0.0 to 2.2.0 |
| [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | 0.8.9 to 0.8.10 |
| [org.owasp:dependency-check-maven](https://github.com/jeremylong/DependencyCheck) | 8.1.2 to 8.3.1 |


Updates `ch.qos.logback:logback-core` from 1.4.7 to 1.4.11
- [Commits](https://github.com/qos-ch/logback/compare/v_1.4.7...v_1.4.11)

Updates `ch.qos.logback:logback-classic` from 1.4.7 to 1.4.11
- [Commits](https://github.com/qos-ch/logback/compare/v_1.4.7...v_1.4.11)

Updates `org.apache.commons:commons-lang3` from 3.12.0 to 3.13.0

Updates `io.github.coffeelibs:tiny-oauth2-client` from 0.5.1 to 0.6.0
- [Release notes](https://github.com/coffeelibs/tiny-oauth2-client/releases)
- [Commits](https://github.com/coffeelibs/tiny-oauth2-client/compare/0.5.1...0.6.0)

Updates `com.nulab-inc:zxcvbn` from 1.7.0 to 1.8.0
- [Release notes](https://github.com/nulab/zxcvbn4j/releases)
- [Changelog](https://github.com/nulab/zxcvbn4j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nulab/zxcvbn4j/compare/1.7.0...1.8.0)

Updates `com.google.guava:guava` from 32.0.1-jre to 32.1.2-jre
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

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

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

Updates `org.junit.jupiter:junit-jupiter` from 5.9.3 to 5.10.0
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.9.3...r5.10.0)

Updates `org.mockito:mockito-core` from 5.3.1 to 5.4.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v5.3.1...v5.4.0)

Updates `com.google.jimfs:jimfs` from 1.2 to 1.3.0
- [Release notes](https://github.com/google/jimfs/releases)
- [Commits](https://github.com/google/jimfs/compare/v1.2...v1.3.0)

Updates `org.jetbrains:annotations` from 23.0.0 to 24.0.1
- [Release notes](https://github.com/JetBrains/java-annotations/releases)
- [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md)
- [Commits](https://github.com/JetBrains/java-annotations/compare/23.0.0...24.0.1)

Updates `org.cryptomator:integrations-mac` from 1.2.0 to 1.2.1
- [Release notes](https://github.com/cryptomator/integrations-mac/releases)
- [Commits](https://github.com/cryptomator/integrations-mac/compare/1.2.0...1.2.1)

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

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

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

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

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

Updates `org.jacoco:jacoco-maven-plugin` from 0.8.9 to 0.8.10
- [Release notes](https://github.com/jacoco/jacoco/releases)
- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.9...v0.8.10)

Updates `org.owasp:dependency-check-maven` from 8.1.2 to 8.3.1
- [Release notes](https://github.com/jeremylong/DependencyCheck/releases)
- [Changelog](https://github.com/jeremylong/DependencyCheck/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jeremylong/DependencyCheck/compare/v8.1.2...v8.3.1)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: org.apache.commons:commons-lang3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: io.github.coffeelibs:tiny-oauth2-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: com.nulab-inc:zxcvbn
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: com.google.dagger:dagger
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: com.google.dagger:dagger-compiler
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: com.google.jimfs:jimfs
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.jetbrains:annotations
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all
- dependency-name: org.cryptomator:integrations-mac
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.apache.maven.plugins:maven-resources-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
- dependency-name: org.jacoco:jacoco-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: org.owasp:dependency-check-maven
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 06:46:37 +00:00
Julian Raufelder de78cd014d Merge branch 'main' into develop 2023-08-11 16:44:23 +02:00
Julian Raufelder 9db53f73e2 Merge branch 'hotfix/1.9.4' 2023-08-11 16:07:37 +02:00
Julian Raufelder e5f0aca477 Finalize 1.9.4 2023-08-11 15:54:50 +02:00
Julian Raufelder 261f2da105 Prepare 1.9.4 2023-08-11 15:53:26 +02:00
Julian Raufelder ef4738f920 Use latest version of Java to avoid using an out of date version when building the release
See https://github.com/actions/setup-java/tree/main#check-latest for further information
2023-08-11 15:52:42 +02:00
Armin Schrenk f23348eda2 Merge branch 'develop' into release/1.10.0 2023-08-09 22:56:05 +02:00
Armin Schrenk 6edfaf3eb6 fix windows installer ci script 2023-08-09 22:55:32 +02:00
Sebastian Stenzel f48963a6d0 Merge branch 'develop' into feature/new-hub-keyloading
# Conflicts:
#	src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java
2023-08-09 15:26:03 +02:00
Sebastian Stenzel 6ce34efb52 Merge branch 'develop' into feature/new-hub-keyloading 2023-07-28 15:23:00 +02:00
Sebastian Stenzel cd10d38990 adjusted to latest API-changes 2023-07-06 18:57:10 +02:00
Sebastian Stenzel 8cb21c97e3 add mark for future improvement 2023-07-04 16:58:37 +02:00
Sebastian Stenzel 0ad8ce77ef handle "invalid setup code" error properly 2023-07-04 16:55:42 +02:00
Sebastian Stenzel e358ffd666 added tests 2023-07-04 16:47:54 +02:00
Sebastian Stenzel 496f9a9cd5 docs
[ci skip]
2023-07-04 15:05:22 +02:00
Sebastian Stenzel 61025de0d6 renamed var
[ci skip]
2023-07-04 14:43:56 +02:00
Sebastian Stenzel f08049b960 fix "register device" on legacy hub instances 2023-07-04 14:34:36 +02:00
Sebastian Stenzel 746c8f54eb Merge branch 'develop' into feature/new-hub-keyloading
# Conflicts:
#	src/main/java/org/cryptomator/ui/keyloading/hub/CreateDeviceDto.java
#	src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java
2023-06-30 16:20:59 +02:00
Sebastian Stenzel 9fc1efa005 adjust labels 2023-06-29 10:38:31 +02:00
Sebastian Stenzel 448eac8ff5 cleanup 2023-06-23 12:06:06 +02:00
Sebastian Stenzel 5a18d086e0 register device using a setup code 2023-06-22 17:16:00 +02:00
Sebastian Stenzel 918ace2eb6 Merge branch 'develop' into feature/new-hub-keyloading 2023-06-14 10:31:58 +02:00
Sebastian Stenzel fe733967dc send device type in device registration request 2023-06-01 15:06:21 +02:00
Sebastian Stenzel 59f5c0cb12 started new unlock workflow using user-specific private key 2023-05-09 17:09:49 +02:00
127 changed files with 1610 additions and 763 deletions
+2 -1
View File
@@ -43,6 +43,7 @@ body:
- WinFsp (Local Drive)
- FUSE-T
- macFUSE
- FUSE
- WebDAV (Windows Explorer)
- WebDAV (AppleScript)
- WebDAV (gio)
@@ -95,4 +96,4 @@ body:
id: further-info
attributes:
label: Anything else?
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
+2 -2
View File
@@ -8,7 +8,7 @@ updates:
time: "06:00"
timezone: "UTC"
groups:
all: # one PR for all dependencies
maven-dependencies:
patterns:
- "*"
@@ -17,7 +17,7 @@ updates:
schedule:
interval: "monthly"
groups:
all: # one PR for all actions
github-actions:
patterns:
- "*"
labels:
+42 -17
View File
@@ -10,7 +10,8 @@ on:
required: false
env:
JAVA_VERSION: 20
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
jobs:
get-version:
@@ -20,26 +21,50 @@ jobs:
build:
name: Build AppImage
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
needs: [get-version]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
- os: [self-hosted, Linux, ARM64]
appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
check-latest: true
cache: 'maven'
- name: Ensure major jfx version in pom equals in jdk
shell: pwsh
- name: Download OpenJFX jmods
id: download-jmods
run: |
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
exit 1
}
fi
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
@@ -53,7 +78,7 @@ jobs:
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
--strip-native-commands
--no-header-files
@@ -118,7 +143,7 @@ jobs:
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit
run: |
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o appimagetool.AppImage
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -130,8 +155,8 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Create detached GPG signatures
run: |
@@ -140,7 +165,7 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: appimage
name: appimage-${{ matrix.appimage-suffix }}
path: |
cryptomator-*.AppImage
cryptomator-*.AppImage.zsync
+4 -3
View File
@@ -6,7 +6,8 @@ on:
types: [labeled]
env:
JAVA_VERSION: 20
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
defaults:
run:
@@ -17,10 +18,10 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
+13 -5
View File
@@ -16,16 +16,20 @@ on:
type: boolean
env:
JAVA_VERSION: 20
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
COFFEELIBS_JDK_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
jobs:
build:
name: Build Debian Package
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- id: versions
name: Get version information
run: |
@@ -39,12 +43,13 @@ jobs:
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
@@ -52,9 +57,11 @@ jobs:
id: download-jmods
run: |
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
mkdir -p jmods/amd64
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
mkdir -p jmods/aarch64
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
- name: Ensure major jfx version in pom and in jmods is the same
@@ -91,7 +98,8 @@ jobs:
run: |
cp -r dist/linux/debian/ pkgdir
export RFC2822_TIMESTAMP=`date --rfc-2822`
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
export DISABLE_UPDATE_CHECK=${{ inputs.dput }}
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
mv pkgdir cryptomator_${{ inputs.ppaver }}
+9 -3
View File
@@ -2,7 +2,7 @@ name: Update Error Database
on:
discussion:
types: [created, edited, category_changed, answered, unanswered]
types: [created, edited, deleted, category_changed, answered, unanswered]
discussion_comment:
types: [created, edited, deleted]
@@ -12,6 +12,7 @@ jobs:
if: github.event.discussion.category.name == 'Errors'
steps:
- name: Query Discussion Data
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
id: query-data
uses: actions/github-script@v6
with:
@@ -47,8 +48,13 @@ jobs:
- name: Merge Error Code Data
run: |
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
jq -s '.[0] * .[1]' original.json new.json > merged.json
if [ ! -z "$DISCUSSION" ]
then
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
jq -s '.[0] * .[1]' original.json new.json > merged.json
else
cat original.json | jq 'del(.[] | select(.url=="https://github.com/cryptomator/cryptomator/discussions/${{ github.event.discussion.number }}"))' > merged.json
fi
env:
DISCUSSION: ${{ steps.query-data.outputs.result }}
- name: Patch Gist
+4 -5
View File
@@ -22,9 +22,8 @@ on:
value: ${{ jobs.determine-version.outputs.type }}
env:
JAVA_VERSION: 20
JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
jobs:
determine-version:
@@ -36,7 +35,7 @@ jobs:
revNum: ${{ steps.versions.outputs.revNum }}
type: ${{ steps.versions.outputs.type}}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Java
@@ -44,7 +43,7 @@ jobs:
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: ${{ env.JAVA_CACHE }}
cache: 'maven'
- id: versions
name: Get version information
run: |
+12 -12
View File
@@ -15,7 +15,8 @@ on:
type: boolean
env:
JAVA_VERSION: 20
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
jobs:
get-version:
@@ -36,24 +37,24 @@ jobs:
output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: macFUSE
openjfx-url: https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip
openjfx-sha: 55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip'
openjfx-sha: '55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c'
- os: [self-hosted, macOS, ARM64]
architecture: aarch64
output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: FUSE-T
openjfx-url: https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip
openjfx-sha: c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk'
architecture: ${{ matrix.architecture }}
check-latest: true
cache: 'maven'
- name: Download OpenJFX jmods
id: download-jmods
@@ -61,7 +62,7 @@ jobs:
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods/
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
@@ -71,8 +72,8 @@ jobs:
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION_AMD64})"
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
exit 1
fi
- name: Set version
@@ -221,7 +222,6 @@ jobs:
--app-drop-link 512 245
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".fseventsd" 320 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
+4 -3
View File
@@ -4,7 +4,8 @@ on:
pull_request:
env:
JAVA_VERSION: 20
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
defaults:
run:
@@ -16,10 +17,10 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
+1 -4
View File
@@ -6,9 +6,6 @@ on:
- 'release/**'
- 'hotfix/**'
env:
JAVA_VERSION: 20
defaults:
run:
shell: bash
@@ -18,7 +15,7 @@ jobs:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- id: validate-pom-version
name: Validate POM version
run: |
+49 -18
View File
@@ -14,11 +14,10 @@ on:
env:
JAVA_VERSION: 20
JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
JFX_JMODS_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
defaults:
run:
@@ -38,20 +37,20 @@ jobs:
LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk'
cache: ${{ env.JAVA_CACHE }}
check-latest: true
cache: 'maven'
- name: Download and extract JavaFX jmods from Gluon
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
run: |
curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}.";
curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
}
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
@@ -144,9 +143,29 @@ jobs:
- name: Fix permissions
run: attrib -r appdir/Cryptomator/Cryptomator.exe
shell: pwsh
- name: Extract integrations DLL for code signing
- name: Extract jars with DLLs for Codesigning
shell: pwsh
run: gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --extract integrations.dll }
run: |
Add-Type -AssemblyName "System.io.compression.filesystem"
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
$jarExtractDir = New-Item -Path ".\appdir\jar-extract" -ItemType Directory
#for all jars inspect
Get-ChildItem -Path $jarFolder -Filter "*.jar" | ForEach-Object {
$jar = [Io.compression.zipfile]::OpenRead($_.FullName)
if (@($jar.Entries | Where-Object {$_.Name.ToString().EndsWith(".dll")} | Select-Object -First 1).Count -gt 0) {
#jars containing dlls extract
Set-Location $jarExtractDir
Expand-Archive -Path $_.FullName
}
$jar.Dispose()
}
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
shell: pwsh
run: |
New-Item -Path appdir/jpackage-jmod -ItemType Directory
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
- name: Codesign
uses: skymatic/code-sign-action@v2
with:
@@ -155,12 +174,22 @@ jobs:
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator
timestampUrl: 'http://timestamp.digicert.com'
folder: appdir/Cryptomator
folder: appdir
recursive: true
- name: Repack signed DLL into jar
- name: Replace DLLs inside jars with signed ones
shell: pwsh
run: |
gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --update integrations.dll; Remove-Item integrations.dll}
$jarExtractDir = Resolve-Path ".\appdir\jar-extract"
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
Get-ChildItem -Path $jarExtractDir | ForEach-Object {
$jarName = $_.Name
$jarFile = "${jarFolder}\${jarName}.jar"
Set-Location $_
Get-ChildItem -Path $_ -Recurse -File "*.dll" | ForEach-Object {
# update jar with signed dll
jar --file="$jarFile" --update $(Resolve-Path -Relative -Path $_)
}
}
- name: Generate license for MSI
run: >
mvn -B license:add-third-party
@@ -194,6 +223,7 @@ jobs:
--file-associations dist/win/resources/FAvaultFile.properties
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Codesign MSI
uses: skymatic/code-sign-action@v2
with:
@@ -235,7 +265,7 @@ jobs:
runs-on: windows-latest
needs: [get-version, build-msi]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download .msi
uses: actions/download-artifact@v3
with:
@@ -247,7 +277,8 @@ jobs:
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: ${{ env.JAVA_CACHE }}
check-latest: true
cache: 'maven'
- name: Generate license for exe
run: >
mvn -B license:add-third-party
+11 -12
View File
@@ -14,10 +14,10 @@
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.45/dagger-compiler-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.45/dagger-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.48.1/dagger-compiler-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.45/dagger-producers-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -26,20 +26,19 @@
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.45/dagger-spi-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.7.0-1.0.6/symbol-processing-api-1.7.0-1.0.6.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.7.0/kotlin-stdlib-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.0/kotlin-stdlib-jdk8-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.0/kotlin-stdlib-jdk7-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.5.0/kotlinx-metadata-jvm-0.5.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
+1 -1
View File
@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+6 -4
View File
@@ -1,4 +1,5 @@
#!/bin/bash
set -e
cd $(dirname $0)
REVISION_NO=`git rev-list --count HEAD`
@@ -10,6 +11,7 @@ command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
SEMVER_STR=${VERSION}
MACHINE_TYPE=$(uname -m)
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
@@ -83,17 +85,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage
/tmp/appimagetool.AppImage \
Cryptomator.AppDir \
cryptomator-${SEMVER_STR}-x86_64.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
echo ""
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-x86_64.AppImage"
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
echo ""
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.AppImage"
echo ""
@@ -66,6 +66,9 @@
</content_rating>
<releases>
<release date="2023-09-20" version="1.10.1"/>
<release date="2023-09-11" version="1.10.0"/>
<release date="2023-08-11" version="1.9.4"/>
<release date="2023-08-07" version="1.9.3"/>
<release date="2023-07-24" version="1.9.2"/>
<release date="2023-06-07" version="1.9.1"/>
+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), coffeelibs-jdk-21, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git
+2 -1
View File
@@ -4,7 +4,7 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
@@ -59,6 +59,7 @@ override_dh_auto_build:
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \
--verbose
+6 -6
View File
@@ -49,21 +49,22 @@ fi
# download and check jmods
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
mkdir -p openjfx-jmods/
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods/
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
exit 1
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
exit 1
fi
# compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime
@@ -168,6 +169,5 @@ create-dmg \
--app-drop-link 512 245 \
--eula "resources/license.rtf" \
--icon ".background" 128 758 \
--icon ".fseventsd" 320 758 \
--icon ".VolumeIcon.icns" 512 758 \
${APP_NAME}-${VERSION_NO}.dmg dmg
Binary file not shown.
+5 -3
View File
@@ -63,10 +63,11 @@ if( !(Test-Path -Path $jfxJmodsZip) ) {
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
exit 1;
exit 1;
}
Expand-Archive -Force -Path $jfxJmodsZip -DestinationPath ".\resources\"
Move-Item -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
& "$Env:JAVA_HOME\bin\jlink" `
@@ -143,6 +144,7 @@ try {
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
$Env:JP_WIXHELPER_DIR = "."
& "$Env:JAVA_HOME\bin\jpackage" `
--verbose `
--type msi `
+1 -1
View File
@@ -3,5 +3,5 @@
::REPLACE ME
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS%
+1 -1
View File
@@ -2,4 +2,4 @@
:: see comments in file ./version170-migrate-settings.ps1
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command .\version170-migrate-settings.ps1
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1
+1 -1
View File
@@ -70,7 +70,7 @@
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
<Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll"/>
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
<?ifndef SkipCryptomatorLegacyCheck ?>
+49 -28
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.10.0</version>
<version>1.11.0</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,7 +26,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>20</project.jdk.version>
<project.jdk.version>21</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
@@ -35,36 +35,43 @@
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.6.7</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.2</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.3.0-beta6</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>3.0.0</cryptomator.fuse.version>
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>4.0.0-beta4</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.3</cryptomator.webdav.version>
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<commons-lang3.version>3.12.0</commons-lang3.version>
<dagger.version>2.45</dagger.version>
<commons-lang3.version>3.13.0</commons-lang3.version>
<dagger.version>2.48.1</dagger.version>
<easybind.version>2.2</easybind.version>
<guava.version>32.0.1-jre</guava.version>
<jackson.version>2.15.2</jackson.version>
<guava.version>32.1.3-jre</guava.version>
<jackson.version>2.15.3</jackson.version>
<javafx.version>20.0.2</javafx.version>
<jwt.version>4.4.0</jwt.version>
<nimbus-jose.version>9.31</nimbus-jose.version>
<logback.version>1.4.7</logback.version>
<slf4j.version>2.0.7</slf4j.version>
<tinyoauth2.version>0.5.1</tinyoauth2.version>
<zxcvbn.version>1.7.0</zxcvbn.version>
<nimbus-jose.version>9.37</nimbus-jose.version>
<logback.version>1.4.11</logback.version>
<slf4j.version>2.0.9</slf4j.version>
<tinyoauth2.version>0.7.0</tinyoauth2.version>
<zxcvbn.version>1.8.2</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.9.3</junit.jupiter.version>
<mockito.version>5.3.1</mockito.version>
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<mockito.version>5.6.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>8.1.2</dependency-check.version>
<jacoco.version>0.8.9</jacoco.version>
<jetbrains.annotations.version>24.0.1</jetbrains.annotations.version>
<dependency-check.version>8.4.0</dependency-check.version>
<jacoco.version>0.8.11</jacoco.version>
<license-generator.version>2.2.0</license-generator.version>
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
<mvn-compiler.version>3.11.0</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.6.0</mvn-dependency.version>
<mvn-surefire.version>3.1.2</mvn-surefire.version>
<mvn-jar.version>3.3.0</mvn-jar.version>
</properties>
<dependencies>
@@ -240,7 +247,7 @@
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.2</version>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
@@ -258,32 +265,32 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<version>${mvn-compiler.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.0</version>
<version>${mvn-resources.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.3.0</version>
<version>${mvn-dependency.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<version>${mvn-surefire.version}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.0.0</version>
<version>${license-generator.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<version>${mvn-jar.version}</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
@@ -332,8 +339,22 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>me.fabriciorby</groupId>
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
<version>${junit-tree-reporter.version}</version>
</dependency>
</dependencies>
<configuration>
<argLine>--enable-preview</argLine>
<reportFormat>plain</reportFormat>
<consoleOutputReporter>
<disable>true</disable>
</consoleOutputReporter>
<statelessTestsetInfoReporter
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
</statelessTestsetInfoReporter>
</configuration>
</plugin>
<plugin>
@@ -2,6 +2,7 @@ package org.cryptomator.common;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,6 +32,7 @@ public class Environment {
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
private Environment() {}
@@ -43,15 +45,16 @@ public class Environment {
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
}
public static Environment getInstance() {
@@ -74,10 +77,6 @@ public class Environment {
return getPaths(SETTINGS_PATH_PROP_NAME);
}
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Stream<Path> getIpcSocketPath() {
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
}
@@ -86,6 +85,10 @@ public class Environment {
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
}
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Optional<Path> getLogDir() {
return getPath(LOG_DIR_PROP_NAME);
}
@@ -94,14 +97,14 @@ public class Environment {
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
}
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME);
}
public Optional<Path> getMountPointsDir() {
return getPath(MOUNTPOINT_DIR_PROP_NAME);
}
public int getMinPwLength() {
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
}
/**
* Returns the app version defined in the {@value APP_VERSION_PROP_NAME} property or returns "SNAPSHOT".
*
@@ -115,20 +118,24 @@ public class Environment {
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
}
public int getMinPwLength() {
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME);
}
public boolean showTrayIcon() {
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
}
public boolean disableUpdateCheck() {
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
}
private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);
}
// visible for testing
@VisibleForTesting
Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
@@ -3,6 +3,7 @@ package org.cryptomator.common;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.Locale;
import java.util.Objects;
@@ -114,7 +115,7 @@ public class ErrorCode {
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
*/
// visible for testing
@VisibleForTesting
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
if (allFrames.length < bottomFrames.length) {
// if frames had been stacked on top of bottomFrames, allFrames would be larger
@@ -124,7 +125,7 @@ public class ErrorCode {
}
}
// visible for testing
@VisibleForTesting
static <T> int commonSuffixLength(T[] set, T[] subset) {
Preconditions.checkArgument(set.length >= subset.length);
// iterate items backwards as long as they are identical
@@ -62,7 +62,7 @@ public final class OneDriveWindowsLocationPresetsProvider implements LocationPre
ProcessBuilder command = new ProcessBuilder(args);
Process p = command.start();
waitForSuccess(p, 3, "`reg query`");
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
return p.inputReader(StandardCharsets.ISO_8859_1).lines().filter(outputFilter);
}
@@ -1,6 +1,7 @@
package org.cryptomator.common.mount;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,7 +67,7 @@ public final class MountWithinParentUtil {
}
}
//visible for testing
@VisibleForTesting
static MountPointState getMountPointState(Path path) throws IOException, IllegalMountPointException {
if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
return MountPointState.NOT_EXISTING;
@@ -82,7 +83,7 @@ public final class MountWithinParentUtil {
return MountPointState.BROKEN_JUNCTION;
}
//visible for testing
@VisibleForTesting
enum MountPointState {
NOT_EXISTING,
@@ -93,7 +94,7 @@ public final class MountWithinParentUtil {
}
//visible for testing
@VisibleForTesting
static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException {
checkIsHideawayDirectory(mountPoint, hideaway);
Files.delete(hideaway); //Fails if not empty
@@ -155,7 +156,7 @@ public final class MountWithinParentUtil {
}
}
//visible for testing
@VisibleForTesting
static Path getHideaway(Path mountPoint) {
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
}
@@ -65,7 +65,6 @@ public class Settings {
public final IntegerProperty windowYPosition;
public final IntegerProperty windowWidth;
public final IntegerProperty windowHeight;
public final StringProperty displayConfiguration;
public final StringProperty language;
public final StringProperty mountService;
public final StringProperty lastUpdateCheck;
@@ -103,7 +102,6 @@ public class Settings {
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
this.language = new SimpleStringProperty(this, "language", json.language);
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
@@ -131,7 +129,6 @@ public class Settings {
windowYPosition.addListener(this::somethingChanged);
windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
lastUpdateCheck.addListener(this::somethingChanged);
@@ -186,7 +183,6 @@ public class Settings {
json.windowYPosition = windowYPosition.get();
json.windowWidth = windowWidth.get();
json.windowHeight = windowHeight.get();
json.displayConfiguration = displayConfiguration.get();
json.language = language.get();
json.mountService = mountService.get();
json.lastUpdateCheck = lastUpdateCheck.get();
@@ -31,9 +31,6 @@ class SettingsJson {
@JsonProperty("theme")
UiTheme theme = Settings.DEFAULT_THEME;
@JsonProperty("displayConfiguration")
String displayConfiguration;
@JsonProperty("keychainProvider")
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
@@ -9,6 +9,7 @@ import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
@@ -126,7 +127,7 @@ public class VaultSettings {
return json;
}
//visible for testing
@VisibleForTesting
static String normalizeDisplayName(String original) {
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
return "_";
@@ -6,6 +6,7 @@
*******************************************************************************/
package org.cryptomator.launcher;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,7 +49,7 @@ class FileOpenRequestHandler {
handleLaunchArgs(FileSystems.getDefault(), args);
}
// visible for testing
@VisibleForTesting
void handleLaunchArgs(FileSystem fs, List<String> args) {
Collection<Path> pathsToOpen = args.stream().map(str -> {
try {
@@ -5,6 +5,7 @@ import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.classic.spi.ConfiguratorRank;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
@@ -19,6 +20,7 @@ import org.cryptomator.common.Environment;
import java.nio.file.Path;
import java.util.Map;
@ConfiguratorRank(ConfiguratorRank.CUSTOM_NORMAL_PRIORITY)
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
@@ -45,9 +45,8 @@ public abstract class AddVaultModule {
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(primaryStage);
@@ -90,13 +89,6 @@ public abstract class AddVaultModule {
// ------------------
@Provides
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
@AddVaultWizardScoped
static Scene provideWelcomeScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_WELCOME);
}
@Provides
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
@AddVaultWizardScoped
@@ -148,11 +140,6 @@ public abstract class AddVaultModule {
// ------------------
@Binds
@IntoMap
@FxControllerKey(AddVaultWelcomeController.class)
abstract FxController bindWelcomeController(AddVaultWelcomeController controller);
@Binds
@IntoMap
@FxControllerKey(ChooseExistingVaultController.class)
@@ -1,38 +0,0 @@
package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.scene.Scene;
import javafx.stage.Stage;
@AddVaultWizardScoped
public class AddVaultWelcomeController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(AddVaultWelcomeController.class);
private final Stage window;
private final Lazy<Scene> chooseExistingVaultScene;
private final Lazy<Scene> createNewVaultScene;
@Inject
AddVaultWelcomeController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_EXISTING) Lazy<Scene> chooseExistingVaultScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> createNewVaultScene) {
this.window = window;
this.chooseExistingVaultScene = chooseExistingVaultScene;
this.createNewVaultScene = createNewVaultScene;
}
public void createNewVault() {
LOG.debug("AddVaultWelcomeController.createNewVault()");
window.setScene(createNewVaultScene.get());
}
public void chooseExistingVault() {
LOG.debug("AddVaultWelcomeController.chooseExistingVault()");
window.setScene(chooseExistingVaultScene.get());
}
}
@@ -12,6 +12,7 @@ import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ResourceBundle;
@AddVaultWizardScoped
@Subcomponent(modules = {AddVaultModule.class})
@@ -20,12 +21,23 @@ public interface AddVaultWizardComponent {
@AddVaultWizardWindow
Stage window();
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
Lazy<Scene> scene();
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME)
Lazy<Scene> sceneNew();
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
Lazy<Scene> sceneExisting();
default void showAddVaultWizard() {
default void showAddNewVaultWizard(ResourceBundle resourceBundle) {
Stage stage = window();
stage.setScene(scene().get());
stage.setScene(sceneNew().get());
stage.setTitle(resourceBundle.getString("addvaultwizard.new.title"));
stage.sizeToScene();
stage.show();
}
default void showAddExistingVaultWizard(ResourceBundle resourceBundle) {
Stage stage = window();
stage.setScene(sceneExisting().get());
stage.setTitle(resourceBundle.getString("addvaultwizard.existing.title"));
stage.sizeToScene();
stage.show();
}
@@ -35,7 +35,6 @@ public class ChooseExistingVaultController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ChooseExistingVaultController.class);
private final Stage window;
private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> successScene;
private final FxApplicationWindows appWindows;
private final ObjectProperty<Path> vaultPath;
@@ -45,9 +44,15 @@ public class ChooseExistingVaultController implements FxController {
private final ObservableValue<Image> screenshot;
@Inject
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, FxApplicationStyle applicationStyle) {
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, //
@FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, //
FxApplicationWindows appWindows, //
ObjectProperty<Path> vaultPath, //
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle, //
FxApplicationStyle applicationStyle) {
this.window = window;
this.welcomeScene = welcomeScene;
this.successScene = successScene;
this.appWindows = appWindows;
this.vaultPath = vaultPath;
@@ -70,11 +75,6 @@ public class ChooseExistingVaultController implements FxController {
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
}
@FXML
public void back() {
window.setScene(welcomeScene.get());
}
@FXML
public void chooseFileAndNext() {
FileChooser fileChooser = new FileChooser();
@@ -17,7 +17,6 @@ import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.regex.Pattern;
@AddVaultWizardScoped
@@ -27,16 +26,17 @@ public class CreateNewVaultNameController implements FxController {
public TextField textField;
private final Stage window;
private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> chooseLocationScene;
private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName;
private final BooleanBinding validVaultName;
@Inject
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
ObjectProperty<Path> vaultPath, //
@Named("vaultName") StringProperty vaultName) {
this.window = window;
this.welcomeScene = welcomeScene;
this.chooseLocationScene = chooseLocationScene;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
@@ -58,11 +58,6 @@ public class CreateNewVaultNameController implements FxController {
}
}
@FXML
public void back() {
window.setScene(welcomeScene.get());
}
@FXML
public void next() {
window.setScene(chooseLocationScene.get());
@@ -1,5 +1,7 @@
package org.cryptomator.ui.addvaultwizard;
import org.jetbrains.annotations.VisibleForTesting;
import javax.inject.Inject;
import java.util.List;
import java.util.ResourceBundle;
@@ -51,7 +53,7 @@ public class ReadmeGenerator {
resourceBundle.getString("addvault.new.readme.accessLocation.4")));
}
// visible for testing
@VisibleForTesting
String createDocument(Iterable<String> paragraphs) {
StringBuilder sb = new StringBuilder(RTF_HEADER);
for (String p : paragraphs) {
@@ -63,7 +65,7 @@ public class ReadmeGenerator {
return sb.toString();
}
// visible for testing
@VisibleForTesting
String escapeNonAsciiChars(CharSequence input) {
StringBuilder sb = new StringBuilder();
appendEscaped(sb, input);
@@ -8,7 +8,6 @@ public enum FxmlFile {
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
@@ -21,9 +20,10 @@ public enum FxmlFile {
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
HUB_LEGACY_REGISTER_DEVICE("/fxml/hub_legacy_register_device.fxml"), //
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"),
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
HUB_SETUP_DEVICE("/fxml/hub_setup_device.fxml"), //
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //
@@ -16,6 +16,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -116,7 +117,7 @@ public class HubToPasswordConvertController implements FxController {
}, Platform::runLater); //
}
//visible for testing
@VisibleForTesting
void convertInternal() throws CompletionException, IllegalArgumentException {
var passphrase = newPasswordController.getNewPassword();
var vaultPath = vault.getPath();
@@ -141,7 +142,7 @@ public class HubToPasswordConvertController implements FxController {
}
}
//visible for testing
@VisibleForTesting
void backupHubConfig(Path hubConfigPath) throws IOException {
byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath);
Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX);
@@ -149,7 +150,7 @@ public class HubToPasswordConvertController implements FxController {
LOG.debug("Successfully created hub config backup {}", backupPath.getFileName());
}
//visible for testing
@VisibleForTesting
Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException {
var unverifiedVaultConfig = vault.getVaultConfigCache().get();
try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
@@ -31,6 +31,7 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
@@ -42,19 +43,21 @@ public class ErrorController implements FxController {
private static final ObjectMapper JSON = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class);
private static final String ERROR_CODES_URL = "https://api.cryptomator.org/desktop/error-codes.json";
private static final String USER_AGENT_FORMAT = "Cryptomator/%s (Build %s) (%s %s %s)";
private static final String ERROR_CODES_URL_FORMAT = "https://api.cryptomator.org/desktop/error-codes.json?error-code=%s";
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
private static final String SEARCH_ERRORCODE_DELIM = " OR ";
private static final String REPORT_BODY_TEMPLATE = """
<!-- 💚 Thank you for reporting this error. -->
OS: %s / %s
App: %s / %s
<!-- ✏ Please describe what happened as accurately as possible. -->
<!-- 📋 Please also copy and paste the detail text from the error window. -->
<!-- ℹ Text enclosed like this (chevrons, exclamation mark, two dashes) is not visible to others! -->
Description:
<!-- 📋 Please also copy and paste the details from the error window. -->
Details:
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
""";
@@ -141,11 +144,19 @@ public class ErrorController implements FxController {
@FXML
public void lookUpSolution() {
String userAgent = USER_AGENT_FORMAT.formatted( //
environment.getAppVersion(), //
environment.getBuildNumber().orElse("undefined"), //
System.getProperty("os.name"), //
System.getProperty("os.version"), //
System.getProperty("os.arch"));
isLoadingHttpResponse.set(true);
askedForLookupDatabasePermission.set(true);
HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
HttpRequest httpRequest = HttpRequest.newBuilder()//
.uri(URI.create(ERROR_CODES_URL))//
.header("User-Agent", userAgent)
.timeout(Duration.ofSeconds(10))
.uri(URI.create(ERROR_CODES_URL_FORMAT.formatted(URLEncoder.encode(errorCode.toString(),StandardCharsets.UTF_8))))//
.build();
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())//
.thenAcceptAsync(this::loadHttpResponse, executorService)//
@@ -1,6 +1,7 @@
package org.cryptomator.ui.fxapp;
import dagger.Lazy;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
@@ -17,6 +18,7 @@ public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
private final long startupTime;
private final Environment environment;
private final Settings settings;
private final AppLaunchEventHandler launchEventHandler;
private final Lazy<TrayMenuComponent> trayMenu;
@@ -26,8 +28,9 @@ public class FxApplication {
private final AutoUnlocker autoUnlocker;
@Inject
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
this.startupTime = startupTime;
this.environment = environment;
this.settings = settings;
this.launchEventHandler = launchEventHandler;
this.trayMenu = trayMenu;
@@ -68,7 +71,9 @@ public class FxApplication {
return null;
});
appWindows.checkAndShowUpdateReminderWindow();
if (!environment.disableUpdateCheck()) {
appWindows.checkAndShowUpdateReminderWindow();
}
launchEventHandler.startHandlingLaunchEvents();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
@@ -22,23 +22,23 @@ public class UpdateChecker {
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
private static final Duration AUTOCHECK_DELAY = Duration.seconds(5);
private final Environment env;
private final Settings settings;
private final String currentVersion;
private final StringProperty latestVersionProperty;
private final Comparator<String> semVerComparator;
private final ScheduledService<String> updateCheckerService;
@Inject
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
this.env = env;
this.settings = settings;
this.latestVersionProperty = latestVersionProperty;
this.semVerComparator = semVerComparator;
this.updateCheckerService = updateCheckerService;
this.currentVersion = env.getAppVersion();
}
public void automaticallyCheckForUpdatesIfEnabled() {
if (settings.checkForUpdates.get()) {
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
startCheckingForUpdates(AUTOCHECK_DELAY);
}
}
@@ -63,9 +63,9 @@ public class UpdateChecker {
private void checkSucceeded(WorkerStateEvent event) {
String latestVersion = updateCheckerService.getValue();
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion);
if (semVerComparator.compare(currentVersion, latestVersion) < 0) {
if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) {
// update is available
latestVersionProperty.set(latestVersion);
} else {
@@ -88,7 +88,7 @@ public class UpdateChecker {
}
public String getCurrentVersion() {
return currentVersion;
return env.getAppVersion();
}
}
@@ -63,6 +63,7 @@ public abstract class UpdateCheckerModule {
return HttpRequest.newBuilder() //
.uri(LATEST_VERSION_URI) //
.header("User-Agent", userAgent) //
.timeout(java.time.Duration.ofSeconds(10))
.build();
}
@@ -101,16 +101,16 @@ public class StartController implements FxController {
}
}
private void loadingKeyFailed(Throwable e) {
switch (e) {
case UnlockCancelledException uce -> {} //ok
case VaultKeyInvalidException vkie -> {
LOG.error("Invalid key"); //TODO: specific error screen
private void loadingKeyFailed(Throwable t) {
switch (t) {
case UnlockCancelledException e -> {} // ok // TODO: rename to _ with JEP 443
case VaultKeyInvalidException e -> { // TODO: rename to _ with JEP 443
LOG.error("Invalid key"); // TODO: specific error screen
appWindows.showErrorWindow(e, window, null);
}
default -> {
LOG.error("Failed to load key.", e);
appWindows.showErrorWindow(e, window, null);
LOG.error("Failed to load key.", t);
appWindows.showErrorWindow(t, window, null);
}
}
}
@@ -35,13 +35,13 @@ public class AuthFlowController implements FxController {
private final String deviceId;
private final HubConfig hubConfig;
private final AtomicReference<String> tokenRef;
private final CompletableFuture<JWEObject> result;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> receiveKeyScene;
private final ObjectProperty<URI> authUri;
private AuthFlowTask task;
@Inject
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
this.application = application;
this.window = window;
this.executor = executor;
@@ -8,6 +8,8 @@ import io.github.coffeelibs.tinyoauth2client.http.response.Response;
import javafx.concurrent.Task;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.time.Duration;
import java.util.function.Consumer;
class AuthFlowTask extends Task<String> {
@@ -34,6 +36,7 @@ class AuthFlowTask extends Task<String> {
protected String call() throws IOException, InterruptedException {
var response = TinyOAuth2.client(hubConfig.clientId) //
.withTokenEndpoint(URI.create(hubConfig.tokenEndpoint)) //
.withRequestTimeout(Duration.ofSeconds(10)) //
.authFlow(URI.create(hubConfig.authEndpoint)) //
.setSuccessResponse(Response.redirect(URI.create(hubConfig.authSuccessUrl + "&device=" + authFlowContext.deviceId()))) //
.setErrorResponse(Response.redirect(URI.create(hubConfig.authErrorUrl + "&device=" + authFlowContext.deviceId()))) //
@@ -1,5 +0,0 @@
package org.cryptomator.ui.keyloading.hub;
record CreateDeviceDto(String id, String name, String publicKey) {
}
@@ -1,19 +0,0 @@
package org.cryptomator.ui.keyloading.hub;
import com.google.common.io.CharStreams;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
class HttpHelper {
public static String readBody(HttpResponse<InputStream> response) throws IOException {
try (var in = response.body(); var reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
return CharStreams.toString(reader);
}
}
}
@@ -1,6 +1,10 @@
package org.cryptomator.ui.keyloading.hub;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.URI;
// needs to be accessible by JSON decoder
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -9,8 +13,19 @@ public class HubConfig {
public String clientId;
public String authEndpoint;
public String tokenEndpoint;
public String devicesResourceUrl;
public String authSuccessUrl;
public String authErrorUrl;
public @Nullable String apiBaseUrl;
@Deprecated // use apiBaseUrl + "/devices/"
public String devicesResourceUrl;
public URI getApiBaseUrl() {
if (apiBaseUrl != null) {
return URI.create(apiBaseUrl);
} else {
// legacy approach
assert devicesResourceUrl != null;
return URI.create(devicesResourceUrl + "/..").normalize();
}
}
}
@@ -1,7 +1,6 @@
package org.cryptomator.ui.keyloading.hub;
import com.google.common.io.BaseEncoding;
import com.nimbusds.jose.JWEObject;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -69,7 +68,7 @@ public abstract class HubKeyLoadingModule {
@Provides
@KeyLoadingScoped
static CompletableFuture<JWEObject> provideResult() {
static CompletableFuture<ReceivedKey> provideResult() {
return new CompletableFuture<>();
}
@@ -114,10 +113,10 @@ public abstract class HubKeyLoadingModule {
}
@Provides
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
static Scene provideHubLegacyRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE);
}
@Provides
@@ -134,6 +133,13 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_FAILED);
}
@Provides
@FxmlScene(FxmlFile.HUB_SETUP_DEVICE)
@KeyLoadingScoped
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_SETUP_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE)
@KeyLoadingScoped
@@ -166,6 +172,11 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(RegisterDeviceController.class)
abstract FxController bindRegisterDeviceController(RegisterDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(LegacyRegisterDeviceController.class)
abstract FxController bindLegacyRegisterDeviceController(LegacyRegisterDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(RegisterSuccessController.class)
@@ -36,11 +36,11 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
private final KeychainManager keychainManager;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> noKeychainScene;
private final CompletableFuture<JWEObject> result;
private final CompletableFuture<ReceivedKey> result;
private final DeviceKey deviceKey;
@Inject
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<JWEObject> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
this.window = window;
this.keychainManager = keychainManager;
window.setTitle(windowTitle);
@@ -60,7 +60,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
var keypair = deviceKey.get();
showWindow(authFlowScene);
var jwe = result.get();
return JWEHelper.decrypt(jwe, keypair.getPrivate());
return jwe.decryptMasterkey(keypair.getPrivate());
} catch (NoKeychainAccessProviderException e) {
showWindow(noKeychainScene);
throw new UnlockCancelledException("Unlock canceled due to missing prerequisites", e);
@@ -2,35 +2,103 @@ package org.cryptomator.ui.keyloading.hub;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.ECDHDecrypter;
import com.nimbusds.jose.crypto.ECDHEncrypter;
import com.nimbusds.jose.crypto.PasswordBasedDecrypter;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.jwk.gen.JWKGenerator;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import java.util.function.Function;
class JWEHelper {
private static final Logger LOG = LoggerFactory.getLogger(JWEHelper.class);
private static final String JWE_PAYLOAD_MASTERKEY_FIELD = "key";
private static final String JWE_PAYLOAD_KEY_FIELD = "key";
private static final String EC_ALG = "EC";
private JWEHelper(){}
public static Masterkey decrypt(JWEObject jwe, ECPrivateKey privateKey) throws MasterkeyLoadingFailedException {
public static JWEObject encryptUserKey(ECPrivateKey userKey, ECPublicKey deviceKey) {
try {
jwe.decrypt(new ECDHDecrypter(privateKey));
return readKey(jwe);
var encodedUserKey = Base64.getEncoder().encodeToString(userKey.getEncoded());
var keyGen = new ECKeyGenerator(Curve.P_384);
var ephemeralKeyPair = keyGen.generate();
var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedUserKey));
var jwe = new JWEObject(header, payload);
jwe.encrypt(new ECDHEncrypter(deviceKey));
return jwe;
} catch (JOSEException e) {
LOG.warn("Failed to decrypt JWE: {}", jwe);
throw new MasterkeyLoadingFailedException("Failed to decrypt JWE", e);
throw new RuntimeException(e);
}
}
private static Masterkey readKey(JWEObject jwe) throws MasterkeyLoadingFailedException {
public static ECPrivateKey decryptUserKey(JWEObject jwe, String setupCode) throws InvalidJweKeyException {
try {
jwe.decrypt(new PasswordBasedDecrypter(setupCode));
return decodeUserKey(jwe);
} catch (JOSEException e) {
throw new InvalidJweKeyException(e);
}
}
public static ECPrivateKey decryptUserKey(JWEObject jwe, ECPrivateKey deviceKey) throws InvalidJweKeyException {
try {
jwe.decrypt(new ECDHDecrypter(deviceKey));
return decodeUserKey(jwe);
} catch (JOSEException e) {
throw new InvalidJweKeyException(e);
}
}
private static ECPrivateKey decodeUserKey(JWEObject decryptedJwe) {
try {
var keySpec = readKey(decryptedJwe, JWE_PAYLOAD_KEY_FIELD, PKCS8EncodedKeySpec::new);
var factory = KeyFactory.getInstance(EC_ALG);
var privateKey = factory.generatePrivate(keySpec);
if (privateKey instanceof ECPrivateKey ecPrivateKey) {
return ecPrivateKey;
} else {
throw new IllegalStateException(EC_ALG + " key factory not generating ECPrivateKeys");
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(EC_ALG + " not supported");
} catch (InvalidKeySpecException e) {
LOG.warn("Unexpected JWE payload: {}", decryptedJwe.getPayload());
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
}
}
public static Masterkey decryptVaultKey(JWEObject jwe, ECPrivateKey privateKey) throws InvalidJweKeyException {
try {
jwe.decrypt(new ECDHDecrypter(privateKey));
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, Masterkey::new);
} catch (JOSEException e) {
throw new InvalidJweKeyException(e);
}
}
private static <T> T readKey(JWEObject jwe, String keyField, Function<byte[], T> rawKeyFactory) throws MasterkeyLoadingFailedException {
Preconditions.checkArgument(jwe.getState() == JWEObject.State.DECRYPTED);
var fields = jwe.getPayload().toJSONObject();
if (fields == null) {
@@ -39,11 +107,11 @@ class JWEHelper {
}
var keyBytes = new byte[0];
try {
if (fields.get(JWE_PAYLOAD_MASTERKEY_FIELD) instanceof String key) {
if (fields.get(keyField) instanceof String key) {
keyBytes = BaseEncoding.base64().decode(key);
return new Masterkey(keyBytes);
return rawKeyFactory.apply(keyBytes);
} else {
throw new IllegalArgumentException("JWE payload doesn't contain field " + JWE_PAYLOAD_MASTERKEY_FIELD);
throw new IllegalArgumentException("JWE payload doesn't contain field " + keyField);
}
} catch (IllegalArgumentException e) {
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
@@ -52,4 +120,11 @@ class JWEHelper {
Arrays.fill(keyBytes, (byte) 0x00);
}
}
public static class InvalidJweKeyException extends MasterkeyLoadingFailedException {
public InvalidJweKeyException(Throwable cause) {
super("Invalid key", cause);
}
}
}
@@ -0,0 +1,191 @@
package org.cryptomator.ui.keyloading.hub;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dagger.Lazy;
import org.cryptomator.common.settings.DeviceKey;
import org.cryptomator.cryptolib.common.P384KeyPair;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@KeyLoadingScoped
public class LegacyRegisterDeviceController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(LegacyRegisterDeviceController.class);
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
private final Stage window;
private final HubConfig hubConfig;
private final String bearerToken;
private final Lazy<Scene> registerSuccessScene;
private final Lazy<Scene> registerFailedScene;
private final String deviceId;
private final P384KeyPair keyPair;
private final CompletableFuture<ReceivedKey> result;
private final DecodedJWT jwt;
private final HttpClient httpClient;
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
public TextField deviceNameField;
public Button registerBtn;
@Inject
public LegacyRegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
this.keyPair = Objects.requireNonNull(deviceKey.get());
this.result = result;
this.bearerToken = Objects.requireNonNull(bearerToken.get());
this.registerSuccessScene = registerSuccessScene;
this.registerFailedScene = registerFailedScene;
this.jwt = JWT.decode(this.bearerToken);
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
}
public void initialize() {
deviceNameField.setText(determineHostname());
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
}
private String determineHostname() {
try {
var hostName = InetAddress.getLocalHost().getHostName();
return Objects.requireNonNullElse(hostName, "");
} catch (IOException e) {
return "";
}
}
@FXML
public void register() {
deviceNameAlreadyExists.set(false);
registerBtn.setContentDisplay(ContentDisplay.LEFT);
registerBtn.setDisable(true);
var deviceUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
var deviceKey = keyPair.getPublic().getEncoded();
var dto = new CreateDeviceDto();
dto.id = deviceId;
dto.name = deviceNameField.getText();
dto.publicKey = Base64.getUrlEncoder().withoutPadding().encodeToString(deviceKey);
var json = toJson(dto);
var request = HttpRequest.newBuilder(deviceUri) //
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
.header("Authorization", "Bearer " + bearerToken) //
.header("Content-Type", "application/json") //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
.thenApply(response -> {
if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
return response;
} else {
throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
}
}).handleAsync((response, throwable) -> {
if (response != null) {
this.handleResponse(response);
} else {
this.registrationFailed(throwable);
}
return null;
}, Platform::runLater);
}
private String toJson(CreateDeviceDto dto) {
try {
return JSON.writer().writeValueAsString(dto);
} catch (JacksonException e) {
throw new IllegalStateException("Failed to serialize DTO", e);
}
}
private void handleResponse(HttpResponse<Void> voidHttpResponse) {
assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
if (voidHttpResponse.statusCode() == 409) {
deviceNameAlreadyExists.set(true);
registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
registerBtn.setDisable(false);
} else {
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
window.setScene(registerSuccessScene.get());
}
}
private void registrationFailed(Throwable cause) {
LOG.warn("Device registration failed.", cause);
window.setScene(registerFailedScene.get());
result.completeExceptionally(cause);
}
@FXML
public void close() {
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
/* Getter */
public String getUserName() {
return jwt.getClaim("email").asString();
}
//--- Getters & Setters
public BooleanProperty deviceNameAlreadyExistsProperty() {
return deviceNameAlreadyExists;
}
public boolean getDeviceNameAlreadyExists() {
return deviceNameAlreadyExists.get();
}
private static class CreateDeviceDto {
public String id;
public String name;
public final String type = "DESKTOP";
public String publicKey;
}
}
@@ -1,5 +1,8 @@
package org.cryptomator.ui.keyloading.hub;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JWEObject;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
@@ -8,6 +11,9 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
@@ -17,14 +23,16 @@ import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -33,25 +41,32 @@ import java.util.concurrent.atomic.AtomicReference;
@KeyLoadingScoped
public class ReceiveKeyController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
private static final String SCHEME_PREFIX = "hub+";
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
private final Stage window;
private final HubConfig hubConfig;
private final String deviceId;
private final String bearerToken;
private final CompletableFuture<JWEObject> result;
private final Lazy<Scene> registerDeviceScene;
private final CompletableFuture<ReceivedKey> result;
private final Lazy<Scene> setupDeviceScene;
private final Lazy<Scene> legacyRegisterDeviceScene;
private final Lazy<Scene> unauthorizedScene;
private final URI vaultBaseUri;
private final Lazy<Scene> invalidLicenseScene;
private final HttpClient httpClient;
@Inject
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_SETUP_DEVICE) Lazy<Scene> setupDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
this.bearerToken = Objects.requireNonNull(tokenRef.get());
this.result = result;
this.registerDeviceScene = registerDeviceScene;
this.setupDeviceScene = setupDeviceScene;
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
this.unauthorizedScene = unauthorizedScene;
this.vaultBaseUri = getVaultBaseUri(vault);
this.invalidLicenseScene = invalidLicenseScene;
@@ -61,23 +76,120 @@ public class ReceiveKeyController implements FxController {
@FXML
public void initialize() {
var keyUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
var request = HttpRequest.newBuilder(keyUri) //
requestVaultMasterkey();
}
/**
* STEP 1 (Request): GET vault key for this user
*/
private void requestVaultMasterkey() {
var accessTokenUri = appendPath(vaultBaseUri, "/access-token");
var request = HttpRequest.newBuilder(accessTokenUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) //
.thenAcceptAsync(this::loadedExistingKey, Platform::runLater) //
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
.thenAcceptAsync(this::receivedVaultMasterkey, Platform::runLater) //
.exceptionally(this::retrievalFailed);
}
private void loadedExistingKey(HttpResponse<InputStream> response) {
/**
* STEP 1 (Response): GET vault key for this user
*
* @param response Response
*/
private void receivedVaultMasterkey(HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
switch (response.statusCode()) {
case 200 -> requestUserKey(response.body());
case 402 -> licenseExceeded();
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
case 404 -> requestLegacyAccessToken();
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
}
}
/**
* STEP 2 (Request): GET user key for this device
*/
private void requestUserKey(String encryptedVaultKey) {
var deviceTokenUri = URI.create(hubConfig.getApiBaseUrl() + "/devices/" + deviceId);
var request = HttpRequest.newBuilder(deviceTokenUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
.thenAcceptAsync(response -> receivedUserKey(encryptedVaultKey, response), Platform::runLater) //
.exceptionally(this::retrievalFailed);
}
/**
* STEP 2 (Response): GET user key for this device
*
* @param response Response
*/
private void receivedUserKey(String encryptedVaultKey, HttpResponse<String> response) {
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
try {
switch (response.statusCode()) {
case 200 -> retrievalSucceeded(response);
case 200 -> {
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
receivedBothEncryptedKeys(encryptedVaultKey, device.userPrivateKey);
}
case 404 -> needsDeviceSetup(); // TODO: using the setup code, we can theoretically immediately unlock
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void needsDeviceSetup() {
window.setScene(setupDeviceScene.get());
}
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) throws IOException {
try {
var vaultKeyJwe = JWEObject.parse(encryptedVaultKey);
var userKeyJwe = JWEObject.parse(encryptedUserKey);
result.complete(ReceivedKey.vaultKeyAndUserKey(vaultKeyJwe, userKeyJwe));
window.close();
} catch (ParseException e) {
throw new IOException("Failed to parse JWE", e);
}
}
/**
* LEGACY FALLBACK (Request): GET the legacy access token from Hub 1.x
*/
@Deprecated
private void requestLegacyAccessToken() {
var legacyAccessTokenUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
.header("Authorization", "Bearer " + bearerToken) //
.GET() //
.timeout(REQ_TIMEOUT) //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
.thenAcceptAsync(this::receivedLegacyAccessTokenResponse, Platform::runLater) //
.exceptionally(this::retrievalFailed);
}
/**
* LEGACY FALLBACK (Response)
*
* @param response Response
*/
@Deprecated
private void receivedLegacyAccessTokenResponse(HttpResponse<String> response) {
try {
switch (response.statusCode()) {
case 200 -> receivedLegacyAccessTokenSuccess(response.body());
case 402 -> licenseExceeded();
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access
case 404 -> needsDeviceRegistration();
case 404 -> needsLegacyDeviceRegistration();
default -> throw new IOException("Unexpected response " + response.statusCode());
}
} catch (IOException e) {
@@ -85,10 +197,11 @@ public class ReceiveKeyController implements FxController {
}
}
private void retrievalSucceeded(HttpResponse<InputStream> response) throws IOException {
@Deprecated
private void receivedLegacyAccessTokenSuccess(String rawToken) throws IOException {
try {
var string = HttpHelper.readBody(response);
result.complete(JWEObject.parse(string));
var token = JWEObject.parse(rawToken);
result.complete(ReceivedKey.legacyDeviceKey(token));
window.close();
} catch (ParseException e) {
throw new IOException("Failed to parse JWE", e);
@@ -99,8 +212,9 @@ public class ReceiveKeyController implements FxController {
window.setScene(invalidLicenseScene.get());
}
private void needsDeviceRegistration() {
window.setScene(registerDeviceScene.get());
@Deprecated
private void needsLegacyDeviceRegistration() {
window.setScene(legacyRegisterDeviceScene.get());
}
private void accessNotGranted() {
@@ -132,14 +246,17 @@ public class ReceiveKeyController implements FxController {
private static URI getVaultBaseUri(Vault vault) {
try {
var kid = vault.getVaultConfigCache().get().getKeyId();
assert kid.getScheme().startsWith(SCHEME_PREFIX);
var hubUriScheme = kid.getScheme().substring(SCHEME_PREFIX.length());
return new URI(hubUriScheme, kid.getSchemeSpecificPart(), kid.getFragment());
var url = vault.getVaultConfigCache().get().getKeyId();
assert url.getScheme().startsWith(SCHEME_PREFIX);
var correctedScheme = url.getScheme().substring(SCHEME_PREFIX.length());
return new URI(correctedScheme, url.getSchemeSpecificPart(), url.getFragment());
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (URISyntaxException e) {
throw new IllegalStateException("URI constructed from params known to be valid", e);
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
}
@@ -0,0 +1,45 @@
package org.cryptomator.ui.keyloading.hub;
import com.nimbusds.jose.JWEObject;
import org.cryptomator.cryptolib.api.Masterkey;
import java.security.interfaces.ECPrivateKey;
@FunctionalInterface
interface ReceivedKey {
/**
* Decrypts the vault key.
*
* @param deviceKey This device's private key.
* @return The decrypted vault key
*/
Masterkey decryptMasterkey(ECPrivateKey deviceKey);
/**
* Creates an unlock response object from the user key + vault key.
*
* @param vaultKeyJwe a JWE containing the symmetric vault key, encrypted for this device's user.
* @param userKeyJwe a JWE containing the user's private key, encrypted for this device.
* @return Ciphertext received by Hub, which can be decrypted using this device's private key.
*/
static ReceivedKey vaultKeyAndUserKey(JWEObject vaultKeyJwe, JWEObject userKeyJwe) {
return deviceKey -> {
var userKey = JWEHelper.decryptUserKey(userKeyJwe, deviceKey);
return JWEHelper.decryptVaultKey(vaultKeyJwe, userKey);
};
}
/**
* Creates an unlock response object from the received legacy "access token" JWE.
*
* @param vaultKeyJwe a JWE containing the symmetric vault key, encrypted for this device.
* @return Ciphertext received by Hub, which can be decrypted using this device's private key.
* @deprecated Only for compatibility with Hub 1.0 - 1.2
*/
@Deprecated
static ReceivedKey legacyDeviceKey(JWEObject vaultKeyJwe) {
return deviceKey -> JWEHelper.decryptVaultKey(vaultKeyJwe, deviceKey);
}
}
@@ -1,7 +1,7 @@
package org.cryptomator.ui.keyloading.hub;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.BaseEncoding;
@@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
@@ -31,14 +32,16 @@ import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
@@ -47,7 +50,7 @@ public class RegisterDeviceController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(RegisterDeviceController.class);
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
private final Stage window;
private final HubConfig hubConfig;
@@ -55,26 +58,27 @@ public class RegisterDeviceController implements FxController {
private final Lazy<Scene> registerSuccessScene;
private final Lazy<Scene> registerFailedScene;
private final String deviceId;
private final P384KeyPair keyPair;
private final CompletableFuture<JWEObject> result;
private final DecodedJWT jwt;
private final P384KeyPair deviceKeyPair;
private final CompletableFuture<ReceivedKey> result;
private final HttpClient httpClient;
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
private final BooleanProperty invalidSetupCode = new SimpleBooleanProperty(false);
private final BooleanProperty workInProgress = new SimpleBooleanProperty(false);
public TextField setupCodeField;
public TextField deviceNameField;
public Button registerBtn;
@Inject
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<JWEObject> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
this.window = window;
this.hubConfig = hubConfig;
this.deviceId = deviceId;
this.keyPair = Objects.requireNonNull(deviceKey.get());
this.deviceKeyPair = Objects.requireNonNull(deviceKey.get());
this.result = result;
this.bearerToken = Objects.requireNonNull(bearerToken.get());
this.registerSuccessScene = registerSuccessScene;
this.registerFailedScene = registerFailedScene;
this.jwt = JWT.decode(this.bearerToken);
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
}
@@ -82,6 +86,13 @@ public class RegisterDeviceController implements FxController {
public void initialize() {
deviceNameField.setText(determineHostname());
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
deviceNameField.disableProperty().bind(workInProgress);
setupCodeField.textProperty().addListener(observable -> invalidSetupCode.set(false));
setupCodeField.disableProperty().bind(workInProgress);
var missingSetupCode = setupCodeField.textProperty().isEmpty();
var missingDeviceName = deviceNameField.textProperty().isEmpty();
registerBtn.disableProperty().bind(workInProgress.or(missingSetupCode).or(missingDeviceName));
registerBtn.contentDisplayProperty().bind(Bindings.when(workInProgress).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY));
}
private String determineHostname() {
@@ -95,35 +106,62 @@ public class RegisterDeviceController implements FxController {
@FXML
public void register() {
deviceNameAlreadyExists.set(false);
registerBtn.setContentDisplay(ContentDisplay.LEFT);
registerBtn.setDisable(true);
workInProgress.set(true);
var keyUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
var deviceKey = keyPair.getPublic().getEncoded();
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64Url().omitPadding().encode(deviceKey));
var json = toJson(dto);
var request = HttpRequest.newBuilder(keyUri) //
var apiRootUrl = hubConfig.getApiBaseUrl();
var userReq = HttpRequest.newBuilder(apiRootUrl.resolve("users/me")) //
.GET() //
.timeout(REQ_TIMEOUT) //
.header("Authorization", "Bearer " + bearerToken) //
.header("Content-Type", "application/json").PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
.header("Content-Type", "application/json") //
.build();
httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
httpClient.sendAsync(userReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
.thenApply(response -> {
if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
return response;
if (response.statusCode() == 200) {
var dto = fromJson(response.body());
return Objects.requireNonNull(dto, "null or empty response body");
} else {
throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
}
}).handleAsync((response, throwable) -> {
}).thenApply(user -> {
try {
assert user.privateKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
var userKey = JWEHelper.decryptUserKey(JWEObject.parse(user.privateKey), setupCodeField.getText());
return JWEHelper.encryptUserKey(userKey, deviceKeyPair.getPublic());
} catch (ParseException e) {
throw new RuntimeException("Server answered with unparsable user key", e);
}
}).thenCompose(jwe -> {
var now = Instant.now().toString();
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
var json = toJson(dto);
var deviceUri = apiRootUrl.resolve("devices/" + deviceId);
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
.timeout(REQ_TIMEOUT) //
.header("Authorization", "Bearer " + bearerToken) //
.header("Content-Type", "application/json") //
.build();
return httpClient.sendAsync(putDeviceReq, HttpResponse.BodyHandlers.discarding());
}).whenCompleteAsync((response, throwable) -> {
if (response != null) {
this.handleResponse(response);
} else {
this.registrationFailed(throwable);
this.setupFailed(throwable);
}
return null;
workInProgress.set(false);
}, Platform::runLater);
}
private UserDto fromJson(String json) {
try {
return JSON.reader().readValue(json, UserDto.class);
} catch (IOException e) {
throw new IllegalStateException("Failed to deserialize DTO", e);
}
}
private String toJson(CreateDeviceDto dto) {
try {
return JSON.writer().writeValueAsString(dto);
@@ -132,23 +170,26 @@ public class RegisterDeviceController implements FxController {
}
}
private void handleResponse(HttpResponse<Void> voidHttpResponse) {
assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
if (voidHttpResponse.statusCode() == 409) {
deviceNameAlreadyExists.set(true);
registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
registerBtn.setDisable(false);
} else {
private void handleResponse(HttpResponse<Void> response) {
if (response.statusCode() == 201) {
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
window.setScene(registerSuccessScene.get());
} else if (response.statusCode() == 409) {
deviceNameAlreadyExists.set(true);
} else {
setupFailed(new IllegalStateException("Unexpected http status code " + response.statusCode()));
}
}
private void registrationFailed(Throwable cause) {
LOG.warn("Device registration failed.", cause);
window.setScene(registerFailedScene.get());
result.completeExceptionally(cause);
private void setupFailed(Throwable cause) {
switch (cause) {
case CompletionException e when e.getCause() instanceof JWEHelper.InvalidJweKeyException -> invalidSetupCode.set(true);
default -> {
LOG.warn("Device setup failed.", cause);
window.setScene(registerFailedScene.get());
result.completeExceptionally(cause);
}
}
}
@FXML
@@ -160,13 +201,6 @@ public class RegisterDeviceController implements FxController {
result.cancel(true);
}
/* Getter */
public String getUserName() {
return jwt.getClaim("email").asString();
}
//--- Getters & Setters
public BooleanProperty deviceNameAlreadyExistsProperty() {
@@ -177,5 +211,21 @@ public class RegisterDeviceController implements FxController {
return deviceNameAlreadyExists.get();
}
public BooleanProperty invalidSetupCodeProperty() {
return invalidSetupCode;
}
public boolean isInvalidSetupCode() {
return invalidSetupCode.get();
}
@JsonIgnoreProperties(ignoreUnknown = true)
private record UserDto(String id, String name, String publicKey, String privateKey, String setupCode) {}
private record CreateDeviceDto(@JsonProperty(required = true) String id, //
@JsonProperty(required = true) String name, //
@JsonProperty(required = true) String publicKey, //
@JsonProperty(required = true, defaultValue = "DESKTOP") String type, //
@JsonProperty(required = true) String userPrivateKey, //
@JsonProperty(required = true) String creationTime) {}
}
@@ -12,10 +12,10 @@ import java.util.concurrent.CompletableFuture;
public class RegisterFailedController implements FxController {
private final Stage window;
private final CompletableFuture<JWEObject> result;
private final CompletableFuture<ReceivedKey> result;
@Inject
public RegisterFailedController(@KeyLoading Stage window, CompletableFuture<JWEObject> result) {
public RegisterFailedController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
this.window = window;
this.result = result;
}
@@ -15,10 +15,10 @@ import java.util.concurrent.CompletableFuture;
public class UnauthorizedDeviceController implements FxController {
private final Stage window;
private final CompletableFuture<JWEObject> result;
private final CompletableFuture<ReceivedKey> result;
@Inject
public UnauthorizedDeviceController(@KeyLoading Stage window, CompletableFuture<JWEObject> result) {
public UnauthorizedDeviceController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
this.window = window;
this.result = result;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
@@ -26,6 +26,7 @@ public interface MainWindowComponent {
default Stage showMainWindow() {
Stage stage = window();
stage.setScene(scene().get());
stage.setIconified(false);
stage.show();
stage.toFront();
stage.requestFocus();
@@ -16,6 +16,7 @@ import javafx.stage.Stage;
public class MainWindowSceneFactory extends DefaultSceneFactory {
protected static final KeyCodeCombination SHORTCUT_N = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN);
protected static final KeyCodeCombination SHORTCUT_O = new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN);
private final Lazy<MainWindowTitleController> mainWindowTitleController;
private final Lazy<VaultListController> vaultListController;
@@ -34,6 +35,7 @@ public class MainWindowSceneFactory extends DefaultSceneFactory {
} else {
scene.getAccelerators().put(SHORTCUT_W, mainWindowTitleController.get()::close);
}
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddVault);
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddNewVault);
scene.getAccelerators().put(SHORTCUT_O, vaultListController.get()::didClickAddExistingVault);
}
}
@@ -6,15 +6,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
@MainWindow
public class ResizeController implements FxController {
@@ -53,48 +52,57 @@ public class ResizeController implements FxController {
public void initialize() {
LOG.trace("init ResizeController");
if (neverTouched()) {
settings.displayConfiguration.set(getMonitorSizes());
return;
} else {
if (didDisplayConfigurationChange()) {
//If the position is illegal, then the window appears on the main screen in the middle of the window.
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
window.setWidth(window.getMinWidth());
window.setHeight(window.getMinHeight());
} else {
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
window.setX(settings.windowXPosition.get());
window.setY(settings.windowYPosition.get());
}
if (!neverTouched()) {
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
window.setX(settings.windowXPosition.get());
window.setY(settings.windowYPosition.get());
}
savePositionalSettings();
window.setOnShowing(this::checkDisplayBounds);
}
private boolean neverTouched() {
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
}
private boolean didDisplayConfigurationChange() {
String currentDisplayConfiguration = getMonitorSizes();
String settingsDisplayConfiguration = settings.displayConfiguration.get();
boolean configurationHasChanged = !settingsDisplayConfiguration.equals(currentDisplayConfiguration);
if (configurationHasChanged) settings.displayConfiguration.set(currentDisplayConfiguration);
return configurationHasChanged;
private void checkDisplayBounds(WindowEvent evt) {
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
// If the position is (-32000, -32000), restore to the last saved position
if (window.getX() == -32000 && window.getY() == -32000) {
window.setX(settings.windowXPosition.get());
window.setY(settings.windowYPosition.get());
window.setWidth(settings.windowWidth.get());
window.setHeight(settings.windowHeight.get());
}
if (isOutOfDisplayBounds()) {
// If the position is illegal, then the window appears on the main screen in the middle of the window.
LOG.debug("Resetting window position due to insufficient screen overlap");
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
window.setWidth(window.getMinWidth());
window.setHeight(window.getMinHeight());
savePositionalSettings();
}
}
private String getMonitorSizes() {
ObservableList<Screen> screens = Screen.getScreens();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < screens.size(); i++) {
Rectangle2D screenBounds = screens.get(i).getBounds();
if (!sb.isEmpty()) sb.append(" ");
sb.append("displayId: " + i + ", " + screenBounds.getWidth() + "x" + screenBounds.getHeight() + ";");
}
return sb.toString();
private boolean isOutOfDisplayBounds() {
// define a rect which is inset on all sides from the window's rect:
final double x = window.getX() + 20; // 20px left
final double y = window.getY() + 5; // 5px top
final double w = window.getWidth() - 40; // 20px left + 20px right
final double h = window.getHeight() - 25; // 5px top + 20px bottom
return isRectangleOutOfScreen(x, y, 0, h) // Left pixel column
|| isRectangleOutOfScreen(x + w, y, 0, h) // Right pixel column
|| isRectangleOutOfScreen(x, y, w, 0) // Top pixel row
|| isRectangleOutOfScreen(x, y + h, w, 0); // Bottom pixel row
}
private boolean isRectangleOutOfScreen(double x, double y, double width, double height) {
return Screen.getScreensForRectangle(x, y, width, height).isEmpty();
}
private void startResize(MouseEvent evt) {
@@ -183,5 +191,4 @@ public class ResizeController implements FxController {
public boolean isShowResizingArrows() {
return showResizingArrows.get();
}
}
@@ -21,6 +21,9 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Side;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListView;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.DragEvent;
@@ -34,6 +37,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
@@ -59,12 +63,23 @@ public class VaultListController implements FxController {
private final RemoveVaultComponent.Builder removeVaultDialogue;
private final VaultListManager vaultListManager;
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
private final ResourceBundle resourceBundle;
public ListView<Vault> vaultList;
public StackPane root;
public Button addVaultBtn;
@FXML
private ContextMenu addVaultContextMenu;
@Inject
VaultListController(@MainWindow Stage mainWindow, ObservableList<Vault> vaults, ObjectProperty<Vault> selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVaultDialogue, VaultListManager vaultListManager) {
VaultListController(@MainWindow Stage mainWindow, //
ObservableList<Vault> vaults, //
ObjectProperty<Vault> selectedVault, //
VaultListCellFactory cellFactory, //
AddVaultWizardComponent.Builder addVaultWizard, //
RemoveVaultComponent.Builder removeVaultDialogue, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle) {
this.mainWindow = mainWindow;
this.vaults = vaults;
this.selectedVault = selectedVault;
@@ -72,6 +87,7 @@ public class VaultListController implements FxController {
this.addVaultWizard = addVaultWizard;
this.removeVaultDialogue = removeVaultDialogue;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.emptyVaultList = Bindings.isEmpty(vaults);
@@ -129,6 +145,15 @@ public class VaultListController implements FxController {
root.setOnDragExited(this::handleDragEvent);
}
@FXML
private void toggleMenu() {
if (addVaultContextMenu.isShowing()) {
addVaultContextMenu.hide();
} else {
addVaultContextMenu.show(addVaultBtn, Side.BOTTOM, 0.0, 0.0);
}
}
private void deselect(MouseEvent released) {
if (released.getY() > (vaultList.getItems().size() * vaultList.fixedCellSizeProperty().get())) {
vaultList.getSelectionModel().clearSelection();
@@ -144,8 +169,13 @@ public class VaultListController implements FxController {
}
@FXML
public void didClickAddVault() {
addVaultWizard.build().showAddVaultWizard();
public void didClickAddNewVault() {
addVaultWizard.build().showAddNewVaultWizard(resourceBundle);
}
@FXML
public void didClickAddExistingVault() {
addVaultWizard.build().showAddExistingVaultWizard(resourceBundle);
}
private void pressedShortcutToRemoveVault() {
@@ -1,5 +1,6 @@
package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.slf4j.Logger;
@@ -19,6 +20,7 @@ public class PreferencesController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(PreferencesController.class);
private final Environment env;
private final Stage window;
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
private final BooleanBinding updateAvailable;
@@ -31,7 +33,8 @@ public class PreferencesController implements FxController {
public Tab aboutTab;
@Inject
public PreferencesController(@PreferencesWindow Stage window, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, UpdateChecker updateChecker) {
public PreferencesController(Environment env, @PreferencesWindow Stage window, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, UpdateChecker updateChecker) {
this.env = env;
this.window = window;
this.selectedTabProperty = selectedTabProperty;
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
@@ -42,6 +45,9 @@ public class PreferencesController implements FxController {
window.setOnShowing(this::windowWillAppear);
selectedTabProperty.addListener(observable -> this.selectChosenTab());
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
if (env.disableUpdateCheck()) {
tabPane.getTabs().remove(updatesTab);
}
}
private void selectChosenTab() {
@@ -8,6 +8,7 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -58,7 +59,7 @@ public class RecoveryKeyFactory {
}
}
// visible for testing
@VisibleForTesting
String createRecoveryKey(byte[] rawKey) {
Preconditions.checkArgument(rawKey.length == 64, "key should be 64 bytes");
byte[] paddedKey = Arrays.copyOf(rawKey, 66);
+10
View File
@@ -795,6 +795,16 @@
-fx-scale-shape: false;
}
/*******************************************************************************
* *
* Add Vault - MenuItem *
* *
******************************************************************************/
.add-vault-menu-item {
-fx-padding: 4px 8px;
}
/*******************************************************************************
* *
* ProgressBar *
+10
View File
@@ -794,6 +794,16 @@
-fx-scale-shape: false;
}
/*******************************************************************************
* *
* Add Vault - MenuItem *
* *
******************************************************************************/
.add-vault-menu-item {
-fx-padding: 4px 8px;
}
/*******************************************************************************
* *
* ProgressBar *
@@ -24,9 +24,8 @@
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
<buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>
<Button fx:id="finishButton" text="%addvaultwizard.existing.chooseBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#chooseFileAndNext" defaultButton="true"/>
</buttons>
</ButtonBar>
@@ -68,9 +68,8 @@
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="B+X">
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
<buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back"/>
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" onAction="#next" defaultButton="true" disable="${!controller.validVaultName}"/>
</buttons>
</ButtonBar>
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.addvaultwizard.AddVaultWelcomeController"
prefWidth="450"
prefHeight="450"
spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="24"/>
</padding>
<children>
<Region VBox.vgrow="ALWAYS"/>
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" smooth="true" cache="true">
<Image url="@../img/logo.png"/>
</ImageView>
<Region VBox.vgrow="ALWAYS"/>
<VBox alignment="CENTER" spacing="9">
<Button styleClass="button-large" text="%addvaultwizard.welcome.newButton" onAction="#createNewVault" prefWidth="Infinity">
<graphic>
<FontAwesome5IconView glyph="MAGIC" glyphSize="15"/>
</graphic>
</Button>
<Button styleClass="button-large" text="%addvaultwizard.welcome.existingButton" onAction="#chooseExistingVault" prefWidth="Infinity">
<graphic>
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
</graphic>
</Button>
</VBox>
<Region VBox.vgrow="ALWAYS"/>
</children>
</VBox>
@@ -15,7 +15,7 @@
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterDeviceController"
fx:controller="org.cryptomator.ui.keyloading.hub.LegacyRegisterDeviceController"
minWidth="400"
maxWidth="400"
minHeight="145"
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterDeviceController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="INFO" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%hub.register.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%hub.register.description" wrapText="true"/>
<HBox spacing="6" alignment="CENTER_LEFT">
<padding>
<Insets top="12"/>
</padding>
<Label text="%hub.register.setupCodeLabel" labelFor="$setupCodeField"/>
<TextField fx:id="setupCodeField" HBox.hgrow="ALWAYS"/>
</HBox>
<HBox spacing="6" alignment="CENTER_LEFT">
<padding>
<Insets top="12"/>
</padding>
<Label text="%hub.register.nameLabel" labelFor="$deviceNameField"/>
<TextField fx:id="deviceNameField" HBox.hgrow="ALWAYS"/>
</HBox>
<HBox alignment="TOP_RIGHT">
<Label text="%hub.register.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" managed="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
<padding>
<Insets top="6"/>
</padding>
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
<Label text="%hub.register.invalidSetupCode" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.invalidSetupCode}" managed="${controller.invalidSetupCode}" graphicTextGap="6">
<padding>
<Insets top="6"/>
</padding>
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
</HBox>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CU">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="registerBtn" text="%hub.register.registerBtn" ButtonBar.buttonData="OTHER" defaultButton="true" onAction="#register" contentDisplay="TEXT_ONLY" >
<graphic>
<FontAwesome5Spinner glyphSize="12" />
</graphic>
</Button>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>
+20 -2
View File
@@ -8,6 +8,8 @@
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Arc?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.MenuItem?>
<StackPane xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:id="root"
@@ -26,11 +28,27 @@
<Arc VBox.vgrow="NEVER" styleClass="onboarding-overlay-arc" type="OPEN" centerX="50" centerY="0" radiusY="100" radiusX="50" startAngle="0" length="-60" strokeWidth="1"/>
</VBox>
</StackPane>
<Button styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" onAction="#didClickAddVault" alignment="BASELINE_CENTER" maxWidth="Infinity">
<Button fx:id="addVaultBtn" onAction="#toggleMenu" styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" alignment="BASELINE_CENTER" maxWidth="Infinity" contentDisplay="RIGHT">
<graphic>
<FontAwesome5IconView glyph="PLUS"/>
<FontAwesome5IconView glyph="CARET_DOWN"/>
</graphic>
</Button>
<fx:define>
<ContextMenu fx:id="addVaultContextMenu">
<items>
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault" >
<graphic>
<FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14" />
</graphic>
</MenuItem>
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault" >
<graphic>
<FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14" />
</graphic>
</MenuItem>
</items>
</ContextMenu>
</fx:define>
</VBox>
<Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/>
</StackPane>
+10 -7
View File
@@ -41,10 +41,8 @@ traymenu.vault.reveal=Reveal
# Add Vault Wizard
addvaultwizard.title=Add Vault
## Welcome
addvaultwizard.welcome.newButton=Create New Vault
addvaultwizard.welcome.existingButton=Open Existing Vault
## New
addvaultwizard.new.title=Add New Vault
### Name
addvaultwizard.new.nameInstruction=Choose a name for the vault
addvaultwizard.new.namePrompt=Vault Name
@@ -94,6 +92,7 @@ addvault.new.readme.accessLocation.2=This is your vault's access location.
addvault.new.readme.accessLocation.3=Any files added to this volume will be encrypted by Cryptomator. You can work on it like on any other drive/folder. This is only a decrypted view of its content, your files stay encrypted on your hard drive all the time.
addvault.new.readme.accessLocation.4=Feel free to remove this file.
## Existing
addvaultwizard.existing.title=Add Existing Vault
addvaultwizard.existing.instruction=Choose the "vault.cryptomator" file of your existing vault. If only a file named "masterkey.cryptomator" exists, select that instead.
addvaultwizard.existing.chooseBtn=Choose…
addvaultwizard.existing.filePickerTitle=Select Vault File
@@ -139,7 +138,7 @@ unlock.error.customPath.message=Unable to mount vault to custom path
unlock.error.customPath.description.notSupported=If you wish to keep using the custom path, please go to the preferences and select a volume type that supports it. Otherwise, go to the vault options and choose a supported mount point.
unlock.error.customPath.description.notExists=The custom mount path does not exist. Either create it in your local filesystem or change it in the vault options.
unlock.error.customPath.description.inUse=The drive letter or custom mount path "%s" is already in use.
unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%3$s" used for unlock could not be removed. Please check the file and then delete it manually.
unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%3$s" used for unlocking could not be removed. Please check the file and then delete it manually.
unlock.error.customPath.description.couldNotBeCleaned=Your vault could not be mounted to the path "%s". Please try again or choose a different path.
unlock.error.customPath.description.notEmptyDir=The custom mount path "%s" is not an empty folder. Please choose an empty folder and try again.
unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %2$s
@@ -155,9 +154,11 @@ hub.auth.loginLink=Not redirected? Click here to open it.
hub.receive.message=Processing response…
hub.receive.description=Cryptomator is receiving and processing the response from Hub. Please wait.
### Register Device
hub.register.message=Device name required
hub.register.description=This seems to be the first Hub access from this device. In order to identify it for access authorization, you need to name this device.
hub.register.message=New Device
hub.register.description=This is the first Hub access from this device. Please authorize it using your setup code.
hub.register.nameLabel=Device Name
hub.register.setupCodeLabel=Setup Code
hub.register.invalidSetupCode=Invalid Setup Code
hub.register.occupiedMsg=Name already in use
hub.register.registerBtn=Confirm
### Registration Success
@@ -367,7 +368,9 @@ main.vaultlist.contextMenu.unlock=Unlock…
main.vaultlist.contextMenu.unlockNow=Unlock Now
main.vaultlist.contextMenu.vaultoptions=Show Vault Options
main.vaultlist.contextMenu.reveal=Reveal Drive
main.vaultlist.addVaultBtn=Add Vault
main.vaultlist.addVaultBtn=Add
main.vaultlist.addVaultBtn.menuItemNew=New Vault...
main.vaultlist.addVaultBtn.menuItemExisting=Existing Vault...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Thanks for choosing Cryptomator to protect your files. If you need any assistance, check out our getting started guides:
+38 -9
View File
@@ -40,10 +40,8 @@ traymenu.vault.reveal=إظهار
# Add Vault Wizard
addvaultwizard.title=أضِف مخزنًا
## Welcome
addvaultwizard.welcome.newButton=إنشاء مخزن جديد
addvaultwizard.welcome.existingButton=افتح مخزن موجود
## New
addvaultwizard.new.title=إضافة مخزن جديد
### Name
addvaultwizard.new.nameInstruction=اختر اسم للمخزن
addvaultwizard.new.namePrompt=اسم الخزينة
@@ -88,11 +86,12 @@ addvault.new.readme.storageLocation.8=2. فتح المخزن باستخدام Cr
addvault.new.readme.storageLocation.9=3. افتح موقع الوصول بالنقر على زر "إظهار".
addvault.new.readme.storageLocation.10=إذا كنت بحاجة إلى المساعدة، قم بالاطلاع على كتيب الوثائق: %s
addvault.new.readme.accessLocation.fileName=مرحبا.rtf
addvault.new.readme.accessLocation.1=🔐️ المجلدات المشفرة 🔐️
addvault.new.readme.accessLocation.1=🔐️ وحدة التخزين مشفرة 🔐️
addvault.new.readme.accessLocation.2=هذا هو موقع الوصول للخرنة الخاص بك.
addvault.new.readme.accessLocation.3=سيتم تشفير أي ملفات تضاف إلى هذا المجلد من قبل Cryptomator. يمكنك العمل عليه كأي مجلد آخر. هذه فقط طريقة عرض غير مشفرة لمحتوياتها، ملفاتك تبقى مشفرة على القرص الصلب الخاص بك طوال الوقت.
addvault.new.readme.accessLocation.4=لا تتردد في إزالة هذا الملف.
## Existing
addvaultwizard.existing.title=أضِف مخزن موجود
addvaultwizard.existing.instruction=اختر ملف "vault.cryptomator" من خزينتك الحالية. إذا كان يوجد فقط ملف اسمه "masterkey.cryptomator"، اختره عوضاً عنه.
addvaultwizard.existing.chooseBtn=اختر…
addvaultwizard.existing.filePickerTitle=حدد مِلَفّ المخزن
@@ -135,10 +134,10 @@ unlock.success.rememberChoice=تذكر اختياري ولا تظهر هذا م
unlock.success.revealBtn=اظهار القرص
## Failure
unlock.error.customPath.message=غير قادر على تركيب المخزن إلى المسار المخصص
unlock.error.customPath.description.notSupported=إذا كنت ترغب في الاستمرار في استخدام المسار المخصص، يرجى الذَّهاب إلى التفضيلات وتحديد نوع واسطة التخزين الذي يدعمها. خلاف ذلك، انتقل إلى خيارات المخزن واختر نقطة تركيب مدعومة.
unlock.error.customPath.description.notSupported=إذا كنت ترغب في الاستمرار في استخدام المسار المخصص، يرجى الذَّهاب إلى التفضيلات وتحديد نوع وحدة التخزين\n الذي يدعمها. خلاف ذلك، انتقل إلى خيارات المخزن واختر نقطة تركيب مدعومة.
unlock.error.customPath.description.notExists=مسار التركيب المخصص غير موجود. قم بإنشائه في نظام الملفات المحلي الخاص بك أو اختر غيره في خيارات الخزانة.
unlock.error.customPath.description.inUse=حرف القرص أو مسار التركيب المخصص "%s" قيد الاستخدام بالفعل.
unlock.error.customPath.description.hideawayNotDir=لا يمكن إزالة الملف المؤقت المخفي "%3$s" المستخدم للفتح. الرجاء التحقق من الملف ثم حذفه يدوياً.
unlock.error.customPath.description.hideawayNotDir=لا يمكن إزالة المِلَفّ المؤقت المخفي "%3$s" المستخدم لفتح القُفْل. الرجاء التحقق من المِلَفّ ثم حذفه يدوياً.
unlock.error.customPath.description.couldNotBeCleaned=لا يمكن تركيب خزانتك على المسار "%s". الرجاء المحاولة مرة أخرى أو اختيار مسار مختلف.
unlock.error.customPath.description.notEmptyDir=مسار التركيب المخصص "%s" ليس مجلد فارغ. الرجاء اختيار مجلد فارغ وحاول مرة أخرى.
unlock.error.customPath.description.generic=لقد اخترت مسار تركيب مخصص لهذه الخزانة، ولكن استخدامه فشل مع الرسالة: %2$s
@@ -249,22 +248,52 @@ health.result.severityFilter.info=معلومات
health.result.severityFilter.warn=تحذير
health.result.severityFilter.crit=حرج
health.result.severityTip.good=الخطورة: جيد\nبنية المخزن عادية.
health.result.severityTip.info=الخطورة: معلومات\nبنية المخزن سليمة، إصلاح المقترح.
health.result.severityTip.warn=الخطورة: تحذير\nبنية المخزن تالفة، ننصح لإصلاح.
health.result.severityTip.crit=الخطورة: حرجة\nبنية المخزن تالفة، وتم تحديد فقدان البيانات.
health.result.fixStateFilter.all=حالة لإصلاح - الكل
health.result.fixStateFilter.fixable=قابلة للإصلاح
health.result.fixStateFilter.notFixable=غير قابل للإصلاح
health.result.fixStateFilter.fixing=يتم الإصلاح…
health.result.fixStateFilter.fixed=تم الإصلاح
health.result.fixStateFilter.fixFailed=فشل الإصلاح
## Fix Application
health.fix.fixBtn=إصلاح
health.fix.successTip=تم الإصلاح بنجاح
health.fix.failTip=فشل الإصلاح، راجع السجل للحصول على التفاصيل
# Preferences
preferences.title=تفضيلات
## General
preferences.general=عام
preferences.general.startHidden=إخفاء النافذة عند بدء تشغيل Cryptomator
preferences.general.autoCloseVaults=اقفل الخزانات المفتوحة تلقائياً عند الإقلاع عن التطبيق
preferences.general.debugLogging=تمكين سجلات التصحيح
preferences.general.debugDirectory=عرض ملفات السجل
preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام
preferences.general.keychainBackend=تخزين كلمات المرور مع
## Interface
preferences.interface=الواجهة
preferences.interface.theme=الشكل والمظهر
preferences.interface.theme.automatic=تلقائي
preferences.interface.theme.dark=مظلم (أسود)
preferences.interface.theme.light=فاتح (أبيض)
preferences.interface.unlockThemes=تفعيل الوضع الداكن
preferences.interface.language=اللغة (يتطلب إعادة التشغيل)
preferences.interface.language.auto=النظام الافتراضي
preferences.interface.interfaceOrientation=اتجاه الواجهة
preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين
preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار
preferences.interface.showMinimizeButton=إظهار زر التصغير
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
## Volume
preferences.volume=القرص الإفتراضي
preferences.volume.type=‮نوع وحدة التخزين
preferences.volume.type.automatic=تلقائي
preferences.volume.docsTooltip=افتح الوثائق لمعرفة المزيد عن مختلف أنواع وحدة التخزين.
preferences.volume.fuseRestartRequired=لتطبيق التغييرات، يحتاج Cryptomator إلى إعادة التشغيل.
preferences.volume.tcp.port=منفذ TCP
preferences.volume.supportedFeatures=يدعم نوع وحدة تخزين المختار الميزات التالية:
## Updates
preferences.updates=تحديثات
preferences.updates.currentVersion=الإصدار الحالي: %s
@@ -310,7 +339,7 @@ main.vaultlist.contextMenu.unlock=فتح…
main.vaultlist.contextMenu.unlockNow=افتح الان
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
main.vaultlist.contextMenu.reveal=اظهار القرص
main.vaultlist.addVaultBtn=أضِف مخزنًا
main.vaultlist.addVaultBtn=إضافة
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام:
@@ -350,8 +379,8 @@ wrongFileAlert.message=هل حاولت تشفير هذه الملفات؟
wrongFileAlert.description=لهذا الغرض، بوفر Cryptomator مجلداً في مدير ملفات النظام الخاص بك.
wrongFileAlert.instruction.0=لتشفير الملفات، قم باتباع الخطوات التالية:
wrongFileAlert.instruction.1=1. فتح المخزن الخاص بك.
wrongFileAlert.instruction.2=2. انقر على "إظهار" لفتح المجلد في مدير الملفات الخاص بك.
wrongFileAlert.instruction.3=3. أضف ملفاتك إلى هذا المجلد.
wrongFileAlert.instruction.2=2. انقر على "إظهار" لفتح وحدة التخزين في مدير ملفاتك.
wrongFileAlert.instruction.3=3. أضِف ملفاتك إلى هذه وحدة التخزين.
wrongFileAlert.link=لمزيد من المساعدة، قم بزيارة
# Vault Options
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Паказаць
# Add Vault Wizard
addvaultwizard.title=Дадаць скарбніцу
## Welcome
addvaultwizard.welcome.newButton=Стварыць новую скарбніцу
addvaultwizard.welcome.existingButton=Адчыніць існуючую скарбніцу
## New
### Name
addvaultwizard.new.nameInstruction=Абраць назву для скарбніцы
@@ -353,7 +350,7 @@ main.vaultlist.contextMenu.unlock=Адамкнуць…
main.vaultlist.contextMenu.unlockNow=Разамкнуць зараз
main.vaultlist.contextMenu.vaultoptions=Паказаць параметры скарбніцы
main.vaultlist.contextMenu.reveal=Паказаць дыск
main.vaultlist.addVaultBtn=Дадаць скарбніцу
main.vaultlist.addVaultBtn=Дадаць
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Дзякуй, што ты абраў Cryptomator для абароны тваіх файлаў. Калі табе патрэбна дапамога, калі ласка, паглядзі нашы інструкцыі:
@@ -35,9 +35,6 @@ traymenu.vault.reveal=Разкриване
# Add Vault Wizard
addvaultwizard.title=Добавяне на хранилище
## Welcome
addvaultwizard.welcome.newButton=Ново хранилище
addvaultwizard.welcome.existingButton=Отваряне на хранилище
## New
### Name
addvaultwizard.new.nameInstruction=Изберете име на хранилището
@@ -156,6 +153,7 @@ hub.invalidLicense.message=Лиценза за Hub е недействителе
# Lock
## Force
lock.forced.retryBtn=Повторен опит
## Failure
# Migration
@@ -216,7 +214,6 @@ main.vaultlist.contextMenu.unlock=Отключване…
main.vaultlist.contextMenu.unlockNow=Отключване сега
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
main.vaultlist.contextMenu.reveal=Разкриване на диска
main.vaultlist.addVaultBtn=Добавяне на хранилище
## Vault Detail
### Welcome
### Locked
@@ -33,9 +33,6 @@ traymenu.vault.lock=লক করুন
# Add Vault Wizard
addvaultwizard.title=ভোল্ট যুক্ত করুন
## Welcome
addvaultwizard.welcome.newButton=নতুন ভোল্ট তৈরি করুন
addvaultwizard.welcome.existingButton=বিদ্যমান কোনো ভোল্ট খুলুন
## New
### Name
addvaultwizard.new.nameInstruction=ভোল্ট এর একটি নাম দেন
@@ -139,7 +136,6 @@ lock.forced.retryBtn=পুনরায় চেষ্টা করুন
main.closeBtn.tooltip=বন্ধ করুন
## Vault List
main.vaultlist.contextMenu.lock=লক করুন
main.vaultlist.addVaultBtn=ভোল্ট যুক্ত করুন
## Vault Detail
### Welcome
### Locked
@@ -31,9 +31,6 @@ 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
@@ -219,7 +216,6 @@ main.vaultlist.contextMenu.unlock=Otključaj…
main.vaultlist.contextMenu.unlockNow=Otključaj sada
main.vaultlist.contextMenu.vaultoptions=Pokaži opcije sefa
main.vaultlist.contextMenu.reveal=Otkrij pogon
main.vaultlist.addVaultBtn=Dodaj sef
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Hvala što ste izabrali Cryptomator za zaštitu podataka. Ako vam je potrebna pomoć, pogledajte naše vodiče za početak:
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Mostra
# Add Vault Wizard
addvaultwizard.title=Afegir una caixa forta
## Welcome
addvaultwizard.welcome.newButton=Crea una caixa forta nova
addvaultwizard.welcome.existingButton=Obri una caixa forta existent
## New
### Name
addvaultwizard.new.nameInstruction=Introduiu el nom de la caixa forta
@@ -351,7 +348,7 @@ main.vaultlist.contextMenu.unlock=Desbloca…
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
main.vaultlist.contextMenu.reveal=Mostra la unitat
main.vaultlist.addVaultBtn=Afegir una caixa forta
main.vaultlist.addVaultBtn=Afegir
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Gràcies per escollir Cryptomator per protegir els vostres fitxers. Si vos cal ajuda, llegiu les nostres guies per donar els Primers passos:
@@ -38,9 +38,6 @@ traymenu.vault.reveal=Zobrazit
# Add Vault Wizard
addvaultwizard.title=Přidat trezor
## Welcome
addvaultwizard.welcome.newButton=Vytvořit nový trezor
addvaultwizard.welcome.existingButton=Otevřít existující trezor
## New
### Name
addvaultwizard.new.nameInstruction=Zvolte jméno trezoru
@@ -341,7 +338,7 @@ main.vaultlist.contextMenu.unlock=Odemknout…
main.vaultlist.contextMenu.unlockNow=Odemknout nyní
main.vaultlist.contextMenu.vaultoptions=Zobrazit možnosti trezoru
main.vaultlist.contextMenu.reveal=Zobrazit jednotku
main.vaultlist.addVaultBtn=Přidat trezor
main.vaultlist.addVaultBtn=Přidat
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Děkujeme, že jste si vybrali Cryptomator pro ochranu vašich souborů. Pokud potřebujete pomoc, podívejte se na naše návody:
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Vis
# Add Vault Wizard
addvaultwizard.title=Tilføj boks
## Welcome
addvaultwizard.welcome.newButton=Opret ny boks
addvaultwizard.welcome.existingButton=Open eksisterende boks
## New
### Name
addvaultwizard.new.nameInstruction=Vælg et navn til boksen
@@ -358,7 +355,7 @@ main.vaultlist.contextMenu.unlock=Lås op…
main.vaultlist.contextMenu.unlockNow=Lås op nu
main.vaultlist.contextMenu.vaultoptions=Vis boksindstillinger
main.vaultlist.contextMenu.reveal=Vis drev
main.vaultlist.addVaultBtn=Tilføj boks
main.vaultlist.addVaultBtn=Tilføj
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Anzeigen
# Add Vault Wizard
addvaultwizard.title=Tresor hinzufügen
## Welcome
addvaultwizard.welcome.newButton=Neuen Tresor erstellen
addvaultwizard.welcome.existingButton=Bestehenden Tresor öffnen
## New
addvaultwizard.new.title=Neuen Tresor hinzufügen
### Name
addvaultwizard.new.nameInstruction=Wähle einen Namen für den Tresor
addvaultwizard.new.namePrompt=Tresorname
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Dies ist der Zugangsort deines Tresors.
addvault.new.readme.accessLocation.3=Alle zu diesem Laufwerk hinzugefügten Dateien werden von Cryptomator verschlüsselt. Du kannst mit diesem arbeiten wie mit jedem anderen Laufwerk bzw. Ordner. Dies ist lediglich eine unverschlüsselte Ansicht des Laufwerkinhalts; auf deiner Festplatte bleiben deine Dateien weiterhin verschlüsselt.
addvault.new.readme.accessLocation.4=Du kannst diese Datei löschen.
## Existing
addvaultwizard.existing.title=Bestehenden Tresor hinzufügen
addvaultwizard.existing.instruction=Wähle die Datei „vault.cryptomator“ deines bestehenden Tresors aus. Falls nur eine Datei mit der Bezeichnung „masterkey.cryptomator“ vorhanden ist, nutze stattdessen diese.
addvaultwizard.existing.chooseBtn=Durchsuchen …
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Tresor kann nicht in benutzerdefinierten Pfad ei
unlock.error.customPath.description.notSupported=Wenn du weiterhin den benutzerdefinierten Pfad verwenden möchtest, öffne die allgemeinen Einstellungen und wähle einen unterstützten Laufwerkstyp. Andernfalls gehe zu den Tresor-Optionen und wähle einen unterstützten Einhängepunkt.
unlock.error.customPath.description.notExists=Der benutzerdefinierte Einhängepunkt existiert nicht. Erstelle ihn in deinem lokalen Dateisystem oder ändere ihn in den Tresor-Optionen.
unlock.error.customPath.description.inUse=Der Laufwerksbuchstabe oder benutzerdefinierte Einhängepunkt „%s“ wird bereits verwendet.
unlock.error.customPath.description.hideawayNotDir=Die temporäre, versteckte Datei "%3$s", die für das Entsperren verwendet wurde, konnte nicht entfernt werden. Bitte überprüfe die Datei und lösche sie manuell.
unlock.error.customPath.description.hideawayNotDir=Die temporäre, versteckte Datei „%3$s“, die für das Entsperren verwendet wurde, konnte nicht entfernt werden. Bitte überprüfe die Datei und lösche sie manuell.
unlock.error.customPath.description.couldNotBeCleaned=Dein Tresor konnte nicht in den Pfad „%s“ eingehängt werden. Bitte versuche es erneut oder wähle einen anderen Pfad aus.
unlock.error.customPath.description.notEmptyDir=Der benutzerdefinierte Einhängepunkt "%s" ist kein leerer Ordner. Bitte wähle einen leeren Ordner und versuche es erneut.
unlock.error.customPath.description.generic=Du hast für diesen Tresor einen benutzerdefinierten Einhängepunkt ausgewählt, aber dessen Verwendung ist mit folgender Meldung fehlgeschlagen: %2$s
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Entsperren …
main.vaultlist.contextMenu.unlockNow=Jetzt entsperren
main.vaultlist.contextMenu.vaultoptions=Tresoroptionen anzeigen
main.vaultlist.contextMenu.reveal=Laufwerk anzeigen
main.vaultlist.addVaultBtn=Tresor hinzufügen
main.vaultlist.addVaultBtn=Hinzufügen
main.vaultlist.addVaultBtn.menuItemNew=Neuer Tresor …
main.vaultlist.addVaultBtn.menuItemExisting=Bestehender Tresor …
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Danke, dass du zum Schutz deiner Dateien Cryptomator gewählt hast. Falls du Hilfe brauchst, schau dir unsere Anleitungen an:
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Αποκάλυψη
# Add Vault Wizard
addvaultwizard.title=Προσθήκη Κρύπτης
## Welcome
addvaultwizard.welcome.newButton=Δημιουργία Νέας Κρύπτης
addvaultwizard.welcome.existingButton=Άνοιγμα Υπάρχοντος Κρύπτης
## New
addvaultwizard.new.title=Προσθήκη Νέας Κρύπτης
### Name
addvaultwizard.new.nameInstruction=Διαλέξτε ένα όνομα για την κρύπτη
addvaultwizard.new.namePrompt=Όνομα Κρύπτης
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Αυτή είναι η τοποθεσία
addvault.new.readme.accessLocation.3=Κάθε αρχείο που θα προστεθεί σε αυτό τον τόμο θα κωδικοποιηθεί από το Cryptomator. Μπορείτε να το επεξεργαστείτε όπως θα κάνατε σε κάθε δίσκο/φάκελο. Αυτή είναι μόνο για αποκρυπτογραφημένη μορφή των περιεχομένων του, τα αρχεία σας μένουν κωδικοποιημένα στον σκληρό σας δίσκο συνέχεια.
addvault.new.readme.accessLocation.4=Μπορείτε ελεύθερα να αφαιρέσετε το αρχείο.
## Existing
addvaultwizard.existing.title=Προσθήκη Υπάρχοντος Κρύπτης
addvaultwizard.existing.instruction=Επιλέξτε το αρχείο "vault.cryptomator" της υπάρχοντος κρύπτης. Αν υπάρχει μόνο ένα αρχείο με όνομα "masterkey.cryptomator", επιλέξτε αυτό.
addvaultwizard.existing.chooseBtn=Επιλογή…
addvaultwizard.existing.filePickerTitle=Επιλέξτε Αρχείο Κρύπτης
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Αδυναμία προσάρτησης της
unlock.error.customPath.description.notSupported=Εάν θέλετε να συνεχίσετε να χρησιμοποιείτε την προσαρμοσμένη διαδρομή, μεταβείτε στις προτιμήσεις και επιλέξτε έναν τύπο τόμου που την υποστηρίζει. Διαφορετικά, μεταβείτε στις επιλογές της κρύπτης και επιλέξτε ένα υποστηριζόμενο σημείο προσάρτησης.
unlock.error.customPath.description.notExists=Η προσαρμοσμένη διαδρομή προσάρτησης δεν υπάρχει. Είτε δημιουργήστε την στο τοπικό σύστημα αρχείων σας είτε αλλάξτε την στις επιλογές κρύπτης.
unlock.error.customPath.description.inUse=Το γράμμα μονάδας δίσκου ή προσαρμοσμένη διαδρομή προσάρτησης "%s" είναι ήδη σε χρήση.
unlock.error.customPath.description.hideawayNotDir=Το προσωρινό, κρυφό αρχείο "%3$s" που χρησιμοποιείται για το ξεκλείδωμα δεν μπορεί να αφαιρεθεί. Παρακαλώ ελέγξτε το αρχείο και στη συνέχεια διαγράψτε το χειροκίνητα.
unlock.error.customPath.description.hideawayNotDir=Το προσωρινό, κρυφό αρχείο "%3$s" που χρησιμοποιείται για το ξεκλείδωμα δεν μπορεί να αφαιρεθεί. Παρακαλούμε ελέγξτε το αρχείο και στη συνέχεια διαγράψτε το χειροκίνητα.
unlock.error.customPath.description.couldNotBeCleaned=Η κρύπτη σας δεν μπορεί να τοποθετηθεί στη διαδρομή "%s". Παρακαλώ δοκιμάστε ξανά ή επιλέξτε διαφορετική διαδρομή.
unlock.error.customPath.description.notEmptyDir=Η προσαρμοσμένη διαδρομή προσάρτησης "%s" δεν είναι ένας άδειος φάκελος. Παρακαλώ επιλέξτε έναν άδειο φάκελο και προσπαθήστε ξανά.
unlock.error.customPath.description.generic=Έχετε επιλέξει μια προσαρμοσμένη διαδρομή προσάρτησης για αυτή την κρύπτη, αλλά η χρήση της απέτυχε με το μήνυμα: %2$s
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Ξεκλείδωμα…
main.vaultlist.contextMenu.unlockNow=Ξεκλείδωμα τώρα
main.vaultlist.contextMenu.vaultoptions=Εμφάνιση επιλογών Vault
main.vaultlist.contextMenu.reveal=Αποκάλυψη εικονικού δίσκου
main.vaultlist.addVaultBtn=Προσθήκη Κρύπτης
main.vaultlist.addVaultBtn=Προσθήκη
main.vaultlist.addVaultBtn.menuItemNew=Νέα Κρύπτη...
main.vaultlist.addVaultBtn.menuItemExisting=Υπάρχουσα Κρύπτη...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Ευχαριστούμε που επιλέξατε το Cryptomator για να προστατεύσετε τα αρχεία σας. Αν χρειάζεστε οποιαδήποτε βοήθεια, ελέγξτε τους οδηγούς για αρχάριους:
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Revelar
# Add Vault Wizard
addvaultwizard.title=Añadir bóveda
## Welcome
addvaultwizard.welcome.newButton=Crear bóveda nueva
addvaultwizard.welcome.existingButton=Abrir bóveda existente
## New
addvaultwizard.new.title=Añadir nueva bóveda
### Name
addvaultwizard.new.nameInstruction=Elegir un nombre para la bóveda
addvaultwizard.new.namePrompt=Nombre de la bóveda
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Este es el lugar de acceso de la bóveda.
addvault.new.readme.accessLocation.3=Los archivos agregados a este volumen serán cifrados por Cryptomator. Se puede trabajar con ellos como en cualquier otra unidad/carpeta. Esta es sólo una vista descifrada del contenido, sus archivos permanecen cifrados en el disco duro todo el tiempo.
addvault.new.readme.accessLocation.4=No dude en eliminar este archivo.
## Existing
addvaultwizard.existing.title=Añadir bóveda existente
addvaultwizard.existing.instruction=Elija el archivo "vault.cryptomator" de su bóveda existente. Si solo existe un archivo llamado "masterkey.cryptomator", selecciónelo en su lugar.
addvaultwizard.existing.chooseBtn=Elegir…
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Desbloquear…
main.vaultlist.contextMenu.unlockNow=Desbloquear ahora
main.vaultlist.contextMenu.vaultoptions=Mostrar opciones de la bóveda
main.vaultlist.contextMenu.reveal=Revelar unidad
main.vaultlist.addVaultBtn=Añadir bóveda
main.vaultlist.addVaultBtn=Añadir
main.vaultlist.addVaultBtn.menuItemNew=Nueva bóveda...
main.vaultlist.addVaultBtn.menuItemExisting=Bóveda existente...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
@@ -30,9 +30,6 @@ traymenu.vault.reveal=ظاهر
# Add Vault Wizard
addvaultwizard.title=اضافه کردن گاوصندوق
## Welcome
addvaultwizard.welcome.newButton=ساخت گاوصندوق جدید
addvaultwizard.welcome.existingButton=باز کردن گاوصندوق موجود
## New
### Name
addvaultwizard.new.nameInstruction=یک نام برای گاوصندوق انتخاب کنید
@@ -112,7 +109,6 @@ main.closeBtn.tooltip=ببند
main.supporterCertificateMissing.tooltip=لطفا کمک مالی در نظر بگیرند
## Vault List
main.vaultlist.contextMenu.lock=قفل
main.vaultlist.addVaultBtn=اضافه کردن گاوصندوق
## Vault Detail
### Welcome
### Locked
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Paljasta
# Add Vault Wizard
addvaultwizard.title=Lisää Vault
## Welcome
addvaultwizard.welcome.newButton=Luo Uusi Vault
addvaultwizard.welcome.existingButton=Avaa Olemassaoleva Vault
## New
### Name
addvaultwizard.new.nameInstruction=Anna uusi nimi Vaultille
@@ -233,7 +230,6 @@ main.preferencesBtn.tooltip=Asetukset
main.vaultlist.contextMenu.lock=Lukitse
main.vaultlist.contextMenu.unlockNow=Avaa Nyt
main.vaultlist.contextMenu.reveal=Paljasta Asema
main.vaultlist.addVaultBtn=Lisää Vault
## Vault Detail
### Welcome
### Locked
+54 -4
View File
@@ -20,6 +20,10 @@ error.description=Oops! Hindi inaasahan ng Cryptomator na ito'y mangyari. Maaari
error.hyperlink.lookup=Hanapin ang solusyon
error.hyperlink.report=I-report ang problema
error.technicalDetails=Mga detalye:
error.existingSolutionDescription=Hindi inaasahan ng Cryptomator na mangyayari ito. Ngunit nakakita kami ng kasalukuyang solusyon para sa error na ito. Mangyaring tingnan ang sumusunod na link.
error.hyperlink.solution=Hanapin ang solusyon
error.lookupPermissionMessage=Maaaring maghanap ng solusyon ang Cryptomator para sa problemang ito online. Magpapadala ito ng kahilingan sa aming database ng problema mula sa iyong IP address.
error.dismiss=I-dismiss
error.lookUpSolution=Itignan ang solusyon
# Defaults
@@ -36,10 +40,8 @@ traymenu.vault.reveal=Ipakita
# Add Vault Wizard
addvaultwizard.title=Magdagdag ng Vault
## Welcome
addvaultwizard.welcome.newButton=Gumawa ng Bagong Vault
addvaultwizard.welcome.existingButton=Magbukas ng Umiiral na Vault
## New
addvaultwizard.new.title=Magdagdag ng Bagong Vault
### Name
addvaultwizard.new.nameInstruction=Pangalanan ang vault
addvaultwizard.new.namePrompt=Pangalan ng Vault
@@ -50,34 +52,78 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Custom Location
addvaultwizard.new.directoryPickerButton=Mamili…
addvaultwizard.new.directoryPickerTitle=Pumili ng Direktoryo
addvaultwizard.new.fileAlreadyExists=Mayroon nang file o direktoryo na may pangalan ng vault
addvaultwizard.new.locationDoesNotExist=Ang isang direktoryo sa tinukoy na landas ay hindi umiiral o hindi ma-access
addvaultwizard.new.locationIsNotWritable=Walang access sa pagsulat sa tinukoy na landas
addvaultwizard.new.locationIsOk=Angkop na lokasyon para sa iyong vault
addvaultwizard.new.invalidName=Di-wastong pangalan ng vault
addvaultwizard.new.validName=Wastong pangalan ng vault
addvaultwizard.new.validCharacters.message=Maaaring naglalaman ang pangalan ng vault ng mga sumusunod na character:
addvaultwizard.new.validCharacters.chars=Mga character ng salita (hal. a, ж o 수)
addvaultwizard.new.validCharacters.numbers=Numero
addvaultwizard.new.validCharacters.dashes=Hyphen (%s) o underscore (%s)
### Expert Settings
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Paganahin ang mga setting ng eksperto
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Maglagay ng value sa pagitan ng 36 at 220 (default 220)
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Ibukas ang dokumentasyon para matuto pa.
addvaultwizard.new.expertSettings.shorteningThreshold.title=Pinakamataas na haba ng mga naka-encrypt na pangalan ng file
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Wasto
### Password
addvaultwizard.new.createVaultBtn=Gumawa ng bagong Vault
addvaultwizard.new.generateRecoveryKeyChoice=Hindi mo maa-access ang iyong data nang wala ang iyong password. Gusto mo ba ng recovery key para sa kaso na nawala mo ang iyong password?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Oo pakiusap, mas ligtas kaysa sorry
addvaultwizard.new.generateRecoveryKeyChoice.no=Hindi, salamat, hindi ko mawawala ang aking password
### Information
addvault.new.readme.storageLocation.fileName=MAHALAGA.rtf
addvault.new.readme.storageLocation.1=⚠️ VAULT FILES ⚠️
addvault.new.readme.storageLocation.2=Ito ang lokasyon ng iyong vault na imbakan.
addvault.new.readme.storageLocation.3=HUWAG
addvault.new.readme.storageLocation.4=• baguhin ang anumang mga file sa loob ng direktoryong ito o
addvault.new.readme.storageLocation.5=• i-paste ang anumang mga file para sa pag-encrypt sa direktoryong ito.
addvault.new.readme.storageLocation.6=Kung gusto mo i-encrypt ang mga files at makita nang nilalaman ng vault, gawin ang nakasabi:
addvault.new.readme.storageLocation.7=1. Idagdag ang vault na ito sa Cryptomator.
addvault.new.readme.storageLocation.8=2. I-unlock ang vault sa Cryptomator.
addvault.new.readme.storageLocation.9=3. Buksan ang lokasyon ng pag-access sa pamamagitan ng pag-click sa pindutang "Ibunyag".
addvault.new.readme.storageLocation.10=Kung kailangan mo ng tulong, bisitahin ang dokumentasyon: %s
addvault.new.readme.accessLocation.fileName=WELCOME.rtf
addvault.new.readme.accessLocation.1=🔐️ Naka-encrypt na VOLUME 🔐️
addvault.new.readme.accessLocation.2=Ito ang lokasyon ng iyong vault.
addvault.new.readme.accessLocation.3=Ang anumang mga file na idinagdag sa volume na ito ay ie-encrypt ng Cryptomator. Maaari mong gawin ito tulad ng sa anumang iba pang drive/folder. Ito ay isang decrypted view lamang ng nilalaman nito, ang iyong mga file ay mananatiling naka-encrypt sa iyong hard drive sa lahat ng oras.
addvault.new.readme.accessLocation.4=Huwag mag-atubiling tanggalin ang file na ito.
## Existing
addvaultwizard.existing.title=Magdagdag ng Umiiral na Vault
addvaultwizard.existing.instruction=Piliin ang "vault.cryptomator" file ng iyong kasalukuyang vault. Kung mayroon lamang file na may pangalang "masterkey.cryptomator," piliin iyon sa halip.
addvaultwizard.existing.chooseBtn=Mamili…
addvaultwizard.existing.filePickerTitle=Piliin ang Vault File
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
## Success
addvaultwizard.success.nextStepsInstructions=Idinagdag ang vault na "%s".\nKailangan mong i-unlock ang vault na ito para ma-access o magdagdag ng mga content. Bilang kahalili, maaari mo itong i-unlock sa anumang susunod na oras.
addvaultwizard.success.unlockNow=I-unlock Ngayon
# Remove Vault
removeVault.title=Tanggalin %s
removeVault.message=Itangal ang vault?
removeVault.description=Makakalimutan lang nito ang Cryptomator tungkol sa vault na ito. Maaari mo itong idagdag muli. Walang matatanggal na mga naka-encrypt na file mula sa iyong hard drive.
removeVault.confirmBtn=Itangal ang vault
# Change Password
changepassword.title=Palitan ANG password
changepassword.enterOldPassword=Ilagay ang kasalukuyang password para sa "%s"
changepassword.finalConfirmation=Naiintindihan ko na hindi ko maa-access ang aking data kung nakalimutan ko ang aking password
# Forget Password
forgetPassword.title=Kalimutan ang Password
forgetPassword.message=Nakalimutan ang iyong password?
forgetPassword.description=Tatanggalin nito ang naka-save na password ng vault na ito mula sa keychain ng iyong system.
forgetPassword.confirmBtn=Kalimutan ang Password
# Unlock
unlock.title=I-unlock ang "%s"
unlock.passwordPrompt=Ipasok ang password para sa "%s":
unlock.savePassword=Maalala ang password
unlock.unlockBtn=I-unlock
## Select
unlock.chooseMasterkey.message=Hindi nahanap ang masterkey file
unlock.chooseMasterkey.filePickerTitle=Piliin ang masterkey file
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
## Success
@@ -103,6 +149,7 @@ lock.forced.retryBtn=Subukan muli
## Start
## Run
## Success
migration.success.unlockNow=I-unlock Ngayon
## Missing file system capabilities
## Impossible
@@ -139,10 +186,12 @@ main.closeBtn.tooltip=Isara
main.preferencesBtn.tooltip=Mga Kagustuhan
## Vault List
main.vaultlist.contextMenu.lock=I-lock
main.vaultlist.addVaultBtn=Magdagdag ng Vault
main.vaultlist.contextMenu.unlockNow=I-unlock Ngayon
main.vaultlist.addVaultBtn=Idagdag
## Vault Detail
### Welcome
### Locked
main.vaultDetail.unlockNowBtn=I-unlock Ngayon
### Unlocked
main.vaultDetail.lockBtn=I-lock
### Missing
@@ -158,6 +207,7 @@ vaultOptions.general.vaultName=Pangalan ng Vault
## Mount
vaultOptions.mount.mountPoint.directoryPickerButton=Mamili…
## Master Key
vaultOptions.masterkey.changePasswordBtn=Palitan ANG password
## Hub
# Recovery Key
@@ -22,9 +22,9 @@ error.hyperlink.report=Signaler cette erreur
error.technicalDetails=Détails :
error.existingSolutionDescription=Cryptomator ne s'attendait pas à ce que cela se produise. Mais nous avons trouvé une solution existante pour cette erreur. Veuillez consulter le lien suivant.
error.hyperlink.solution=Rechercher la solution
error.lookupPermissionMessage=Cryptomator peut rechercher une solution en ligne. Une requête sera envoyée à notre base de données de problèmes à partir de votre adresse IP.
error.lookupPermissionMessage=Cryptomator peut trouver une solution à ce problème en ligne. Cela enverra une requête à notre base de données de problèmes à partir de votre adresse IP.
error.dismiss=Ignorer
error.lookUpSolution=Rechercher une solution
error.lookUpSolution=Rechercher la solution
# Defaults
defaults.vault.vaultName=Coffre
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Révéler
# Add Vault Wizard
addvaultwizard.title=Ajouter un coffre
## Welcome
addvaultwizard.welcome.newButton=Créer un nouveau coffre
addvaultwizard.welcome.existingButton=Ouvrir un coffre existant
## New
addvaultwizard.new.title=Ajouter un nouveau coffre
### Name
addvaultwizard.new.nameInstruction=Choisir un nom pour le coffre
addvaultwizard.new.namePrompt=Nom du coffre
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Ceci est le chemin d'accès de votre coffre
addvault.new.readme.accessLocation.3=Tous les fichiers ajoutés à ce volume seront chiffrés par Cryptomator. Vous pouvez l'utiliser comme n'importe quel lecteur/répertoire. Ceci est seulement une vue déchiffrée de son contenu, vos fichiers restent chiffrés dans votre disque dur en permanence.
addvault.new.readme.accessLocation.4=Vous pouvez supprimer ce fichier.
## Existing
addvaultwizard.existing.title=Ajouter un coffre existant
addvaultwizard.existing.instruction=Choisissez le fichier « vault.cryptomator » de votre volume existant. Si seul le fichier « masterkey.cryptomator » est présent, sélectionnez celui-là.
addvaultwizard.existing.chooseBtn=Choisir...
addvaultwizard.existing.filePickerTitle=Sélectionnez le fichier correspondant au volume chiffré
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Impossible de monter le coffre vers le chemin pe
unlock.error.customPath.description.notSupported=Si vous souhaitez continuer à utiliser le chemin personnalisé, veuillez aller dans les préférences et sélectionner un type de volume qui le prend en charge. Sinon, allez dans les options du coffre et choisissez un point de montage pris en charge.
unlock.error.customPath.description.notExists=Le chemin de montage personnalisé n'existe pas. Créez-le dans votre système de fichiers local ou modifiez-le dans les options du coffre.
unlock.error.customPath.description.inUse=La lettre de lecteur ou le point de montage personnalisé «%s» est déjà utilisé.
unlock.error.customPath.description.hideawayNotDir=Le fichier temporaire et caché « %3$s » utilisé pour le déverrouillage n’a pas pu être supprimé. Veuillez vérifier le fichier puis le supprimer manuellement.
unlock.error.customPath.description.hideawayNotDir=Le fichier temporaire et caché «%3$s» utilisé pour déverrouiller n'a pas pu être supprimé. Veuillez vérifier le fichier puis le supprimer manuellement.
unlock.error.customPath.description.couldNotBeCleaned=Votre coffre n'a pas pu être monté au point "%s". Veuillez réessayer ou choisissez un autre point.
unlock.error.customPath.description.notEmptyDir=Le chemin de montage personnalisé "%s" n'est pas un dossier vide. Veuillez choisir un dossier vide et réessayez.
unlock.error.customPath.description.generic=Vous avez sélectionné un point de montage personnalisé pour ce coffre, mais son utilisation a échoué avec le message : %2$s
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Déverrouiller…
main.vaultlist.contextMenu.unlockNow=Déverrouiller maintenant
main.vaultlist.contextMenu.vaultoptions=Afficher les options du volume chiffré
main.vaultlist.contextMenu.reveal=Afficher le lecteur
main.vaultlist.addVaultBtn=Ajouter un volume chiffré
main.vaultlist.addVaultBtn=Ajouter
main.vaultlist.addVaultBtn.menuItemNew=Nouveau coffre...
main.vaultlist.addVaultBtn.menuItemExisting=Coffre existant...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Merci d'avoir choisi Cryptomator pour protéger vos fichiers. Si vous avez besoin d'aide, consultez nos guides de démarrage :
@@ -22,7 +22,6 @@ error.message=Produciuse un erro
# Tray Menu
# Add Vault Wizard
## Welcome
## New
### Name
### Location
@@ -35,9 +35,6 @@ traymenu.vault.reveal=חשוף
# Add Vault Wizard
addvaultwizard.title=הוספת כספת
## Welcome
addvaultwizard.welcome.newButton=צור כספת חדשה
addvaultwizard.welcome.existingButton=פתיחת כספת קיימת
## New
### Name
addvaultwizard.new.nameInstruction=בחירת שם עבור הכספת
@@ -127,6 +124,7 @@ unlock.success.revealBtn=חשוף את הכונן
unlock.error.customPath.message=כשלון בקישור הכספת לנתיב הידני שהוגדר
unlock.error.customPath.description.notSupported=אם ברצונך להשתמש בנתיב ידני, אנא גש להעדפות ובחר סוג volume שתומך בכך. אחרת, לך לאפשרויות הכספת ובחר אפשרות יעד קישור נתמך.
unlock.error.customPath.description.notExists=יעד הקישור הידני לא קיים. או שתיצור אותו במערכת הקבצים המקומית או שנה אותו באפשרויות הכספת.
unlock.error.customPath.description.hideawayNotDir=הקובץ הזמני, הנסתר %3$s עבור פתיחה לא ניתן להסרה. יש לבדוק את הקובץ ולמחוק אותו ידנית.
## Hub
hub.noKeychain.message=לא ניתן לגשת למפתח המכשיר
hub.noKeychain.description=כדאי לשחרר כספות האב נדרש מפתח מכשיר שיאובטח בצרור מפתחות. כדאי להמשיך, אפשר ״%s״ ובחר את צרור המפתחות בהעדפות.
@@ -351,7 +349,7 @@ main.vaultlist.contextMenu.unlock=שחרר נעילה…
main.vaultlist.contextMenu.unlockNow=בטל נעילה כעת
main.vaultlist.contextMenu.vaultoptions=הצג את אפשרויות הכספת
main.vaultlist.contextMenu.reveal=חשוף את הכונן
main.vaultlist.addVaultBtn=יצירת כספת
main.vaultlist.addVaultBtn=הוספה
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=תודה שבחרת ב- Cryptomator להגן על הקבצים שלך. אם אתה זקוק לסיוע, אנא עיין במדריכים שלנו:
@@ -35,9 +35,6 @@ traymenu.vault.reveal=प्रत्यक्ष करें
# Add Vault Wizard
addvaultwizard.title=वाउल्ट डालें
## Welcome
addvaultwizard.welcome.newButton=नया वाउल्ट बनाएं
addvaultwizard.welcome.existingButton=मौजूदा वाउल्ट खोलें
## New
### Name
addvaultwizard.new.nameInstruction=वाउल्ट के लिए नाम चुनें
@@ -212,7 +209,6 @@ main.vaultlist.contextMenu.unlock=अनलॉक करें...
main.vaultlist.contextMenu.unlockNow=अब अनलॉक करें
main.vaultlist.contextMenu.vaultoptions=वॉल्ट के विकल्प दिखाए
main.vaultlist.contextMenu.reveal=फोल्डर खोलें
main.vaultlist.addVaultBtn=वाउल्ट डालें
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Cryptomator को अपनी फाइल्स सिराक्षित रखने को चुनने के लिए धन्यवाद। अगर आपको सहायता चाइये, तो हमारी गेटिंग स्टार्टेड गाइगाइड्स देखिये:
@@ -35,9 +35,6 @@ traymenu.vault.reveal=Otkrij
# Add Vault Wizard
addvaultwizard.title=Dodaj trezor
## Welcome
addvaultwizard.welcome.newButton=Izradi novi trezor
addvaultwizard.welcome.existingButton=Otvori postojeći trezor
## New
### Name
addvaultwizard.new.nameInstruction=Odaberi ime za trezor
@@ -277,7 +274,7 @@ main.vaultlist.contextMenu.unlock=Otključaj…
main.vaultlist.contextMenu.unlockNow=Otključaj sada
main.vaultlist.contextMenu.vaultoptions=Prikaži opcije trezora
main.vaultlist.contextMenu.reveal=Otkrij pogon
main.vaultlist.addVaultBtn=Dodaj trezor
main.vaultlist.addVaultBtn=Dodaj
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Hvala što ste odabrali Cryptomator za zaštitu Vaših datoteka. Ukoliko trebate pomoć, provjerite naše vodiče za početak rada:
@@ -37,9 +37,6 @@ traymenu.vault.reveal=Megmutatás
# Add Vault Wizard
addvaultwizard.title=Széf hozzáadása
## Welcome
addvaultwizard.welcome.newButton=Új széf létrehozása
addvaultwizard.welcome.existingButton=Meglévő széf megnyitása
## New
### Name
addvaultwizard.new.nameInstruction=Válasszon egy nevet az új széf számára
@@ -135,7 +132,6 @@ unlock.error.customPath.message=Nem lehet csatolni a széfet az egyéni útvonal
unlock.error.customPath.description.notSupported=Ha szeretné továbbra is az egyéni útvonalat használni, kérem, menjen a beállításokba és válasszon egy kötet típust, amely támogatja azt. Máskülönben, menjen a széf opciókhoz és válasszon egy támogatott csatoláspontot.
unlock.error.customPath.description.notExists=Az egyéni csatolás útvonal nem létezik. Hozza létre a helyi fájlrendszerében vagy változtassa meg a széf opciókban.
unlock.error.customPath.description.inUse=A meghajtó betűjele vagy a csatolási útvonal: "%s" már foglalt.
unlock.error.customPath.description.hideawayNotDir=A feloldáshoz használt, "%3$s" nevű ideiglenes, rejtett fájlt nem lehet eltávolítani. Kérjük, ellenőrizze a fájlt, majd törölje manuálisan.
unlock.error.customPath.description.couldNotBeCleaned=A széfet nem lehetett a "%s" elérési útvonalra csatlakoztatni. Kérjük, próbálja meg újra, vagy válasszon másik elérési útvonalat.
unlock.error.customPath.description.notEmptyDir=A megadott "%s" elérési útvonal nem egy üres mappa. Kérjük, válasszon egy üres mappát, és próbálja meg újra.
unlock.error.customPath.description.generic=Egyéni csatolási útvonalat választott ehhez a széfhez, de használatakor ez a hibaüzenet érkezett: %2$s
@@ -363,7 +359,7 @@ main.vaultlist.contextMenu.unlock=Feloldás…
main.vaultlist.contextMenu.unlockNow=Azonnali feloldás
main.vaultlist.contextMenu.vaultoptions=Széf beállítások
main.vaultlist.contextMenu.reveal=Széf megjelenítése
main.vaultlist.addVaultBtn=Széf hozzáadása
main.vaultlist.addVaultBtn=Hozzáadás
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Köszönjük, hogy a Cryptomator programot választotta a fájlai védelmére. Ha segítségre van szüksége, akkor olvassa el a kezdő útmutatónk lépéseit:
@@ -35,9 +35,6 @@ traymenu.vault.reveal=Perlihatkan
# Add Vault Wizard
addvaultwizard.title=Tambah Vault
## Welcome
addvaultwizard.welcome.newButton=Buat Vault Baru
addvaultwizard.welcome.existingButton=Buka Vault yang Tersedia
## New
### Name
addvaultwizard.new.nameInstruction=Beri nama untuk vault berikut
@@ -277,7 +274,6 @@ main.vaultlist.contextMenu.unlock=Buka Kunci…
main.vaultlist.contextMenu.unlockNow=Buka Kunci Sekarang
main.vaultlist.contextMenu.vaultoptions=Tampilkan Opsi Vault
main.vaultlist.contextMenu.reveal=Buka Drive
main.vaultlist.addVaultBtn=Tambah Vault
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Terima kasih telah memilih Cryptomator untuk melindungi file Anda. Jika Anda memerlukan bantuan, lihat panduan awal kami:
@@ -40,10 +40,8 @@ traymenu.vault.reveal=Mostra
# Add Vault Wizard
addvaultwizard.title=Aggiungi Cassaforte
## Welcome
addvaultwizard.welcome.newButton=Crea Nuova Cassaforte
addvaultwizard.welcome.existingButton=Apri Cassaforte Esistente
## New
addvaultwizard.new.title=Aggiungi una Nuova Cassaforte
### Name
addvaultwizard.new.nameInstruction=Scegli un nome per la cassaforte
addvaultwizard.new.namePrompt=Nome della Cassaforte
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=Questa è la posizione d'accesso della tua
addvault.new.readme.accessLocation.3=Ogni file aggiunto a questo volume sarà crittografato da Cryptomator. Puoi lavorarci come su ogni altra unità/cartella. Questa è solo una vista decrittografata del suo contenuto, i tuoi file restano sempre crittografati sul tuo disco rigido.
addvault.new.readme.accessLocation.4=Sentiti libero di rimuovere questo file.
## Existing
addvaultwizard.existing.title=Aggiungi una Cassaforte Esistente
addvaultwizard.existing.instruction=Scegliere il file "vault.cryptomator" della tua cassaforte. Se esiste solo un file chiamato "masterkey.cryptomator", allora scegli quello.
addvaultwizard.existing.chooseBtn=Scegli…
addvaultwizard.existing.filePickerTitle=Seleziona file cassaforte
@@ -138,7 +137,7 @@ unlock.error.customPath.message=Impossibile montare la cassaforte sul percorso p
unlock.error.customPath.description.notSupported=Se desideri continuare a utilizzare il percorso personalizzato, vai alle preferenze e seleziona un tipo di volume che lo supporta. Altrimenti, vai alle opzioni della cassaforte e scegli un punto di montaggio supportato.
unlock.error.customPath.description.notExists=Il percorso di mount personalizzato non esiste. Crealo nel tuo file system locale o cambialo nelle opzioni della cassaforte.
unlock.error.customPath.description.inUse=La lettera di unità o il percorso di montaggio selezionato "%s" è già in uso.
unlock.error.customPath.description.hideawayNotDir=Il file provvisorio nascosto "%3$s" utilizzato per sbloccare non può essere rimosso. Controlla il file ed eliminalo manualmente.
unlock.error.customPath.description.hideawayNotDir=Impossibile rimuovere il file temporaneo nascosto "%3$s" utilizzato per lo sblocco. Controllare il file e quindi eliminarlo manualmente.
unlock.error.customPath.description.couldNotBeCleaned=La tua cassaforte non può essere montata sul percorso "%s". Riprova o scegli un percorso diverso.
unlock.error.customPath.description.notEmptyDir=Il percorso di montaggio selezionato "%s" non è una cartella vuota. Scegli una cartella vuota e riprova.
unlock.error.customPath.description.generic=Hai selezionato un percorso di montaggio personalizzato per questa cassaforte, ma il suo utilizzo non è riuscito con il messaggio: %2$s
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=Sblocca…
main.vaultlist.contextMenu.unlockNow=Sblocca Ora
main.vaultlist.contextMenu.vaultoptions=Mostra le Opzioni della Cassaforte
main.vaultlist.contextMenu.reveal=Rivela Unità
main.vaultlist.addVaultBtn=Aggiungi Cassaforte
main.vaultlist.addVaultBtn=Aggiungi
main.vaultlist.addVaultBtn.menuItemNew=Nuova Cassaforte...
main.vaultlist.addVaultBtn.menuItemExisting=Cassaforte esistente...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Grazie per aver scelto Cryptomator per proteggere i tuoi file. Se necessiti d'assistenza, dai un'occhiata alle nostre guide per iniziare:
@@ -40,10 +40,8 @@ traymenu.vault.reveal=表示
# Add Vault Wizard
addvaultwizard.title=金庫を追加
## Welcome
addvaultwizard.welcome.newButton=新しい金庫を作成
addvaultwizard.welcome.existingButton=すでにある金庫を開く
## New
addvaultwizard.new.title=新しい金庫を追加
### Name
addvaultwizard.new.nameInstruction=金庫の名前を入力してください
addvaultwizard.new.namePrompt=金庫の名前
@@ -93,6 +91,7 @@ addvault.new.readme.accessLocation.2=ここは金庫のアクセス先です。
addvault.new.readme.accessLocation.3=このボリュームに追加したファイルは Cryptomator によって暗号化されます。一般的なドライブ/フォルダー上のように作業することができます。ここでは復号したコンテンツのビューにすぎず、ファイルは常にハードドライブ上で暗号化されています。
addvault.new.readme.accessLocation.4=このファイルはいつでも削除できます。
## Existing
addvaultwizard.existing.title=既存の金庫を追加
addvaultwizard.existing.instruction=すでにある金庫の "vault.cryptomator" ファイルを選択してください。"masterkey.cryptomator" という名前のファイルだけが存在する場合は、このファイルを代わりに選択してください。
addvaultwizard.existing.chooseBtn=選択...
addvaultwizard.existing.filePickerTitle=金庫のファイルを選択
@@ -366,7 +365,9 @@ main.vaultlist.contextMenu.unlock=解錠...
main.vaultlist.contextMenu.unlockNow=今すぐ解錠
main.vaultlist.contextMenu.vaultoptions=金庫のオプションを表示
main.vaultlist.contextMenu.reveal=ドライブを表示
main.vaultlist.addVaultBtn=金庫を追加
main.vaultlist.addVaultBtn=追加
main.vaultlist.addVaultBtn.menuItemNew=新しい金庫...
main.vaultlist.addVaultBtn.menuItemExisting=既存の金庫...
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=ファイルを保護するために Cryptomator を選んでいただきありがとうございます。ヘルプが必要であれば、スタートガイドをご覧ください:
+20 -4
View File
@@ -35,9 +35,6 @@ traymenu.vault.reveal=표시
# Add Vault Wizard
addvaultwizard.title=Vault 추가
## Welcome
addvaultwizard.welcome.newButton=새 Vault 생성
addvaultwizard.welcome.existingButton=기존 Vault 열기
## New
### Name
addvaultwizard.new.nameInstruction=새 Vault의 이름을 입력하십시요
@@ -134,10 +131,15 @@ hub.register.registerBtn=확인
### Registration Success
### Registration Failed
### Unauthorized
hub.unauthorized.message=액세스 거부
hub.unauthorized.description=귀하의 기기는 아직 이 저장소에 액세스할 수 있는 권한이 없습니다. Vault 소유자에게 승인을 요청하세요.
### License Exceeded
hub.invalidLicense.message=Hub 라이선스가 잘못되었습니다.
hub.invalidLicense.description=Cryptomator Hub 인스턴스에 잘못된 라이선스가 있습니다. 라이센스를 업그레이드하거나 갱신하려면 허브 관리자에게 알리십시오.
# Lock
## Force
lock.forced.message=잠금 실패
lock.forced.description=대기 중인 작동이나 파일이 열려있어 "%s"를 잠그는데 실패하였습니다. 이 Vault를 강제로 잠글 수 있으나, 입/출력의 중단은 저장되지 않은 데이터의 유실을 초래할 수 있습니다.
lock.forced.retryBtn=재시도
lock.forced.forceBtn=강제 잠금
@@ -178,6 +180,7 @@ migration.impossible.moreInfo=Vault를 이전 버전으로 계속 열수 있습
health.title="%s"의 상태 검사
health.intro.header=상태 검사
health.intro.text=상태 검사는 Vault의 내부 구조의 문제점을 점검하고 해결할 수 있는 기능입니다. 다음 사항을 유의하시기 바랍니다:
health.intro.remarkFix=모든 문제를 해결할 수 있는 것은 아닙니다.
health.intro.remarkBackup=데이터가 손상된 경우 백업만이 유일한 해결책입니다.
health.intro.affirmation=나는 위 정보를 읽고 정말 이해했습니다.
## Start Failure
@@ -186,6 +189,7 @@ health.fail.ioError=설정 파일에 접근하는 중 에러가 발생했습니
health.fail.parseError=Vault 설정을 파싱하는 중 에러가 발생했습니다.
health.fail.moreInfo=더 많은 정보
## Check Selection
health.checkList.description=검사할 항목을 왼쪽 목록에서 선택하거나 아래 버튼을 사용하세요.
health.checkList.selectAllButton=모든 항목 선택
health.checkList.deselectAllButton=모든 항목 선택 해제
health.check.runBatchBtn=선택된 검사항목 실행
@@ -195,6 +199,8 @@ health.check.detail.checkScheduled=검사가 예약되었습니다.
health.check.detail.checkRunning=검사가 현재 실행중입니다...
health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
health.check.detail.checkCancelled=검사가 취소되었습니다
health.check.detail.listFilters.label=필터
health.check.exportBtn=보고서 내보내기
## Result view
@@ -228,9 +234,12 @@ preferences.interface.showMinimizeButton=최소화 버튼 표시
preferences.interface.showTrayIcon=트레이 아이콘 보기 (재시작 필요)
## Volume
preferences.volume=가상 드라이브
preferences.volume.type=볼륨 유형
preferences.volume.type.automatic=자동
preferences.volume.tcp.port=TCP 포트
preferences.volume.supportedFeatures=현재 선택한 볼륨 타입은 다음과 같은 기능들을 지원합니다:
preferences.volume.feature.mountFlags=사용자 정의 마운트 설정
preferences.volume.feature.readOnly=읽기 전용 마운트
## Updates
preferences.updates=업데이트
preferences.updates.currentVersion=현재 버전: %s
@@ -265,9 +274,11 @@ stats.read.accessCount=총 읽기 횟수: %d
stats.write.throughput.idle=쓰기: 대기중
stats.write.throughput.mibs=쓰기: %.2f MiB/s
stats.write.total.data.none=데이터 기록됨: -
stats.write.total.data.kib=데이터 쓰기: %.1f KiB
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
stats.encr.total.data.none=데이터 암호화: -
stats.encr.total.data.kib=데이터 암호화: %.1f KiB
stats.encr.total.data.mib=데이터 암호화: %.1f MiB
stats.encr.total.data.gib=데이터 암호화: %.1f GiB
stats.write.accessCount=총 쓰기 횟수: %d
@@ -289,7 +300,7 @@ main.vaultlist.contextMenu.unlock=잠금해제...
main.vaultlist.contextMenu.unlockNow=지금 잠금해제
main.vaultlist.contextMenu.vaultoptions=Vault 옵션 보기
main.vaultlist.contextMenu.reveal=드라이브 표시
main.vaultlist.addVaultBtn=Vault 추가
main.vaultlist.addVaultBtn=추가
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=파일을 보호하기 위해 Cryptomator를 선택해주셔서 감사합니다. 만약 다른 도움이 필요하시면, 시작안내서를 참조하시기 바랍니다.
@@ -303,10 +314,12 @@ main.vaultDetail.passwordSavedInKeychain=비밀번호 저장됨
main.vaultDetail.unlockedStatus=잠금해제됨
main.vaultDetail.accessLocation=이 Vault의 내용은 다음의 경로에서 접근할 수 있습니다:
main.vaultDetail.revealBtn=드라이브 표시
main.vaultDetail.copyUri=URI 복사
main.vaultDetail.lockBtn=잠금
main.vaultDetail.bytesPerSecondRead=읽기:
main.vaultDetail.bytesPerSecondWritten=쓰기:
main.vaultDetail.throughput.idle=대기
main.vaultDetail.throughput.kbps=%.1f KiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Vault 통계
### Missing
@@ -363,6 +376,7 @@ vaultOptions.masterkey.recoveryKeyExplanation=복구 키는 비밀번호를 잊
vaultOptions.masterkey.showRecoveryKeyBtn=복구 키 표시
vaultOptions.masterkey.recoverPasswordBtn=비밀번호 재설정
## Hub
vaultOptions.hub=복구
# Recovery Key
## Display Recovery Key
@@ -374,6 +388,7 @@ recoveryKey.display.StorageHints=매우 안전한곳에 보관하십시요. 예
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=비밀번호 바꾸기
recoveryKey.recover.prompt="%s"의 복구키를 입력하십시요:
recoveryKey.recover.correctKey=올바른 복구 키 입니다
recoveryKey.recover.wrongKey=이 복구 키는 다른 vault의 키입니다
recoveryKey.recover.invalidKey=해당 복구 키는 유효하지 않습니다
@@ -404,5 +419,6 @@ quit.message=잠금 해제된 vault들이 존재합니다
quit.lockAndQuitBtn=Vault 잠금 후 종료하기
# Forced Quit
quit.forced.forceAndQuitBtn=Vault 강제 잠금 후 종료하기
# Update Reminder
@@ -35,9 +35,6 @@ traymenu.vault.reveal=Atklāt
# Add Vault Wizard
addvaultwizard.title=Pievienot glabātuvi
## Welcome
addvaultwizard.welcome.newButton=Izveidot jaunu glabātuvi
addvaultwizard.welcome.existingButton=Atvērt esošu glabātuvi
## New
### Name
addvaultwizard.new.nameInstruction=Izvēlies glabātuves nosaukumu
@@ -190,7 +187,6 @@ main.vaultlist.emptyList.onboardingInstruction=Spied šeit, lai pievienotu glab
main.vaultlist.contextMenu.lock=Aizslēgt
main.vaultlist.contextMenu.unlockNow=Atslēgt tagad
main.vaultlist.contextMenu.reveal=Atklāt disku
main.vaultlist.addVaultBtn=Pievienot glabātuvi
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Paldies, ka izvēlējāties Cryptomator lai aizsargātu jūsu datus. Ja jums nepieciešama palīdzība, iepazīstieties ar mūsu darba sākšanas ceļvežiem:

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