Compare commits

...
Author SHA1 Message Date
Armin Schrenk d013497c48 cleanup 2023-05-09 09:23:20 +02:00
Armin Schrenk 8691eec42b enable directory listings in jar files 2023-05-08 21:21:43 +02:00
Armin Schrenk 0f9d572677 remove static language and read i18dir in constructor 2023-05-08 20:28:56 +02:00
Armin Schrenk baee23b6d4 Merge branch 'develop' into feature/dynamic-languages
# Conflicts:
#	src/main/java/org/cryptomator/launcher/SupportedLanguages.java
#	src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java
2023-05-08 19:56:38 +02:00
Tobias HagemannandGitHub 532ffb1202 Merge pull request #2882 from bluen/develop
Respect user's locale when sorting language list
2023-05-05 11:51:17 +02:00
Tobias Hagemann 2a704d5eb4 init collator once 2023-05-05 11:45:02 +02:00
Sebastian Stenzel e8f8466d9a adjusted labels used in auto-generated release notes
[ci skip]
2023-05-05 10:56:44 +02:00
Sebastian Stenzel 9297562c99 improve auto-generated release notes
[ci skip]
2023-05-05 10:52:51 +02:00
Jürgen Kleer 7d62fc78de Set preferred locale in constructor, make it default in applyPreferred 2023-04-27 18:03:41 +02:00
Jürgen Kleer 8e7e7de358 Refactoring
make LANGUAGE_TAGS private and provide a getter
2023-04-26 17:34:47 +02:00
Jürgen Kleer 10c60d7492 https://github.com/cryptomator/cryptomator/issues/2813
> List of languages should have system default, English and then all other languages in alphabetic order.
> That is, in alphabetic order with respect to the language the list is localized in (seems to be English always)
2023-04-26 15:24:50 +02:00
Armin Schrenk aa03bd119a Merge branch 'main' into develop 2023-04-25 10:45:51 +02:00
Armin Schrenk 325ffda9af Merge branch 'release/1.8.0' 2023-04-25 10:45:08 +02:00
Armin Schrenk d1270ceeb2 finalize 1.8.0 2023-04-25 10:44:41 +02:00
Armin Schrenk 7bfd0ed341 load supported languages on app start 2023-04-24 18:52:09 +02:00
Armin Schrenk 901a290dd9 prepare 1.8.0 2023-04-24 17:35:25 +02:00
CryptobotandGitHub 35b9dadfc2 New Crowdin updates (#2848)
New translations strings.properties

Arabic; Belarusian; Bengali; Bosnian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Croatian; Czech; Danish; Dutch; Filipino; 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-04-24 17:33:59 +02:00
Armin SchrenkandGitHub 5f57678edc Merge pull request #2840 from cryptomator/feature/convert-hub-to-local
Feature: convert hub-based vault to password-based
2023-04-21 15:31:28 +02:00
Tobias Hagemann 30e1922bc9 fixed spacing
[ci skip]
2023-04-21 15:23:06 +02:00
Armin Schrenk 2e0908ab15 replace config manually instead of using CryptoFileSystem.init() 2023-04-21 09:40:34 +02:00
Julian Raufelder 689ce5b985 Revive status and no-response bot 2023-04-20 15:38:57 +02:00
Tobias Hagemann a71a23aa31 replaced password bullet point with a different char that exists in open sans
[ci skip]
2023-04-18 16:36:27 +02:00
Tobias Hagemann 864454e6fc updated strings
[ci skip]
2023-04-18 16:03:05 +02:00
Armin Schrenk 94c3381723 forgot one renaming 2023-04-18 15:40:44 +02:00
d9f945e70a Apply suggestions from code review
more renaming

Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2023-04-18 15:34:20 +02:00
Armin Schrenk dc9b39202f rename classes 2023-04-18 14:34:48 +02:00
Armin Schrenk 2a01aba3cf clean up 2023-04-17 12:51:36 +02:00
Armin Schrenk 4305fd3285 close also vault options window to prevent invalid state of options window 2023-04-17 12:50:46 +02:00
a24cd1ba7f Apply suggestions from code review
Co-authored-by: Tobias Hagemann <tobias.hagemann@skymatic.de>
2023-04-14 16:47:45 +02:00
Tobias Hagemann 2ba0d963ec updated insets
[ci skip]
2023-04-13 16:12:08 +02:00
Julian Raufelder cd0c6fbd33 Merge branch 'hotfix/1.7.5' into develop 2023-04-07 11:45:08 +02:00
Julian Raufelder f4374a2606 Merge branch 'hotfix/1.7.5' 2023-04-07 11:41:24 +02:00
Julian Raufelder a1d5b8a4e2 Prepare 1.7.5 2023-04-07 11:39:21 +02:00
Julian Raufelder 34e430aff6 Revert "bump cryptofs"
This reverts commit db2560fccf.
2023-04-07 11:35:38 +02:00
Armin Schrenk c79766cdf6 Merge branch 'main' into develop 2023-04-05 09:58:30 +02:00
Armin Schrenk bf76bad626 Merge branch 'release/1.7.4' 2023-04-05 09:57:55 +02:00
Armin Schrenk c3f654b454 finalize release 2023-04-05 09:56:34 +02:00
Armin Schrenk d1d990d47c prepare 1.7.4 2023-04-04 18:17:24 +02:00
CryptobotandGitHub 6052c0589e New Crowdin updates (#2800)
New translations strings.properties

Belarusian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Danish; Dutch; French; German; Greek; Hebrew; Hungarian; Italian; Japanese; Norwegian Bokmal; Polish; Portuguese; Portuguese, Brazilian; Romanian; Russian; Slovak; Spanish; Swahili, Tanzania; Swedish; Turkish; 

[ci skip]
2023-04-04 18:15:07 +02:00
Armin Schrenk db2560fccf bump cryptofs 2023-04-04 13:44:19 +02:00
Armin Schrenk 3a50c32e50 rework convert button logic 2023-04-04 12:42:23 +02:00
Armin Schrenk 65eca31d26 integrate internationalization 2023-04-04 12:17:22 +02:00
Armin Schrenk 3462e0b540 further design adjustment 2023-04-03 10:42:25 +02:00
Armin Schrenk 697529136e use regular jdk 2023-03-31 18:27:35 +02:00
Armin Schrenk 50d31bdc18 integrate debug installer feature into regular windows ci workflow 2023-03-31 18:24:10 +02:00
Armin Schrenk 84caf96d3f closes #2814
Zulu JDK does not create required directory structure, so use temurin + gluon jfx jmods instead
2023-03-31 18:21:30 +02:00
Armin Schrenk a1a5fd3609 adjust design 2023-03-30 17:35:32 +02:00
Armin Schrenk 6c11cc8f1d complete workflow 2023-03-30 16:47:30 +02:00
Armin Schrenk 2b391a6ee3 Merge branch 'develop' into feature/convert-hub-to-local 2023-03-30 15:54:20 +02:00
Armin Schrenk b7fc03213d bump fuse-nio-adapter
Fixes #2801
2023-03-30 11:28:07 +02:00
Armin SchrenkandGitHub dfe17569e1 Merge pull request #2789 from cryptomator/feature/2786-only-restart-macfuset
Feature: Only require app restart if switching between macFUSE and FUSE-T
2023-03-30 11:23:09 +02:00
Armin Schrenk 827f9ad141 adjust comment 2023-03-30 11:18:28 +02:00
Armin Schrenk c8a6d0339e Use in volume preferences controller same objet as in MountModule to store first used fuse mount service 2023-03-29 17:42:18 +02:00
Tobias Hagemann b5bbd21f25 refactored the refactoring to a macfuse/fuse-t workaround again 2023-03-29 16:50:02 +02:00
Tobias Hagemann 771468c8c6 refactored macfuse/fuse-t specific workaround to a generic fuse workaround 2023-03-29 16:16:04 +02:00
Armin Schrenk ea2a48771f Closes #2829 2023-03-29 12:03:35 +02:00
Tobias Hagemann 0e10da25b3 Update README.md
[ci skip]
2023-03-29 11:58:14 +02:00
Armin Schrenk 943a3e9cfd adjust visibility 2023-03-29 11:42:14 +02:00
Armin Schrenk c988fb50a7 increase readability 2023-03-29 11:39:18 +02:00
Armin Schrenk 219ee0da9a implement core functionality 2023-03-28 14:02:11 +02:00
Armin Schrenk 5665e92839 deduplicate and add convenience method 2023-03-28 14:01:57 +02:00
Armin Schrenk 04ff188624 rename method in recoveryKeyFactory 2023-03-28 13:59:59 +02:00
Armin Schrenk ec7d6eafec add new password screen for convert hub vault flow 2023-03-24 18:11:39 +01:00
Armin Schrenk 28bb2ff9b1 add stub for vault conversion (hub to local) 2023-03-24 17:22:05 +01:00
Armin Schrenk a92ebfdc7b replace builder by stateless, threadsafe factory 2023-03-24 16:42:04 +01:00
Armin Schrenk f1e97fa64b fix workflow 2023-03-22 10:44:54 +01:00
Armin Schrenk b9d5cf04c2 fix workflow 2023-03-22 10:40:52 +01:00
Armin Schrenk 75cd3e44d8 change win-debug workflow to use temurin and openjfx jmods from gluon 2023-03-22 10:39:00 +01:00
Armin Schrenk 3cf1b829b8 make recovery key ui validation reusable 2023-03-20 21:40:16 +01:00
Armin Schrenk 6e4e9cd261 Merge branch 'develop' into feature/hub-local-access 2023-03-20 20:43:34 +01:00
Armin Schrenk e15dd7565f add workflow to build a windows debug launcher 2023-03-20 10:35:42 +01:00
Tobias Hagemann 77bc60fe5b Update README.md 2023-03-17 17:56:46 +01:00
Tobias HagemannandGitHub 9f633a1ecb Update README.md 2023-03-17 17:06:34 +01:00
Armin Schrenk fcf59d12a8 remove winget workflow 2023-03-15 16:47:34 +01:00
Armin Schrenk 6721075831 Merge branch 'main' into develop 2023-03-15 13:51:59 +01:00
Armin Schrenk 4bb0026415 Merge branch 'release/1.7.3' 2023-03-15 13:51:20 +01:00
Armin Schrenk bebae14744 finalize release 2023-03-15 13:51:09 +01:00
Armin Schrenk 997315eaf5 prepare 1.7.3 2023-03-15 13:49:53 +01:00
CryptobotandGitHub 16d677c40f New Crowdin updates (#2766)
New translations strings.properties

Belarusian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; French; German; Hebrew; Norwegian Bokmal; Russian; Swahili, Tanzania; Turkish; 

[ci skip]
2023-03-15 13:47:22 +01:00
Armin Schrenk 42a1913c17 Fixes #2797 and fixes #2760 2023-03-15 13:42:03 +01:00
Armin Schrenk c3f6655e48 add developer 2023-03-15 12:34:13 +01:00
Armin Schrenk fa1b0f2de8 reestablish interrupt state 2023-03-15 12:29:19 +01:00
Armin Schrenk 385574a618 prevent infinite loop 2023-03-15 12:10:15 +01:00
Armin Schrenk a67477bf3b Fixes #2778
ensure that mountpoint is ready
2023-03-15 11:40:44 +01:00
Armin Schrenk 03a362e9b4 fix compilation 2023-03-13 13:01:13 +01:00
Armin Schrenk 2223bc5e78 use correct selection logic 2023-03-13 12:58:34 +01:00
Armin Schrenk fdc0d2d6b5 only require restart in case if macFUSE or FUSE-T
Closes #2786
2023-03-13 12:44:56 +01:00
Armin Schrenk 58ed48b097 UI:
* remove restart notice for volume type
* only show restart label, when switching between macFUSE and FUSET
2023-03-13 11:17:56 +01:00
Julian Raufelder 7a0d255bd3 Use the same source tarball for signing that is linked in the release 2023-03-07 13:44:36 +01:00
Armin Schrenk 228fa099cb Merge branch 'main' into develop 2023-03-07 12:24:26 +01:00
Armin Schrenk a60ff20f15 Merge branch 'release/1.7.2' 2023-03-07 12:23:30 +01:00
Armin Schrenk 2328a5e3a8 finalize release 2023-03-07 12:19:35 +01:00
Armin Schrenk e7e181b1a5 Merge branch 'develop' into release/1.7.2 2023-03-07 12:19:23 +01:00
Armin Schrenk f3e7c08b43 deactivate winget workflow 2023-03-07 12:19:05 +01:00
Armin Schrenk 2a41afcfa9 prepare 1.7.2 2023-03-07 11:52:04 +01:00
CryptobotandGitHub 5bf38a328c New Crowdin updates (#2743)
New translations strings.properties

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

[ci skip]
2023-03-07 11:49:50 +01:00
Armin Schrenk f983b29034 use correct docs URL for volume type 2023-03-07 11:40:10 +01:00
Armin Schrenk 4bc217e489 fixes #2761 2023-03-07 11:36:59 +01:00
Armin Schrenk b24f52a4ec fixes 2741 2023-03-07 10:58:34 +01:00
Armin Schrenk 91e7fa3de3 fixes #2758, fixes #2754 2023-03-06 15:58:02 +01:00
Armin Schrenk 1365efab3f use recommended notation of unit prefixes 2023-03-06 15:54:43 +01:00
Armin Schrenk 852963b785 check for correct WinFsp version 2023-03-06 15:50:21 +01:00
Armin Schrenk c47d4eaf02 Merge branch 'main' into develop 2023-03-03 16:37:45 +01:00
Armin Schrenk 36da26c6b9 Merge branch 'release/1.7.1' 2023-03-03 16:19:49 +01:00
Armin Schrenk 33e1cb3131 finalize release 2023-03-03 16:19:37 +01:00
Armin Schrenk ed4b433df0 prepare 1.7.1 2023-03-03 16:14:01 +01:00
Armin Schrenk d2ece543c8 suppress false positive 2023-03-03 16:04:34 +01:00
Armin Schrenk 4bb2df7d8f Fixes #2730 2023-03-03 16:03:36 +01:00
CryptobotandGitHub 4d5c6ca29a New Crowdin updates (#2712)
New translations strings.properties

Danish; German; Hebrew; 

[ci skip]

Closes #2724
2023-03-03 16:02:09 +01:00
Armin Schrenk f0d597ba73 require fuse3 packge for deb to have fusermount3 2023-03-01 19:28:40 +01:00
Armin Schrenk 310329d0b5 use correct javafx patch version for debia build 2023-03-01 15:51:42 +01:00
Julian Raufelder 30f86a7f2a Fix issue template 2023-03-01 15:23:30 +01:00
Armin Schrenk 5237dc8cae Merge branch 'main' into develop 2023-03-01 12:07:46 +01:00
Armin Schrenk 67ff4da05b Merge branch 'release/1.7.0' 2023-03-01 12:06:40 +01:00
Armin Schrenk 947eb11198 set correct version in pom 2023-03-01 12:06:25 +01:00
Armin Schrenk c6a9af1a04 update metainfo.xml 2023-03-01 12:03:03 +01:00
Armin Schrenk 0ddf221e26 Merge branch 'develop' into release/1.7.0 2023-03-01 11:43:49 +01:00
CryptobotandGitHub 3f2b5ccde9 New Crowdin updates (#2698)
New translations strings.properties

Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; French; Japanese; 

[ci skip]
2023-03-01 11:42:57 +01:00
Armin Schrenk 4fc07c27b3 Merge branch 'develop' into feature/hub-local-access 2023-02-27 15:35:40 +01:00
Armin SchrenkandGitHub 3d0b6dde28 Update README.md 2023-02-27 13:09:56 +01:00
Armin Schrenk 45534b0a2b suppress false positive 2023-02-27 12:19:13 +01:00
Armin Schrenk 6d906beeb8 bump dependency-check 2023-02-27 12:17:47 +01:00
Tobias Hagemann e577c64c35 arm64 dmg installer (link + bg) for fuse-t 2023-02-27 11:06:19 +01:00
Armin Schrenk b848e78372 Merge branch 'develop' into release/1.7.0 2023-02-24 14:16:38 +01:00
Armin Schrenk 39e9395b4b bump fuse-nio-adapter 2023-02-24 14:16:20 +01:00
CryptobotandGitHub ca3157cc70 New Crowdin updates (#2695)
New translations strings.properties

Arabic; Belarusian; Bengali; Bosnian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Croatian; Czech; Danish; Dutch; Filipino; 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-02-24 14:15:50 +01:00
Armin Schrenk f94c1f07d6 add new wix extension to ci build 2023-02-24 13:23:10 +01:00
Armin Schrenk d43f197c11 fix 67e566832c 2023-02-24 13:14:14 +01:00
Armin Schrenk 67e566832c use correct path to get winfsp download url 2023-02-24 13:13:07 +01:00
Armin Schrenk 6f5a88af1d extend winfsp uninstall instruction by reboot step 2023-02-24 13:10:15 +01:00
Armin Schrenk 210b891616 block exe installation if winFsp is outdated 2023-02-24 12:50:20 +01:00
Armin Schrenk 20e820c903 update IDE specific files 2023-02-24 12:48:41 +01:00
Armin Schrenk 5307e93c1b bump fuse-nio-adapter
fix illegalArgumentException when setting loopbackHostName
2023-02-24 12:48:29 +01:00
Armin Schrenk ec1d25bf65 Merge branch 'develop' into feature/hub-local-access 2023-02-23 21:10:03 +01:00
Armin Schrenk 7aa554498b move new password files to changepassword package 2023-02-23 13:00:51 +01:00
Armin Schrenk 85ac3c244d rename forgetPassword package 2023-02-23 12:55:30 +01:00
Armin Schrenk 5db5346c88 refactor error component to own package 2023-02-23 12:53:29 +01:00
Armin Schrenk 8f4bf144c3 Merge branch 'develop' into feature/hub-local-access 2023-02-23 11:38:33 +01:00
Armin Schrenk 268c66a108 add hub specific tab to vault options 2023-02-21 23:18:00 +01:00
147 changed files with 3030 additions and 1438 deletions
+2 -2
View File
@@ -38,7 +38,7 @@ body:
attributes: attributes:
label: Volume Type label: Volume Type
description: What volume type is selected under Settings → Virtual Drive? description: What volume type is selected under Settings → Virtual Drive?
value: | options:
- WinFsp - WinFsp
- WinFsp (Local Drive) - WinFsp (Local Drive)
- FUSE-T - FUSE-T
@@ -49,7 +49,7 @@ body:
- WebDAV (HTTP Address) - WebDAV (HTTP Address)
- Dokany (1.5) - Dokany (1.5)
validations: validations:
required: true required: false
- type: textarea - type: textarea
id: reproduction-steps id: reproduction-steps
attributes: attributes:
-13
View File
@@ -1,13 +0,0 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: state:awaiting-response
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.
+29
View File
@@ -0,0 +1,29 @@
# .github/release.yml
# see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
changelog:
exclude:
authors:
- cryptobot
- dependabot
- github-actions
categories:
- title: What's New 🎉
labels:
- type:feature-request
- type:enhancement
- title: Bugfixes 🐛
labels:
- type:security-issue
- type:bug
- type:minor-bug
- title: Other Changes 📎
labels:
- "*"
exclude:
labels:
- type:feature-request
- type:enhancement
- type:security-issue
- type:bug
- type:minor-bug
-24
View File
@@ -1,24 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 365
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 90
# Issues with these labels will never be considered stale
exemptLabels:
- type:security-issue # never close automatically
- type:feature-request # never close automatically
- type:enhancement # never close automatically
- type:upstream-bug # never close automatically
- state:awaiting-response # handled by different bot
- state:blocked
- state:confirmed
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Label to use when marking an issue as stale
staleLabel: state:stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods" --module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr --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.zipfs
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
+4
View File
@@ -53,4 +53,8 @@ jobs:
body: |- body: |-
:construction: Work in Progress :construction: Work in Progress
Please be patient, the builds are still running. We will publish new versions of Cryptomator here in a few moments.
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
--- ---
<!-- Don't forget to include the 💾 SHA-256 checksums of release artifacts: -->
+2 -2
View File
@@ -20,8 +20,8 @@ on:
env: env:
JAVA_VERSION: 19 JAVA_VERSION: 19
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19/openjfx-19_linux-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19/openjfx-19_linux-aarch64_bin-jmods.zip' OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-aarch64_bin-jmods.zip'
jobs: jobs:
build: build:
+7 -5
View File
@@ -30,10 +30,12 @@ jobs:
architecture: x64 architecture: x64
output-suffix: x64 output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app' xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: macFUSE
- os: [self-hosted, macOS, ARM64] - os: [self-hosted, macOS, ARM64]
architecture: aarch64 architecture: aarch64
output-suffix: arm64 output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app' xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: FUSE-T
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
@@ -68,7 +70,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods" --module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -177,7 +179,7 @@ jobs:
run: | run: |
mkdir dmg mkdir dmg
mv Cryptomator.app dmg mv Cryptomator.app dmg
cp dist/mac/dmg/resources/macFUSE.webloc dmg cp dist/mac/dmg/resources/${{ matrix.fuse-lib }}.webloc dmg
ls -l dmg ls -l dmg
- name: Install create-dmg - name: Install create-dmg
run: | run: |
@@ -188,14 +190,14 @@ jobs:
create-dmg create-dmg
--volname Cryptomator --volname Cryptomator
--volicon "dist/mac/dmg/resources/Cryptomator-Volume.icns" --volicon "dist/mac/dmg/resources/Cryptomator-Volume.icns"
--background "dist/mac/dmg/resources/Cryptomator-background.tiff" --background "dist/mac/dmg/resources/Cryptomator-${{ matrix.fuse-lib }}-background.tiff"
--window-pos 400 100 --window-pos 400 100
--window-size 640 694 --window-size 640 694
--icon-size 128 --icon-size 128
--icon "Cryptomator.app" 128 245 --icon "Cryptomator.app" 128 245
--hide-extension "Cryptomator.app" --hide-extension "Cryptomator.app"
--icon "macFUSE.webloc" 320 501 --icon "${{ matrix.fuse-lib }}.webloc" 320 501
--hide-extension "macFUSE.webloc" --hide-extension "${{ matrix.fuse-lib }}.webloc"
--app-drop-link 512 245 --app-drop-link 512 245
--eula "dist/mac/dmg/resources/license.rtf" --eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758 --icon ".background" 128 758
+22
View File
@@ -0,0 +1,22 @@
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
name: 'Close awaiting response issues'
on:
schedule:
- cron: '00 09 * * *'
jobs:
no-response:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
with:
days-before-stale: 14
days-before-close: 0
days-before-pr-close: -1
stale-issue-label: 'state:stale'
close-issue-message: "This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further."
only-labels: 'state:awaiting-response'
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps: steps:
- name: Download source tarball - name: Download source tarball
run: | run: |
curl -L -H "Accept: application/vnd.github+json" ${{ github.event.release.tarball_url }} --output cryptomator-${{ github.event.release.tag_name }}.tar.gz curl -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
- name: Sign source tarball with key 615D449FE6E6A235 - name: Sign source tarball with key 615D449FE6E6A235
run: | run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
+24
View File
@@ -0,0 +1,24 @@
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
name: 'Close stale issues'
on:
schedule:
- cron: '00 09 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
with:
days-before-stale: 365
days-before-close: 90
exempt-issue-labels: 'type:security-issue,type:feature-request,type:enhancement,type:upstream-bug,state:awaiting-response,state:blocked,state:confirmed'
exempt-all-milestones: true
stale-issue-label: 'state:stale'
stale-pr-label: 'state:stale'
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
+40 -21
View File
@@ -8,11 +8,17 @@ on:
version: version:
description: 'Version' description: 'Version'
required: false required: false
isDebug:
description: 'Build debug version with console output'
type: boolean
env: env:
JAVA_VERSION: 19 JAVA_VERSION: 19
JAVA_DIST: 'zulu' JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven' JAVA_CACHE: 'maven'
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_windows-x64_bin-jmods.zip'
JFX_JMODS_HASH: 'B7CF2CAD2468842B3B78D99F6C0555771499A36FA1F1EE3DD1B9A4597F1FAB86'
defaults: defaults:
run: run:
@@ -30,6 +36,7 @@ jobs:
needs: [get-version] needs: [get-version]
env: env:
LOOPBACK_ALIAS: 'cryptomator-vault' LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
@@ -37,17 +44,31 @@ jobs:
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx' java-package: 'jdk'
cache: ${{ env.JAVA_CACHE }} cache: ${{ env.JAVA_CACHE }}
- name: Ensure major jfx version in pom equals in jdk - name: Download and extract JavaFX jmods from Gluon
shell: pwsh #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
run: | run: |
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\." curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\." if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) { exit 1;
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
} }
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
shell: pwsh
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1)
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
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_AMD64})"
exit 1
fi
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
@@ -61,13 +82,16 @@ jobs:
${JAVA_HOME}/bin/jlink ${JAVA_HOME}/bin/jlink
--verbose --verbose
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods" --module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
--strip-debug --strip-debug
--compress=1 --compress=1
- name: Change win-console flag if debug is active
if: ${{ inputs.isDebug }}
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
- name: Run jpackage - name: Run jpackage
run: > run: >
${JAVA_HOME}/bin/jpackage ${JAVA_HOME}/bin/jpackage
@@ -99,8 +123,10 @@ jobs:
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\"" --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
--resource-dir dist/win/resources --resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico --icon dist/win/resources/Cryptomator.ico
${WIN_CONSOLE_FLAG}
- name: Patch Application Directory - name: Patch Application Directory
run: | run: |
cp dist/win/contrib/* appdir/Cryptomator cp dist/win/contrib/* appdir/Cryptomator
@@ -203,15 +229,6 @@ jobs:
*.msi *.msi
*.asc *.asc
call-winget-flow:
needs: [get-version, build-msi]
if: github.event.action == 'published' && needs.get-version.outputs.versionType == 'stable'
uses: ./.github/workflows/winget.yml
with:
releaseTag: ${{ github.event.release.tag_name }}
secrets: inherit
build-exe: build-exe:
name: Build .exe installer name: Build .exe installer
runs-on: windows-latest runs-on: windows-latest
@@ -243,13 +260,14 @@ jobs:
shell: pwsh shell: pwsh
- name: Download WinFsp - name: Download WinFsp
run: | run: |
$winfspUrl= (Select-String -Path ".\dist\win\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value $winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
shell: pwsh shell: pwsh
- name: Compile to wixObj file - name: Compile to wixObj file
run: > run: >
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs "${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
-ext WixBalExtension -ext WixBalExtension
-ext WixUtilExtension
-out dist/win/bundle/ -out dist/win/bundle/
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" -dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
-dBundleVendor="Skymatic GmbH" -dBundleVendor="Skymatic GmbH"
@@ -261,6 +279,7 @@ jobs:
run: > run: >
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj "${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
-ext WixBalExtension -ext WixBalExtension
-ext WixUtilExtension
-out installer/unsigned/Cryptomator-Installer.exe -out installer/unsigned/Cryptomator-Installer.exe
- name: Detach burn engine in preparation to sign - name: Detach burn engine in preparation to sign
run: > run: >
-49
View File
@@ -1,49 +0,0 @@
name: Release to Winget
on:
workflow_call:
inputs:
releaseTag:
required: true
type: string
workflow_dispatch:
inputs:
releaseTag:
description: 'Release tag name'
required: true
type: string
jobs:
publish-winget:
name: Publish on winget repo
runs-on: windows-latest
steps:
- name: Get download url for release assets
id: get-release-assets
uses: actions/github-script@v6
with:
script: |
const query =`query($tag:String!) {
repository(owner:"cryptomator", name:"cryptomator"){
release(tagName: $tag) {
releaseAssets(first:20) {
nodes {
name
downloadUrl
}
}
}
}
}`;
const variables = {
tag: "${{ inputs.releaseTag }}"
}
return await github.graphql(query, variables)
- name: Submit package to Windows Package Manager Community Repository
id: submit-winget
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$releaseAssets = (ConvertFrom-Json '${{ steps.get-release-assets.outputs.result }}').repository.release.releaseAssets.nodes
$installerUrl = $releaseAssets | Where-Object -Property name -match '^Cryptomator-.*\.msi$' | Select -ExpandProperty downloadUrl -First 1
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v "${{ inputs.releaseTag }}" -u "$installerUrl" -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
shell: pwsh
+6 -4
View File
@@ -14,10 +14,10 @@
<option name="dagger.fastInit" value="enabled" /> <option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" /> <option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.44/dagger-compiler-2.44.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.45/dagger-compiler-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.44/dagger-2.44.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.45/dagger-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-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.44/dagger-producers-2.44.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.45/dagger-producers-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.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/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -26,14 +26,16 @@
<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/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$/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$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.44/dagger-spi-2.44.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/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$/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/1.7.0/kotlin-stdlib-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" /> <entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" /> <entry name="$MAVEN_REPOSITORY$/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/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-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-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/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/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" /> <entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
+1 -2
View File
@@ -21,7 +21,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
<tbody> <tbody>
<tr> <tr>
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td> <td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td>
<td><a href="https://proxy-hub.com/"><img src="https://cryptomator.org/img/sponsors/proxyhub.svg" alt="Proxy-Hub" height="80"></a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -74,7 +73,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
### Consistency ### Consistency
- HMAC over file contents to recognize changed ciphertext before decryption - Authenticated encryption is used for file content to recognize changed ciphertext before decryption
- I/O operations are transactional and atomic, if the filesystems support it - I/O operations are transactional and atomic, if the filesystems support it
- Each file contains all information needed for decryption (except for the key of course), no common metadata means no [SPOF](http://en.wikipedia.org/wiki/Single_point_of_failure) - Each file contains all information needed for decryption (except for the key of course), no common metadata means no [SPOF](http://en.wikipedia.org/wiki/Single_point_of_failure)
+1 -1
View File
@@ -24,7 +24,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \ --verbose \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods" \ --module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \ --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.zipfs \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -66,6 +66,13 @@
</content_rating> </content_rating>
<releases> <releases>
<release date="2023-04-25" version="1.8.0"/>
<release date="2023-04-07" version="1.7.5"/>
<release date="2023-04-05" version="1.7.4"/>
<release date="2023-03-15" version="1.7.3"/>
<release date="2023-03-07" version="1.7.2"/>
<release date="2023-03-03" version="1.7.1"/>
<release date="2023-03-01" version="1.7.0"/>
<release date="2022-12-14" version="1.6.17"/> <release date="2022-12-14" version="1.6.17"/>
<release date="2022-12-06" version="1.6.16"/> <release date="2022-12-06" version="1.6.16"/>
<release date="2022-10-06" version="1.6.15"/> <release date="2022-10-06" version="1.6.15"/>
+1 -1
View File
@@ -12,7 +12,7 @@ Package: cryptomator
Architecture: any Architecture: any
Section: utils Section: utils
Priority: optional Priority: optional
Depends: ${shlibs:Depends}, ${misc:Depends}, libfuse3-3 Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3
Recommends: gvfs-backends, gvfs-fuse, gnome-keyring Recommends: gvfs-backends, gvfs-fuse, gnome-keyring
XB-AppName: Cryptomator XB-AppName: Cryptomator
XB-Category: Utility;Security;FileTools; XB-Category: Utility;Security;FileTools;
+1 -1
View File
@@ -27,7 +27,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \ $(JAVA_HOME)/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JMODS_PATH}" \ --module-path "${JMODS_PATH}" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \ --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.zipfs \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
+6 -5
View File
@@ -27,6 +27,7 @@ MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator" MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
REVISION_NO=`git rev-list --count HEAD` REVISION_NO=`git rev-list --count HEAD`
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
FUSE_LIB="FUSE-T"
# check preconditions # check preconditions
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
@@ -45,7 +46,7 @@ cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
${JAVA_HOME}/bin/jlink \ ${JAVA_HOME}/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods" \ --module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \ --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -125,20 +126,20 @@ fi
# prepare dmg contents # prepare dmg contents
mkdir dmg mkdir dmg
mv ${APP_NAME}.app dmg mv ${APP_NAME}.app dmg
cp resources/macFUSE.webloc dmg cp resources/${FUSE_LIB}.webloc dmg
# create dmg # create dmg
create-dmg \ create-dmg \
--volname ${APP_NAME} \ --volname ${APP_NAME} \
--volicon "resources/${APP_NAME}-Volume.icns" \ --volicon "resources/${APP_NAME}-Volume.icns" \
--background "resources/${APP_NAME}-background.tiff" \ --background "resources/${APP_NAME}-${FUSE_LIB}-background.tiff" \
--window-pos 400 100 \ --window-pos 400 100 \
--window-size 640 694 \ --window-size 640 694 \
--icon-size 128 \ --icon-size 128 \
--icon "${APP_NAME}.app" 128 245 \ --icon "${APP_NAME}.app" 128 245 \
--hide-extension "${APP_NAME}.app" \ --hide-extension "${APP_NAME}.app" \
--icon "macFUSE.webloc" 320 501 \ --icon "${FUSE_LIB}.webloc" 320 501 \
--hide-extension "macFUSE.webloc" \ --hide-extension "${FUSE_LIB}.webloc" \
--app-drop-link 512 245 \ --app-drop-link 512 245 \
--eula "resources/license.rtf" \ --eula "resources/license.rtf" \
--icon ".background" 128 758 \ --icon ".background" 128 758 \
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>URL</key>
<string>https://www.fuse-t.org/</string>
</dict>
</plist>
+4 -4
View File
@@ -51,7 +51,7 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
--verbose ` --verbose `
--output runtime ` --output runtime `
--module-path "$Env:JAVA_HOME/jmods" ` --module-path "$Env:JAVA_HOME/jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr ` --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs `
--strip-native-commands ` --strip-native-commands `
--no-header-files ` --no-header-files `
--no-man-pages ` --no-man-pages `
@@ -153,7 +153,7 @@ $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
# download Winfsp # download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s $ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value $winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
Write-Output "Downloading ${winfspMsiUrl}..." Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
@@ -161,11 +161,11 @@ Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redir
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
# create bundle including winfsp # create bundle including winfsp
& "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ ` & "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -ext WixUtilextension -out bundle\ `
-dBundleVersion="$semVerNo.$revisionNo" ` -dBundleVersion="$semVerNo.$revisionNo" `
-dBundleVendor="$Vendor" ` -dBundleVendor="$Vendor" `
-dBundleCopyright="$copyright" ` -dBundleCopyright="$copyright" `
-dAboutUrl="$AboutUrl" ` -dAboutUrl="$AboutUrl" `
-dHelpUrl="$HelpUrl" ` -dHelpUrl="$HelpUrl" `
-dUpdateUrl="$UpdateUrl" -dUpdateUrl="$UpdateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\$AppName-Installer.exe & "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -ext WixUtilextension -out installer\$AppName-Installer.exe
+19 -1
View File
@@ -1,13 +1,31 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension"> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html--> <!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html-->
<!-- Attributes explicitly not used: <!-- Attributes explicitly not used:
Condition - the single msi files have their own install conditions, no need to copy them here Condition - the single msi files have their own install conditions, no need to copy them here
--> -->
<Bundle Name="Cryptomator" UpgradeCode="29eea626-2e5b-4449-b5f8-4602925ddf7b" Version="$(var.BundleVersion)" Manufacturer="$(var.BundleVendor)" <Bundle Name="Cryptomator" UpgradeCode="29eea626-2e5b-4449-b5f8-4602925ddf7b" Version="$(var.BundleVersion)" Manufacturer="$(var.BundleVendor)"
AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico"> AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico">
<!-- detect outdated WinFsp installations -->
<?include "resources\winFspMetaData.wxi" ?>
<util:ProductSearch
Variable="InstalledWinFspVersion"
Result="version"
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"
/>
<!-- Note: The bundle engine takes the Message format literaly -->
<bal:Condition Message=
"The WinFsp driver used by Cryptomator is outdated and must be removed before the installation.
1. Open the view of installed apps
2. Search for &quot;WinFsp&quot;
3. Uninstall the listed application
4. Reboot your device
5. Restart this installer">(InstalledWinFspVersion = v0.0.0.0) OR ($(var.BundledWinFspVersion) &lt;= InstalledWinFspVersion)</bal:Condition>
<!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/--> <!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/-->
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense"> <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense">
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/bal/wixstandardbootstrapperapplication.html --> <!-- see https://wixtoolset.org/documentation/manual/v3/xsd/bal/wixstandardbootstrapperapplication.html -->
+2 -1
View File
@@ -10,6 +10,7 @@
<Font Id="2" Height="-22" Weight="500" Foreground="666666">Segoe UI</Font> <Font Id="2" Height="-22" Weight="500" Foreground="666666">Segoe UI</Font>
<Font Id="3" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font> <Font Id="3" Height="-12" Weight="500" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
<Font Id="4" Height="-12" Weight="500" Foreground="ff0000" Background="FFFFFF" Underline="yes">Segoe UI</Font> <Font Id="4" Height="-12" Weight="500" Foreground="ff0000" Background="FFFFFF" Underline="yes">Segoe UI</Font>
<Font Id="5" Height="-12" Weight="700" Foreground="000000" Background="FFFFFF">Segoe UI</Font>
<Image X="11" Y="11" Width="64" Height="64" ImageFile="logo.png" /> <Image X="11" Y="11" Width="64" Height="64" ImageFile="logo.png" />
<Text X="80" Y="11" Width="-11" Height="64" FontId="1" DisablePrefix="yes">#(loc.Title)</Text> <Text X="80" Y="11" Width="-11" Height="64" FontId="1" DisablePrefix="yes">#(loc.Title)</Text>
@@ -82,7 +83,7 @@
<Text Name="FailureUninstallHeader" X="185" Y="50" Width="-11" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureUninstallHeader)</Text> <Text Name="FailureUninstallHeader" X="185" Y="50" Width="-11" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureUninstallHeader)</Text>
<Text Name="FailureRepairHeader" X="185" Y="50" Width="-11" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRepairHeader)</Text> <Text Name="FailureRepairHeader" X="185" Y="50" Width="-11" Height="30" FontId="2" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRepairHeader)</Text>
<Hypertext Name="FailureLogFileLink" X="185" Y="121" Width="-11" Height="68" FontId="3" TabStop="yes" HideWhenDisabled="yes">#(loc.FailureHyperlinkLogText)</Hypertext> <Hypertext Name="FailureLogFileLink" X="185" Y="121" Width="-11" Height="68" FontId="3" TabStop="yes" HideWhenDisabled="yes">#(loc.FailureHyperlinkLogText)</Hypertext>
<Hypertext Name="FailureMessageText" X="185" Y="-115" Width="-11" Height="80" FontId="3" TabStop="yes" HideWhenDisabled="yes" /> <Hypertext Name="FailureMessageText" X="185" Y="-80" Width="-11" Height="140" FontId="5" TabStop="yes" HideWhenDisabled="yes" />
<Text Name="FailureRestartText" X="185" Y="-57" Width="-11" Height="80" FontId="3" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRestartText)</Text> <Text Name="FailureRestartText" X="185" Y="-57" Width="-11" Height="80" FontId="3" HideWhenDisabled="yes" DisablePrefix="yes">#(loc.FailureRestartText)</Text>
<Button Name="FailureRestartButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FailureRestartButton)</Button> <Button Name="FailureRestartButton" X="-91" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0" HideWhenDisabled="yes">#(loc.FailureRestartButton)</Button>
<Button Name="FailureCloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.FailureCloseButton)</Button> <Button Name="FailureCloseButton" X="-11" Y="-11" Width="75" Height="23" TabStop="yes" FontId="0">#(loc.FailureCloseButton)</Button>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
<?define BundledWinFspVersion="v1.12.22339" ?>
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
</Include>
-2
View File
@@ -1,2 +0,0 @@
[InternetShortcut]
URL=https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi
+10 -47
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId> <artifactId>cryptomator</artifactId>
<version>1.7.0-SNAPSHOT</version> <version>1.9.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name> <name>Cryptomator Desktop App</name>
<organization> <organization>
@@ -17,6 +17,11 @@
<email>sebastian.stenzel@gmail.com</email> <email>sebastian.stenzel@gmail.com</email>
<timezone>+1</timezone> <timezone>+1</timezone>
</developer> </developer>
<developer>
<name>Armin Schrenk</name>
<email>armin.schrenk+dev@mailbox.org</email>
<timezone>+1</timezone>
</developer>
</developers> </developers>
<properties> <properties>
@@ -28,15 +33,14 @@
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds> <nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptolib.version>2.1.1</cryptomator.cryptolib.version> <cryptomator.cryptofs.version>2.6.2</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.6.1</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version> <cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version> <cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>2.0.0</cryptomator.fuse.version> <cryptomator.fuse.version>2.0.5</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version> <cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.0</cryptomator.webdav.version> <cryptomator.webdav.version>2.0.2</cryptomator.webdav.version>
<!-- 3rd party dependencies --> <!-- 3rd party dependencies -->
<commons-lang3.version>3.12.0</commons-lang3.version> <commons-lang3.version>3.12.0</commons-lang3.version>
@@ -59,18 +63,12 @@
<!-- build-time dependencies --> <!-- build-time dependencies -->
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version> <jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>7.4.4</dependency-check.version> <dependency-check.version>8.1.0</dependency-check.version>
<jacoco.version>0.8.8</jacoco.version> <jacoco.version>0.8.8</jacoco.version>
</properties> </properties>
<dependencies> <dependencies>
<!-- Cryptomator Libs --> <!-- Cryptomator Libs -->
<dependency>
<!-- needed due to https://github.com/cryptomator/cryptolib/issues/34 -->
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>${cryptomator.cryptolib.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId> <artifactId>cryptofs</artifactId>
@@ -319,41 +317,6 @@
</compilerArgs> </compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>compile-light-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/light_theme.css</arg>
<arg>${project.build.outputDirectory}/css/light_theme.bss</arg>
</arguments>
</configuration>
</execution>
<execution>
<id>compile-dark-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/dark_theme.css</arg>
<arg>${project.build.outputDirectory}/css/dark_theme.bss</arg>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -1,5 +1,9 @@
package org.cryptomator.common; package org.cryptomator.common;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import java.net.URI;
public interface Constants { public interface Constants {
String MASTERKEY_FILENAME = "masterkey.cryptomator"; String MASTERKEY_FILENAME = "masterkey.cryptomator";
@@ -7,6 +11,7 @@ public interface Constants {
String VAULTCONFIG_FILENAME = "vault.cryptomator"; String VAULTCONFIG_FILENAME = "vault.cryptomator";
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator"; String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator"; String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
byte[] PEPPER = new byte[0]; byte[] PEPPER = new byte[0];
} }
@@ -2,7 +2,9 @@ package org.cryptomator.common;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
public class ObservableUtil { public class ObservableUtil {
@@ -15,4 +17,14 @@ public class ObservableUtil {
} }
}, observable); }, observable);
} }
public static <T, U> ObservableValue<U> mapWithDefault(ObservableValue<T> observable, Function<? super T, ? extends U> mapper, Supplier<U> defaultValue) {
return Bindings.createObjectBinding(() -> {
if (observable.getValue() == null) {
return defaultValue.get();
} else {
return mapper.apply(observable.getValue());
}
}, observable);
}
} }
@@ -2,50 +2,71 @@ package org.cryptomator.common.mount;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountService; import org.cryptomator.integrations.mount.MountService;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@Module @Module
public class MountModule { public class MountModule {
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
@Provides @Provides
@Singleton @Singleton
static List<MountService> provideSupportedMountServices() { static List<MountService> provideSupportedMountServices() {
return MountService.get().toList(); return MountService.get().toList();
} }
//currently not used, because macFUSE and FUSE-T cannot be used in the same JVM
/*
@Provides @Provides
@Singleton @Singleton
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls) { @Named("FUPFMS")
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
return new AtomicReference<>(null);
}
@Provides
@Singleton
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null); var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
return ObservableUtil.mapWithDefault(settings.mountService(), //
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService(), //
desiredServiceImpl -> { // desiredServiceImpl -> { //
var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); // var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); // var targetedService = serviceFromSettings.orElse(fallbackProvider);
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
}, // }, //
new ActualMountService(fallbackProvider, true)); () -> { //
} return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
*/ });
return observableMountService;
@Provides
@Singleton
static ActualMountService provideActualMountService(Settings settings, List<MountService> serviceImpls) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(settings.mountService().getValue())).findFirst(); //
return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
} }
@Provides //see https://github.com/cryptomator/cryptomator/issues/2786
@Singleton private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) {
static ObservableValue<ActualMountService> provideMountService(ActualMountService service) { //set the first used problematic fuse service if applicable
return new SimpleObjectProperty<>(service); var targetIsProblematicFuse = isProblematicFuseService(targetedService);
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
firstUsedProblematicFuseMountService.set(targetedService);
} }
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
return new ActualMountService(formerSelectedMountService.get(), false);
} else {
formerSelectedMountService.set(targetedService);
return new ActualMountService(targetedService, isDesired);
}
}
public static boolean isProblematicFuseService(MountService service) {
return problematicFuseMountServices.contains(service.getClass().getName());
}
} }
@@ -2,6 +2,10 @@ package org.cryptomator.common.mount;
public class MountPointPreparationException extends RuntimeException { public class MountPointPreparationException extends RuntimeException {
public MountPointPreparationException(String msg) {
super(msg);
}
public MountPointPreparationException(Throwable cause) { public MountPointPreparationException(Throwable cause) {
super(cause); super(cause);
} }
@@ -51,8 +51,19 @@ public final class MountWithinParentUtil {
if (SystemUtils.IS_OS_WINDOWS) { if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS); Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
} }
int attempts = 0;
while (!Files.notExists(mountPoint)) {
if (attempts >= 10) {
throw new MountPointPreparationException("Path " + mountPoint + " could not be cleared");
}
Thread.sleep(1000);
attempts++;
}
} catch (IOException e) { } catch (IOException e) {
throw new MountPointPreparationException(e); throw new MountPointPreparationException(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new MountPointPreparationException(e);
} }
} }
} }
@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javafx.application.Platform;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
@@ -40,6 +41,7 @@ public class AutoLocker {
private void autolock(Vault vault) { private void autolock(Vault vault) {
try { try {
vault.lock(false); vault.lock(false);
Platform.runLater(() -> vault.stateProperty().set(VaultState.Value.LOCKED));
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName()); LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
} catch (UnmountFailedException | IOException e) { } catch (UnmountFailedException | IOException e) {
LOG.error("Autolocking failed.", e); LOG.error("Autolocking failed.", e);
@@ -1,39 +1,95 @@
package org.cryptomator.launcher; package org.cryptomator.launcher;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.stream.StreamSupport;
@Singleton @Singleton
public class SupportedLanguages { public class SupportedLanguages {
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_": public static final String ENGLISH = "en";
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fil", "fa", "fr", "gl", "he", //
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "si", "sk", "sr", "sr-Latn", "sv", "sw", //
"ta", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
@Nullable private final List<String> sortedLanguageTags;
private final String preferredLanguage; private final Locale preferredLocale;
@Inject @Inject
public SupportedLanguages(Settings settings) { public SupportedLanguages(Settings settings) {
this.preferredLanguage = settings.languageProperty().get(); var preferredLanguage = settings.languageProperty().get();
preferredLocale = preferredLanguage == null ? Locale.getDefault() : Locale.forLanguageTag(preferredLanguage);
var collator = Collator.getInstance(preferredLocale);
collator.setStrength(Collator.PRIMARY);
List<String> sortedTags = new ArrayList<>();
sortedTags.add(0, Settings.DEFAULT_LANGUAGE);
sortedTags.add(1, ENGLISH);
getSupportedLanguageTags().stream() //
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
.forEach(sortedTags::add);
sortedLanguageTags = Collections.unmodifiableList(sortedTags);
} }
public void applyPreferred() { public void applyPreferred() {
if (preferredLanguage == null) { LOG.debug("Using locale {}", preferredLocale);
LOG.debug("Using system locale");
return;
}
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
Locale.setDefault(preferredLocale); Locale.setDefault(preferredLocale);
} }
public List<String> getLanguageTags() {
return sortedLanguageTags;
}
/**
* Iterates over the /i18n directory and extracts from every localization file the BCP 47 code.
*
* @return list of supported BCP 47 language codes
*/
private static List<String> getSupportedLanguageTags() {
try {
var i18DirURI = SupportedLanguages.class.getResource("/i18n").toURI();
var uriScheme = i18DirURI.getScheme();
if (uriScheme.equals("jar")) {
final String[] array = i18DirURI.toString().split("!");
try (var jarFs = FileSystems.newFileSystem(URI.create(array[0]), Map.<String, String>of())) {
return streamDirectory(jarFs.getPath(array[1]));
}
} else if (uriScheme.equals("file")) {
return streamDirectory(Path.of(i18DirURI));
} else {
throw new IOException("Unsupported uri scheme: " + uriScheme);
}
} catch (URISyntaxException | IOException e) {
LOG.warn("Unable to determine additional supported languages.", e);
return List.of();
}
}
private static List<String> streamDirectory(Path i18Dir) throws IOException {
try (var dirStream = Files.newDirectoryStream(i18Dir, "strings_*.properties")) {
return StreamSupport.stream(dirStream.spliterator(), false) //
.map(SupportedLanguages::getBCP47CodeFromFileName) //
.toList();
}
}
private static String getBCP47CodeFromFileName(Path p) {
var fileName = p.getFileName().toString();
return fileName.substring("strings_".length(), fileName.indexOf(".properties")).replace('_', '-');
}
} }
@@ -11,8 +11,8 @@ import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil; import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController; import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
@@ -13,7 +13,7 @@ import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.common.Tasks;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
@@ -48,13 +48,13 @@ import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@AddVaultWizardScoped @AddVaultWizardScoped
public class CreateNewVaultPasswordController implements FxController { public class CreateNewVaultPasswordController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class); private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class);
private static final URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); // TODO better place?
private final Stage window; private final Stage window;
private final Lazy<Scene> chooseLocationScene; private final Lazy<Scene> chooseLocationScene;
@@ -9,7 +9,6 @@ import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -10,8 +10,6 @@ import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import javax.inject.Named; import javax.inject.Named;
@@ -1,5 +1,7 @@
package org.cryptomator.ui.common; package org.cryptomator.ui.changepassword;
import org.cryptomator.common.Passphrase;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.controls.NiceSecurePasswordField;
@@ -91,4 +93,8 @@ public class NewPasswordController implements FxController {
return passwordStrength.get(); return passwordStrength.get();
} }
public Passphrase getNewPassword() {
return passwordField.getCharacters();
}
} }
@@ -6,7 +6,7 @@
* Contributors: * Contributors:
* Jean-Noël Charon - initial API and implementation * Jean-Noël Charon - initial API and implementation
*******************************************************************************/ *******************************************************************************/
package org.cryptomator.ui.common; package org.cryptomator.ui.changepassword;
import com.nulabinc.zxcvbn.Zxcvbn; import com.nulabinc.zxcvbn.Zxcvbn;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
@@ -9,6 +9,9 @@ public enum FxmlFile {
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), // ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), // ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
CHANGEPASSWORD("/fxml/changepassword.fxml"), // CHANGEPASSWORD("/fxml/changepassword.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
ERROR("/fxml/error.fxml"), // ERROR("/fxml/error.fxml"), //
FORGET_PASSWORD("/fxml/forget_password.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), //
HEALTH_START("/fxml/health_start.fxml"), // HEALTH_START("/fxml/health_start.fxml"), //
@@ -18,6 +18,7 @@ public enum FontAwesome5Icon {
COPY("\uF0C5"), // COPY("\uF0C5"), //
CROWN("\uF521"), // CROWN("\uF521"), //
EDIT("\uF044"), // EDIT("\uF044"), //
EXCHANGE_ALT("\uF362"), //
EXCLAMATION("\uF12A"), // EXCLAMATION("\uF12A"), //
EXCLAMATION_CIRCLE("\uF06A"), // EXCLAMATION_CIRCLE("\uF06A"), //
EXCLAMATION_TRIANGLE("\uF071"), // EXCLAMATION_TRIANGLE("\uF071"), //
@@ -43,7 +43,7 @@ public class SecurePasswordField extends TextField {
private static final char WIPE_CHAR = ' '; private static final char WIPE_CHAR = ' ';
private static final int INITIAL_BUFFER_SIZE = 50; private static final int INITIAL_BUFFER_SIZE = 50;
private static final int GROW_BUFFER_SIZE = 50; private static final int GROW_BUFFER_SIZE = 50;
private static final String DEFAULT_PLACEHOLDER = "●"; private static final String DEFAULT_PLACEHOLDER = "•";
private static final String STYLE_CLASS = "secure-password-field"; private static final String STYLE_CLASS = "secure-password-field";
private static final KeyCodeCombination SHORTCUT_BACKSPACE = new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCombination.SHORTCUT_DOWN); private static final KeyCodeCombination SHORTCUT_BACKSPACE = new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCombination.SHORTCUT_DOWN);
@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.ui.convertvault;
import dagger.BindsInstance;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named;
import javafx.scene.Scene;
import javafx.stage.Stage;
@ConvertVaultScoped
@Subcomponent(modules = {ConvertVaultModule.class})
public interface ConvertVaultComponent {
@ConvertVaultWindow
Stage window();
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START)
Lazy<Scene> hubToPasswordScene();
default void showHubToPasswordWindow() {
Stage stage = window();
stage.setScene(hubToPasswordScene().get());
stage.sizeToScene();
stage.show();
}
@Subcomponent.Factory
interface Factory {
ConvertVaultComponent create(@BindsInstance @ConvertVaultWindow Vault vault, @BindsInstance @Named("convertVaultOwner") Stage owner);
}
}
@@ -0,0 +1,126 @@
package org.cryptomator.ui.convertvault;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class ConvertVaultModule {
//TODO: if this fails, we cannot display an error
@Provides
@ConvertVaultWindow
@ConvertVaultScoped
static VaultConfig.UnverifiedVaultConfig vaultConfig(@ConvertVaultWindow Vault vault) {
try {
return vault.getVaultConfigCache().get();
} catch (IOException e) {
return null;
}
}
@Provides
@ConvertVaultWindow
@ConvertVaultScoped
static StringProperty provideRecoveryKeyProperty() {
return new SimpleStringProperty();
}
@Provides
@ConvertVaultWindow
@ConvertVaultScoped
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@ConvertVaultWindow
@ConvertVaultScoped
static Stage provideStage(StageFactory factory, @Named("convertVaultOwner") Stage owner, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(owner);
stage.setTitle(resourceBundle.getString("convertVault.title"));
return stage;
}
@Provides
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START)
@ConvertVaultScoped
static Scene provideHubToPasswordStartScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START);
}
@Provides
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_CONVERT)
@ConvertVaultScoped
static Scene provideHubToPasswordConvertScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_CONVERT);
}
@Provides
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_SUCCESS)
@ConvertVaultScoped
static Scene provideHubToPasswordSuccessScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_SUCCESS);
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(HubToPasswordStartController.class)
abstract FxController bindHubToPasswordStartController(HubToPasswordStartController controller);
@Binds
@IntoMap
@FxControllerKey(HubToPasswordConvertController.class)
abstract FxController bindHubToPasswordConvertController(HubToPasswordConvertController controller);
@Binds
@IntoMap
@FxControllerKey(HubToPasswordSuccessController.class)
abstract FxController bindHubToPasswordSuccessController(HubToPasswordSuccessController controller);
@Provides
@IntoMap
@FxControllerKey(NewPasswordController.class)
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
return new NewPasswordController(resourceBundle, strengthRater);
}
@Provides
@IntoMap
@FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
}
}
@@ -0,0 +1,13 @@
package org.cryptomator.ui.convertvault;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertVaultScoped {
}
@@ -0,0 +1,14 @@
package org.cryptomator.ui.convertvault;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface ConvertVaultWindow {
}
@@ -0,0 +1,171 @@
package org.cryptomator.ui.convertvault;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.Constants;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultVersionMismatchException;
import org.cryptomator.cryptofs.common.BackupHelper;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.common.FxController;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.stage.Stage;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
public class HubToPasswordConvertController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(HubToPasswordConvertController.class);
private final Stage window;
private final Lazy<Scene> successScene;
private final FxApplicationWindows applicationWindows;
private final Vault vault;
private final StringProperty recoveryKey;
private final RecoveryKeyFactory recoveryKeyFactory;
private final MasterkeyFileAccess masterkeyFileAccess;
private final ExecutorService backgroundExecutorService;
private final ResourceBundle resourceBundle;
private final BooleanProperty conversionStarted;
@FXML
NewPasswordController newPasswordController;
public Button convertBtn;
@Inject
public HubToPasswordConvertController(@ConvertVaultWindow Stage window, @FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows applicationWindows, @ConvertVaultWindow Vault vault, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, MasterkeyFileAccess masterkeyFileAccess, ExecutorService backgroundExecutorService, ResourceBundle resourceBundle) {
this.window = window;
this.successScene = successScene;
this.applicationWindows = applicationWindows;
this.vault = vault;
this.recoveryKey = recoveryKey;
this.recoveryKeyFactory = recoveryKeyFactory;
this.masterkeyFileAccess = masterkeyFileAccess;
this.backgroundExecutorService = backgroundExecutorService;
this.resourceBundle = resourceBundle;
this.conversionStarted = new SimpleBooleanProperty(false);
}
@FXML
public void initialize() {
convertBtn.disableProperty().bind(Bindings.createBooleanBinding( //
() -> !newPasswordController.isGoodPassword() || conversionStarted.get(), //
newPasswordController.goodPasswordProperty(), //
conversionStarted));
convertBtn.contentDisplayProperty().bind(Bindings.createObjectBinding( //
() -> conversionStarted.getValue() ? ContentDisplay.LEFT : ContentDisplay.TEXT_ONLY, //
conversionStarted));
convertBtn.textProperty().bind(Bindings.createStringBinding( //
() -> resourceBundle.getString("convertVault.convert.convertBtn." + (conversionStarted.get() ? "processing" : "before")), //
conversionStarted));
}
@FXML
public void close() {
window.close();
}
@FXML
public void convert() {
Preconditions.checkState(newPasswordController.isGoodPassword());
LOG.info("Converting access method of vault {} from hub to password", vault.getPath());
CompletableFuture.runAsync(() -> conversionStarted.setValue(true), Platform::runLater) //
.thenRunAsync(this::convertInternal, backgroundExecutorService) //
.whenCompleteAsync((result, exception) -> {
if (exception == null) {
LOG.info("Conversion of vault {} succeeded.", vault.getPath());
window.setScene(successScene.get());
} else {
LOG.error("Conversion of vault {} failed.", vault.getPath(), exception);
applicationWindows.showErrorWindow(exception, window, null);
}
}, Platform::runLater); //
}
//visible for testing
void convertInternal() throws CompletionException, IllegalArgumentException {
var passphrase = newPasswordController.getNewPassword();
var vaultPath = vault.getPath();
try {
//create masterkey
recoveryKeyFactory.newMasterkeyFileWithPassphrase(vaultPath, recoveryKey.get(), passphrase);
LOG.debug("Successfully created masterkey file for vault {}", vaultPath);
//create password config
Path passwordConfigPath = vaultPath.resolve("passwordBased." + VAULTCONFIG_FILENAME + ".tmp");
passwordConfigPath = createPasswordConfig(passwordConfigPath, vaultPath.resolve(MASTERKEY_FILENAME), passphrase);
//backup hub config
var hubConfigPath = vaultPath.resolve(VAULTCONFIG_FILENAME);
backupHubConfig(hubConfigPath);
//replace hub by password
Files.move(passwordConfigPath, hubConfigPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (MasterkeyLoadingFailedException e) {
throw new CompletionException(new IOException("Vault conversion failed", e));
} catch (IOException e) {
throw new CompletionException("Vault conversion failed", e);
} finally {
passphrase.destroy();
}
}
//visible for testing
void backupHubConfig(Path hubConfigPath) throws IOException {
byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath);
Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX);
Files.copy(hubConfigPath, backupPath, StandardCopyOption.REPLACE_EXISTING);
LOG.debug("Successfully created hub config backup {}", backupPath.getFileName());
}
//visible for testing
Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException {
var unverifiedVaultConfig = vault.getVaultConfigCache().get();
try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
var hubConfig = unverifiedVaultConfig.verify(masterkey.getEncoded(), unverifiedVaultConfig.allegedVaultVersion());
var passwordConfig = VaultConfig.createNew() //
.cipherCombo(hubConfig.getCipherCombo()) //
.shorteningThreshold(hubConfig.getShorteningThreshold()) //
.build();
if (passwordConfig.getVaultVersion() != hubConfig.getVaultVersion()) {
throw new VaultVersionMismatchException("Only vaults of version " + passwordConfig.getVaultVersion() + " can be converted.");
}
var token = passwordConfig.toToken(Constants.DEFAULT_KEY_ID.toString(), masterkey.getEncoded());
Files.writeString(passwordConfigPath, token, StandardCharsets.US_ASCII, WRITE, CREATE_NEW);
LOG.debug("Successfully created password config {}", passwordConfigPath);
return passwordConfigPath;
}
}
}
@@ -0,0 +1,47 @@
package org.cryptomator.ui.convertvault;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class HubToPasswordStartController implements FxController {
private final Stage window;
private final Lazy<Scene> convertScene;
@FXML
RecoveryKeyValidateController recoveryKeyValidateController;
@Inject
public HubToPasswordStartController(@ConvertVaultWindow Stage window, @FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_CONVERT) Lazy<Scene> convertScene) {
this.window = window;
this.convertScene = convertScene;
}
@FXML
public void initialize() {
}
@FXML
public void close() {
window.close();
}
@FXML
public void next() {
window.setScene(convertScene.get());
}
/* Getter/Setter */
public RecoveryKeyValidateController getValidateController() {
return recoveryKeyValidateController;
}
}
@@ -0,0 +1,32 @@
package org.cryptomator.ui.convertvault;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
public class HubToPasswordSuccessController implements FxController {
private final Stage window;
private final Vault vault;
@Inject
HubToPasswordSuccessController(@ConvertVaultWindow Stage window, @ConvertVaultWindow Vault vault) {
this.window = window;
this.vault = vault;
}
@FXML
public void close() {
window.close();
window.getOwner().hide();
}
/* Observables */
public Vault getVault() {
return vault;
}
}
@@ -1,8 +1,10 @@
package org.cryptomator.ui.common; package org.cryptomator.ui.error;
import dagger.BindsInstance; import dagger.BindsInstance;
import dagger.Subcomponent; import dagger.Subcomponent;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -1,8 +1,9 @@
package org.cryptomator.ui.common; package org.cryptomator.ui.error;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.cryptomator.common.ErrorCode; import org.cryptomator.common.ErrorCode;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@@ -1,10 +1,16 @@
package org.cryptomator.ui.common; package org.cryptomator.ui.error;
import dagger.Binds; import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import org.cryptomator.common.ErrorCode; import org.cryptomator.common.ErrorCode;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
@@ -1,4 +1,4 @@
package org.cryptomator.ui.forgetPassword; package org.cryptomator.ui.forgetpassword;
import dagger.BindsInstance; import dagger.BindsInstance;
import dagger.Lazy; import dagger.Lazy;
@@ -1,4 +1,4 @@
package org.cryptomator.ui.forgetPassword; package org.cryptomator.ui.forgetpassword;
import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
@@ -1,4 +1,4 @@
package org.cryptomator.ui.forgetPassword; package org.cryptomator.ui.forgetpassword;
import dagger.Binds; import dagger.Binds;
import dagger.Module; import dagger.Module;
@@ -1,4 +1,4 @@
package org.cryptomator.ui.forgetPassword; package org.cryptomator.ui.forgetpassword;
import javax.inject.Scope; import javax.inject.Scope;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
@@ -1,4 +1,4 @@
package org.cryptomator.ui.forgetPassword; package org.cryptomator.ui.forgetpassword;
import javax.inject.Qualifier; import javax.inject.Qualifier;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
@@ -7,9 +7,7 @@ package org.cryptomator.ui.fxapp;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
@@ -18,13 +16,9 @@ import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent; import org.cryptomator.ui.unlock.UnlockComponent;
import javax.inject.Named;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class}) @Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
abstract class FxApplicationModule { abstract class FxApplicationModule {
@@ -5,7 +5,7 @@ import dagger.Lazy;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider; import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
@@ -15,8 +15,6 @@ import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped; import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
@@ -153,13 +151,6 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(AuthFlowController.class) @FxControllerKey(AuthFlowController.class)
abstract FxController bindAuthFlowController(AuthFlowController controller); abstract FxController bindAuthFlowController(AuthFlowController controller);
@Provides
@IntoMap
@FxControllerKey(NewPasswordController.class)
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
return new NewPasswordController(resourceBundle, strengthRater);
}
@Binds @Binds
@IntoMap @IntoMap
@FxControllerKey(InvalidLicenseController.class) @FxControllerKey(InvalidLicenseController.class)
@@ -29,8 +29,8 @@ import java.util.concurrent.ExecutionException;
public class HubKeyLoadingStrategy implements KeyLoadingStrategy { public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
private static final String SCHEME_PREFIX = "hub+"; private static final String SCHEME_PREFIX = "hub+";
static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http"; public static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http";
static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https"; public static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https";
private final Stage window; private final Stage window;
private final KeychainManager keychainManager; private final KeychainManager keychainManager;
@@ -8,7 +8,7 @@ import dagger.multibindings.StringKey;
import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped; import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
@@ -7,7 +7,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.WeakBindings; import org.cryptomator.ui.common.WeakBindings;
import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoading;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -6,7 +6,7 @@ import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
@@ -1,6 +1,5 @@
package org.cryptomator.ui.preferences; package org.cryptomator.ui.preferences;
import com.google.common.base.Strings;
import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme; import org.cryptomator.common.settings.UiTheme;
@@ -35,6 +34,7 @@ public class InterfacePreferencesController implements FxController {
private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty; private final ObjectProperty<SelectedPreferencesTab> selectedTabProperty;
private final LicenseHolder licenseHolder; private final LicenseHolder licenseHolder;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final SupportedLanguages supportedLanguages;
public ChoiceBox<UiTheme> themeChoiceBox; public ChoiceBox<UiTheme> themeChoiceBox;
public CheckBox showMinimizeButtonCheckbox; public CheckBox showMinimizeButtonCheckbox;
public CheckBox showTrayIconCheckbox; public CheckBox showTrayIconCheckbox;
@@ -44,13 +44,14 @@ public class InterfacePreferencesController implements FxController {
public RadioButton nodeOrientationRtl; public RadioButton nodeOrientationRtl;
@Inject @Inject
InterfacePreferencesController(Settings settings, TrayMenuComponent trayMenu, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle) { InterfacePreferencesController(Settings settings, SupportedLanguages supportedLanguages, TrayMenuComponent trayMenu, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle) {
this.settings = settings; this.settings = settings;
this.trayMenuInitialized = trayMenu.isInitialized(); this.trayMenuInitialized = trayMenu.isInitialized();
this.trayMenuSupported = trayMenu.isSupported(); this.trayMenuSupported = trayMenu.isSupported();
this.selectedTabProperty = selectedTabProperty; this.selectedTabProperty = selectedTabProperty;
this.licenseHolder = licenseHolder; this.licenseHolder = licenseHolder;
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.supportedLanguages = supportedLanguages;
} }
@FXML @FXML
@@ -66,8 +67,7 @@ public class InterfacePreferencesController implements FxController {
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon()); showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon());
preferredLanguageChoiceBox.getItems().add(null); preferredLanguageChoiceBox.getItems().addAll(supportedLanguages.getLanguageTags());
preferredLanguageChoiceBox.getItems().addAll(SupportedLanguages.LANGUAGAE_TAGS);
preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.languageProperty()); preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.languageProperty());
preferredLanguageChoiceBox.setConverter(new LanguageTagConverter(resourceBundle)); preferredLanguageChoiceBox.setConverter(new LanguageTagConverter(resourceBundle));
@@ -141,9 +141,7 @@ public class InterfacePreferencesController implements FxController {
return resourceBundle.getString("preferences.interface.language.auto"); return resourceBundle.getString("preferences.interface.language.auto");
} else { } else {
var locale = Locale.forLanguageTag(tag); var locale = Locale.forLanguageTag(tag);
var lang = locale.getDisplayLanguage(locale); return locale.getDisplayName();
var region = locale.getDisplayCountry(locale);
return lang + (Strings.isNullOrEmpty(region) ? "" : " (" + region + ")");
} }
} }
@@ -2,12 +2,14 @@ package org.cryptomator.ui.preferences;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.mount.MountModule;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.MountCapability; import org.cryptomator.integrations.mount.MountCapability;
import org.cryptomator.integrations.mount.MountService; import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application; import javafx.application.Application;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanExpression; import javafx.beans.binding.BooleanExpression;
@@ -19,11 +21,12 @@ import javafx.util.StringConverter;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicReference;
@PreferencesScoped @PreferencesScoped
public class VolumePreferencesController implements FxController { public class VolumePreferencesController implements FxController {
private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/vault-mounting/"; private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
private final Settings settings; private final Settings settings;
private final ObservableValue<MountService> selectedMountService; private final ObservableValue<MountService> selectedMountService;
@@ -33,6 +36,7 @@ public class VolumePreferencesController implements FxController {
private final ObservableValue<Boolean> mountToDriveLetterSupported; private final ObservableValue<Boolean> mountToDriveLetterSupported;
private final ObservableValue<Boolean> mountFlagsSupported; private final ObservableValue<Boolean> mountFlagsSupported;
private final ObservableValue<Boolean> readonlySupported; private final ObservableValue<Boolean> readonlySupported;
private final ObservableValue<Boolean> fuseRestartRequired;
private final Lazy<Application> application; private final Lazy<Application> application;
private final List<MountService> mountProviders; private final List<MountService> mountProviders;
public ChoiceBox<MountService> volumeTypeChoiceBox; public ChoiceBox<MountService> volumeTypeChoiceBox;
@@ -40,7 +44,7 @@ public class VolumePreferencesController implements FxController {
public Button loopbackPortApplyButton; public Button loopbackPortApplyButton;
@Inject @Inject
VolumePreferencesController(Settings settings, Lazy<Application> application, List<MountService> mountProviders, ResourceBundle resourceBundle) { VolumePreferencesController(Settings settings, Lazy<Application> application, List<MountService> mountProviders, @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) {
this.settings = settings; this.settings = settings;
this.application = application; this.application = application;
this.mountProviders = mountProviders; this.mountProviders = mountProviders;
@@ -53,6 +57,12 @@ public class VolumePreferencesController implements FxController {
this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER)); this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS)); this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY)); this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
this.fuseRestartRequired = selectedMountService.map(s -> {//
return firstUsedProblematicFuseMountService.get() != null //
&& MountModule.isProblematicFuseService(s) //
&& !firstUsedProblematicFuseMountService.get().equals(s);
});
} }
public void initialize() { public void initialize() {
@@ -129,6 +139,14 @@ public class VolumePreferencesController implements FxController {
return mountFlagsSupported.getValue(); return mountFlagsSupported.getValue();
} }
public ObservableValue<Boolean> fuseRestartRequiredProperty() {
return fuseRestartRequired;
}
public boolean getFuseRestartRequired() {
return fuseRestartRequired.getValue();
}
/* Helpers */ /* Helpers */
private class MountServiceConverter extends StringConverter<MountService> { private class MountServiceConverter extends StringConverter<MountService> {
@@ -38,16 +38,11 @@ public interface RecoveryKeyComponent {
stage.show(); stage.show();
} }
@Subcomponent.Builder
interface Builder {
@BindsInstance @Subcomponent.Factory
Builder vault(@RecoveryKeyWindow Vault vault); interface Factory {
@BindsInstance RecoveryKeyComponent create(@BindsInstance @RecoveryKeyWindow Vault vault, @BindsInstance @Named("keyRecoveryOwner") Stage owner);
Builder owner(@Named("keyRecoveryOwner") Stage owner);
RecoveryKeyComponent build();
} }
} }
@@ -81,7 +81,7 @@ public class RecoveryKeyFactory {
* @throws IllegalArgumentException If the recoveryKey is invalid * @throws IllegalArgumentException If the recoveryKey is invalid
* @apiNote This is a long-running operation and should be invoked in a background thread * @apiNote This is a long-running operation and should be invoked in a background thread
*/ */
public void resetPasswordWithRecoveryKey(Path vaultPath, String recoveryKey, CharSequence newPassword) throws IOException, IllegalArgumentException { public void newMasterkeyFileWithPassphrase(Path vaultPath, String recoveryKey, CharSequence newPassword) throws IOException, IllegalArgumentException {
final byte[] rawKey = decodeRecoveryKey(recoveryKey); final byte[] rawKey = decodeRecoveryKey(recoveryKey);
try (var masterkey = new Masterkey(rawKey)) { try (var masterkey = new Masterkey(rawKey)) {
Path masterkeyPath = vaultPath.resolve(MASTERKEY_FILENAME); Path masterkeyPath = vaultPath.resolve(MASTERKEY_FILENAME);
@@ -13,8 +13,8 @@ import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil; import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import javax.inject.Named; import javax.inject.Named;
@@ -140,6 +140,13 @@ abstract class RecoveryKeyModule {
@FxControllerKey(RecoveryKeyResetPasswordSuccessController.class) @FxControllerKey(RecoveryKeyResetPasswordSuccessController.class)
abstract FxController bindRecoveryKeyResetPasswordSuccessController(RecoveryKeyResetPasswordSuccessController controller); abstract FxController bindRecoveryKeyResetPasswordSuccessController(RecoveryKeyResetPasswordSuccessController controller);
@Provides
@IntoMap
@FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
}
@Provides @Provides
@IntoMap @IntoMap
@FxControllerKey(NewPasswordController.class) @FxControllerKey(NewPasswordController.class)
@@ -1,14 +1,9 @@
package org.cryptomator.ui.recoverykey; package org.cryptomator.ui.recoverykey;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
@@ -16,96 +11,34 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.ObjectProperty; import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@RecoveryKeyScoped @RecoveryKeyScoped
public class RecoveryKeyRecoverController implements FxController { public class RecoveryKeyRecoverController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class); private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class);
private static final CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' '));
private final Stage window; private final Stage window;
private final Vault vault;
private final VaultConfig.UnverifiedVaultConfig unverifiedVaultConfig;
private final StringProperty recoveryKey;
private final ObservableValue<Boolean> recoveryKeyCorrect;
private final ObservableValue<Boolean> recoveryKeyWrong;
private final ObservableValue<Boolean> recoveryKeyInvalid;
private final RecoveryKeyFactory recoveryKeyFactory;
private final ObjectProperty<RecoveryKeyState> recoveryKeyState;
private final Lazy<Scene> resetPasswordScene; private final Lazy<Scene> resetPasswordScene;
private final AutoCompleter autoCompleter;
private volatile boolean isWrongKey; @FXML
RecoveryKeyValidateController recoveryKeyValidateController;
public TextArea textarea;
@Inject @Inject
public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig unverifiedVaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, ResourceBundle resourceBundle) { public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, ResourceBundle resourceBundle) {
this.window = window; this.window = window;
window.setTitle(resourceBundle.getString("recoveryKey.recover.title")); window.setTitle(resourceBundle.getString("recoveryKey.recover.title"));
this.vault = vault;
this.unverifiedVaultConfig = unverifiedVaultConfig;
this.recoveryKey = recoveryKey;
this.recoveryKeyFactory = recoveryKeyFactory;
this.resetPasswordScene = resetPasswordScene; this.resetPasswordScene = resetPasswordScene;
this.autoCompleter = new AutoCompleter(recoveryKeyFactory.getDictionary());
this.recoveryKeyState = new SimpleObjectProperty<>();
this.recoveryKeyCorrect = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.CORRECT::equals, false);
this.recoveryKeyWrong = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.WRONG::equals, false);
this.recoveryKeyInvalid = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.INVALID::equals, false);
} }
@FXML @FXML
public void initialize() { public void initialize() {
recoveryKey.bind(textarea.textProperty());
textarea.textProperty().addListener(((observable, oldValue, newValue) -> validateRecoveryKey()));
}
private TextFormatter.Change filterTextChange(TextFormatter.Change change) {
if (Strings.isNullOrEmpty(change.getText())) {
// pass-through caret/selection changes that don't affect the text
return change;
}
if (!ALLOWED_CHARS.matchesAllOf(change.getText())) {
return null; // reject change
}
String text = change.getControlNewText();
int caretPos = change.getCaretPosition();
if (caretPos == text.length() || text.charAt(caretPos) == ' ') { // are we at the end of a word?
int beginOfWord = Math.max(text.substring(0, caretPos).lastIndexOf(' ') + 1, 0);
String currentWord = text.substring(beginOfWord, caretPos);
Optional<String> suggestion = autoCompleter.autocomplete(currentWord);
if (suggestion.isPresent()) {
String completion = suggestion.get().substring(currentWord.length());
change.setText(change.getText() + completion);
change.setAnchor(caretPos + completion.length());
}
}
return change;
}
@FXML
public void onKeyPressed(KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.TAB && textarea.getAnchor() > textarea.getCaretPosition()) {
// apply autocompletion:
int pos = textarea.getAnchor();
textarea.insertText(pos, " ");
textarea.positionCaret(pos + 1);
}
} }
@FXML @FXML
@@ -118,85 +51,10 @@ public class RecoveryKeyRecoverController implements FxController {
window.setScene(resetPasswordScene.get()); window.setScene(resetPasswordScene.get());
} }
/**
* Checks, if vault config is signed with the given key.
*
* @param key byte array of possible signing key
* @return true, if vault config is signed with this key
*/
private boolean checkKeyAgainstVaultConfig(byte[] key) {
try {
var config = unverifiedVaultConfig.verify(key, unverifiedVaultConfig.allegedVaultVersion());
LOG.info("Provided recovery key matches vault config signature for vault {}", config.getId());
return true;
} catch (VaultKeyInvalidException e) {
LOG.debug("Provided recovery key does not match vault config signature.");
isWrongKey = true;
return false;
} catch (VaultConfigLoadException e) {
LOG.error("Failed to parse vault config", e);
return false;
}
}
private void validateRecoveryKey() {
isWrongKey = false;
var valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null);
if (valid) {
recoveryKeyState.set(RecoveryKeyState.CORRECT);
} else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig()
recoveryKeyState.set(RecoveryKeyState.WRONG);
} else {
recoveryKeyState.set(RecoveryKeyState.INVALID);
}
}
/* Getter/Setter */ /* Getter/Setter */
public Vault getVault() { public RecoveryKeyValidateController getValidateController() {
return vault; return recoveryKeyValidateController;
} }
public TextFormatter getRecoveryKeyTextFormatter() {
return new TextFormatter<>(this::filterTextChange);
}
public ObservableValue<Boolean> recoveryKeyInvalidProperty() {
return recoveryKeyInvalid;
}
public boolean isRecoveryKeyInvalid() {
return recoveryKeyInvalid.getValue();
}
public ObservableValue<Boolean> recoveryKeyCorrectProperty() {
return recoveryKeyCorrect;
}
public boolean isRecoveryKeyCorrect() {
return recoveryKeyCorrect.getValue();
}
public ObservableValue<Boolean> recoveryKeyWrongProperty() {
return recoveryKeyWrong;
}
public boolean isRecoveryKeyWrong() {
return recoveryKeyWrong.getValue();
}
private enum RecoveryKeyState {
/**
* Recovery key is a valid key and belongs to this vault
*/
CORRECT,
/**
* Recovery key is a valid key, but does not belong to this vault
*/
WRONG,
/**
* Recovery key is not a valid key.
*/
INVALID;
}
} }
@@ -5,7 +5,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -76,7 +76,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
@Override @Override
protected Void call() throws IOException, IllegalArgumentException { protected Void call() throws IOException, IllegalArgumentException {
recoveryKeyFactory.resetPasswordWithRecoveryKey(vault.getPath(), recoveryKey.get(), newPasswordController.passwordField.getCharacters()); recoveryKeyFactory.newMasterkeyFileWithPassphrase(vault.getPath(), recoveryKey.get(), newPasswordController.passwordField.getCharacters());
return null; return null;
} }
@@ -0,0 +1,180 @@
package org.cryptomator.ui.recoverykey;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class RecoveryKeyValidateController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class);
private static final CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' '));
private final Vault vault;
private final VaultConfig.UnverifiedVaultConfig unverifiedVaultConfig;
private final StringProperty recoveryKey;
private final ObservableValue<Boolean> recoveryKeyCorrect;
private final ObservableValue<Boolean> recoveryKeyWrong;
private final ObservableValue<Boolean> recoveryKeyInvalid;
private final RecoveryKeyFactory recoveryKeyFactory;
private final ObjectProperty<RecoveryKeyState> recoveryKeyState;
private final AutoCompleter autoCompleter;
private volatile boolean isWrongKey;
public TextArea textarea;
public RecoveryKeyValidateController(Vault vault, @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
this.vault = vault;
this.unverifiedVaultConfig = vaultConfig;
this.recoveryKey = recoveryKey;
this.recoveryKeyFactory = recoveryKeyFactory;
this.autoCompleter = new AutoCompleter(recoveryKeyFactory.getDictionary());
this.recoveryKeyState = new SimpleObjectProperty<>();
this.recoveryKeyCorrect = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.CORRECT::equals, false);
this.recoveryKeyWrong = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.WRONG::equals, false);
this.recoveryKeyInvalid = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.INVALID::equals, false);
}
@FXML
public void initialize() {
recoveryKey.bind(textarea.textProperty());
textarea.textProperty().addListener(((observable, oldValue, newValue) -> validateRecoveryKey()));
}
private TextFormatter.Change filterTextChange(TextFormatter.Change change) {
if (Strings.isNullOrEmpty(change.getText())) {
// pass-through caret/selection changes that don't affect the text
return change;
}
if (!ALLOWED_CHARS.matchesAllOf(change.getText())) {
return null; // reject change
}
String text = change.getControlNewText();
int caretPos = change.getCaretPosition();
if (caretPos == text.length() || text.charAt(caretPos) == ' ') { // are we at the end of a word?
int beginOfWord = Math.max(text.substring(0, caretPos).lastIndexOf(' ') + 1, 0);
String currentWord = text.substring(beginOfWord, caretPos);
var suggestion = autoCompleter.autocomplete(currentWord);
if (suggestion.isPresent()) {
String completion = suggestion.get().substring(currentWord.length());
change.setText(change.getText() + completion);
change.setAnchor(caretPos + completion.length());
}
}
return change;
}
@FXML
public void onKeyPressed(KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.TAB && textarea.getAnchor() > textarea.getCaretPosition()) {
// apply autocompletion:
int pos = textarea.getAnchor();
textarea.insertText(pos, " ");
textarea.positionCaret(pos + 1);
}
}
/**
* Checks, if vault config is signed with the given key.
*
* @param key byte array of possible signing key
* @return true, if vault config is signed with this key
*/
private boolean checkKeyAgainstVaultConfig(byte[] key) {
assert unverifiedVaultConfig != null;
try {
var config = unverifiedVaultConfig.verify(key, unverifiedVaultConfig.allegedVaultVersion());
LOG.info("Provided recovery key matches vault config signature for vault {}", config.getId());
return true;
} catch (VaultKeyInvalidException e) {
LOG.debug("Provided recovery key does not match vault config signature.");
isWrongKey = true;
return false;
} catch (VaultConfigLoadException e) {
LOG.error("Failed to parse vault config", e);
return false;
}
}
private void validateRecoveryKey() {
isWrongKey = false;
var valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null);
if (valid) {
recoveryKeyState.set(RecoveryKeyState.CORRECT);
} else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig()
recoveryKeyState.set(RecoveryKeyState.WRONG);
} else {
recoveryKeyState.set(RecoveryKeyState.INVALID);
}
}
/* Getter/Setter */
public Vault getVault() {
return vault;
}
public TextFormatter getRecoveryKeyTextFormatter() {
return new TextFormatter<>(this::filterTextChange);
}
public ObservableValue<Boolean> recoveryKeyInvalidProperty() {
return recoveryKeyInvalid;
}
public boolean isRecoveryKeyInvalid() {
return recoveryKeyInvalid.getValue();
}
public ObservableValue<Boolean> recoveryKeyCorrectProperty() {
return recoveryKeyCorrect;
}
public boolean isRecoveryKeyCorrect() {
return recoveryKeyCorrect.getValue();
}
public ObservableValue<Boolean> recoveryKeyWrongProperty() {
return recoveryKeyWrong;
}
public boolean isRecoveryKeyWrong() {
return recoveryKeyWrong.getValue();
}
private enum RecoveryKeyState {
/**
* Recovery key is a valid key and belongs to this vault
*/
CORRECT,
/**
* Recovery key is a valid key, but does not belong to this vault
*/
WRONG,
/**
* Recovery key is not a valid key.
*/
INVALID;
}
}
@@ -0,0 +1,27 @@
package org.cryptomator.ui.vaultoptions;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.convertvault.ConvertVaultComponent;
import javax.inject.Inject;
import javafx.stage.Stage;
public class HubOptionsController implements FxController {
private final Vault vault;
private final Stage window;
private final ConvertVaultComponent.Factory convertVaultFactory;
@Inject
public HubOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ConvertVaultComponent.Factory convertVaultFactory) {
this.vault = vault;
this.window = window;
this.convertVaultFactory = convertVaultFactory;
}
public void startConversion() {
convertVaultFactory.create(vault,window).showHubToPasswordWindow();
}
}
@@ -4,7 +4,7 @@ import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.changepassword.ChangePasswordComponent; import org.cryptomator.ui.changepassword.ChangePasswordComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -23,14 +23,14 @@ public class MasterkeyOptionsController implements FxController {
private final Vault vault; private final Vault vault;
private final Stage window; private final Stage window;
private final ChangePasswordComponent.Builder changePasswordWindow; private final ChangePasswordComponent.Builder changePasswordWindow;
private final RecoveryKeyComponent.Builder recoveryKeyWindow; private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final ForgetPasswordComponent.Builder forgetPasswordWindow; private final ForgetPasswordComponent.Builder forgetPasswordWindow;
private final KeychainManager keychain; private final KeychainManager keychain;
private final ObservableValue<Boolean> passwordSaved; private final ObservableValue<Boolean> passwordSaved;
@Inject @Inject
MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow, ForgetPasswordComponent.Builder forgetPasswordWindow, KeychainManager keychain) { MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Factory recoveryKeyWindow, ForgetPasswordComponent.Builder forgetPasswordWindow, KeychainManager keychain) {
this.vault = vault; this.vault = vault;
this.window = window; this.window = window;
this.changePasswordWindow = changePasswordWindow; this.changePasswordWindow = changePasswordWindow;
@@ -51,12 +51,12 @@ public class MasterkeyOptionsController implements FxController {
@FXML @FXML
public void showRecoveryKey() { public void showRecoveryKey() {
recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyCreationWindow(); recoveryKeyWindow.create(vault, window).showRecoveryKeyCreationWindow();
} }
@FXML @FXML
public void showRecoverVaultDialog() { public void showRecoverVaultDialog() {
recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyRecoverWindow(); recoveryKeyWindow.create(vault, window).showRecoveryKeyRecoverWindow();
} }
@FXML @FXML
@@ -21,4 +21,9 @@ public enum SelectedVaultOptionsTab {
*/ */
KEY, KEY,
/**
* Show hub tab
*/
HUB
} }
@@ -2,6 +2,8 @@ package org.cryptomator.ui.vaultoptions;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -25,6 +27,7 @@ public class VaultOptionsController implements FxController {
public Tab generalTab; public Tab generalTab;
public Tab mountTab; public Tab mountTab;
public Tab keyTab; public Tab keyTab;
public Tab hubTab;
@Inject @Inject
VaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObjectProperty<SelectedVaultOptionsTab> selectedTabProperty) { VaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObjectProperty<SelectedVaultOptionsTab> selectedTabProperty) {
@@ -38,9 +41,13 @@ public class VaultOptionsController implements FxController {
window.setOnShowing(this::windowWillAppear); window.setOnShowing(this::windowWillAppear);
selectedTabProperty.addListener(observable -> this.selectChosenTab()); selectedTabProperty.addListener(observable -> this.selectChosenTab());
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged()); tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
if(!vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme().equals("masterkeyfile")){ var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
if(!vaultScheme.equals(MasterkeyFileLoadingStrategy.SCHEME)){
tabPane.getTabs().remove(keyTab); tabPane.getTabs().remove(keyTab);
} }
if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){
tabPane.getTabs().remove(hubTab);
}
} }
private void selectChosenTab() { private void selectChosenTab() {
@@ -53,6 +60,7 @@ public class VaultOptionsController implements FxController {
case ANY, GENERAL -> generalTab; case ANY, GENERAL -> generalTab;
case MOUNT -> mountTab; case MOUNT -> mountTab;
case KEY -> keyTab; case KEY -> keyTab;
case HUB -> hubTab;
}; };
} }
@@ -13,7 +13,8 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent; import org.cryptomator.ui.convertvault.ConvertVaultComponent;
import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
@@ -26,7 +27,7 @@ import javafx.stage.Stage;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@Module(subcomponents = {ChangePasswordComponent.class, RecoveryKeyComponent.class, ForgetPasswordComponent.class}) @Module(subcomponents = {ChangePasswordComponent.class, RecoveryKeyComponent.class, ForgetPasswordComponent.class, ConvertVaultComponent.class})
abstract class VaultOptionsModule { abstract class VaultOptionsModule {
@Provides @Provides
@@ -84,4 +85,9 @@ abstract class VaultOptionsModule {
@IntoMap @IntoMap
@FxControllerKey(MasterkeyOptionsController.class) @FxControllerKey(MasterkeyOptionsController.class)
abstract FxController bindMasterkeyOptionsController(MasterkeyOptionsController controller); abstract FxController bindMasterkeyOptionsController(MasterkeyOptionsController controller);
@Binds
@IntoMap
@FxControllerKey(HubOptionsController.class)
abstract FxController bindHubOptionsController(HubOptionsController controller);
} }
+4
View File
@@ -116,6 +116,10 @@
-fx-font-size: 0.64em; -fx-font-size: 0.64em;
} }
.label-red {
-fx-text-fill: RED_5;
}
.text-flow > * { .text-flow > * {
-fx-fill: TEXT_FILL; -fx-fill: TEXT_FILL;
} }
+4
View File
@@ -116,6 +116,10 @@
-fx-font-size: 0.64em; -fx-font-size: 0.64em;
} }
.label-red {
-fx-text-fill: RED_5;
}
.text-flow > * { .text-flow > * {
-fx-fill: TEXT_FILL; -fx-fill: TEXT_FILL;
} }
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?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.convertvault.HubToPasswordConvertController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<fx:include fx:id="newPassword" source="new_password.fxml"/>
<Region VBox.vgrow="ALWAYS"/>
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="convertBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#convert"> <!-- for button logic, see controller -->
<graphic>
<FontAwesome5Spinner glyphSize="12"/>
</graphic>
</Button>
</buttons>
</ButtonBar>
</VBox>
</children>
</VBox>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.convertvault.HubToPasswordStartController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/>
<Region VBox.vgrow="ALWAYS"/>
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#next" disable="${!controller.validateController.recoveryKeyCorrect}"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</VBox>
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?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 javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.convertvault.HubToPasswordSuccessController"
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="CHECK" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%convertVault.success.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%convertVault.hubToPassword.success.description" wrapText="true" textAlignment="LEFT"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" cancelButton="true" onAction="#close"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>
+1 -1
View File
@@ -15,7 +15,7 @@
<?import javafx.scene.shape.Circle?> <?import javafx.scene.shape.Circle?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.common.ErrorController" fx:controller="org.cryptomator.ui.error.ErrorController"
prefWidth="450" prefWidth="450"
prefHeight="450" prefHeight="450"
spacing="18" spacing="18"
+1 -1
View File
@@ -13,7 +13,7 @@
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<HBox xmlns:fx="http://javafx.com/fxml" <HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.forgetPassword.ForgetPasswordController" fx:controller="org.cryptomator.ui.forgetpassword.ForgetPasswordController"
minWidth="400" minWidth="400"
maxWidth="400" maxWidth="400"
minHeight="145" minHeight="145"
+1 -1
View File
@@ -8,7 +8,7 @@
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.common.NewPasswordController" fx:controller="org.cryptomator.ui.changepassword.NewPasswordController"
spacing="6" spacing="6"
alignment="CENTER_LEFT"> alignment="CENTER_LEFT">
<fx:define> <fx:define>
@@ -8,9 +8,9 @@
<?import javafx.scene.control.Hyperlink?> <?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.Separator?> <?import javafx.scene.control.Separator?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Tooltip?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.preferences.VolumePreferencesController" fx:controller="org.cryptomator.ui.preferences.VolumePreferencesController"
@@ -32,6 +32,8 @@
</Hyperlink> </Hyperlink>
</HBox> </HBox>
<Label styleClass="label-red" text="%preferences.volume.fuseRestartRequired" visible="${controller.fuseRestartRequired}" managed="${controller.fuseRestartRequired}"/>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}"> <HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}">
<Label text="%preferences.volume.tcp.port"/> <Label text="%preferences.volume.tcp.port"/>
<NumericTextField fx:id="loopbackPortField"/> <NumericTextField fx:id="loopbackPortField"/>
@@ -1,16 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?> <?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.Group?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyRecoverController" fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyRecoverController"
@@ -23,32 +17,8 @@
<Insets topRightBottomLeft="12"/> <Insets topRightBottomLeft="12"/>
</padding> </padding>
<children> <children>
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/> <fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/>
<StackPane>
<Label text="Just some Filler" visible="false" graphicTextGap="6">
<graphic>
<FontAwesome5IconView glyph="ANCHOR"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.correctKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyCorrect}">
<graphic>
<FontAwesome5IconView glyph="CHECK"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.wrongKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyWrong}">
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.invalidKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyInvalid}">
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
</StackPane>
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
@@ -56,7 +26,7 @@
<ButtonBar buttonMinWidth="120" buttonOrder="+CX"> <ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons> <buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#recover" disable="${!controller.recoveryKeyCorrect}"/> <Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#recover" disable="${!controller.validateController.recoveryKeyCorrect}"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox> </VBox>
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyValidateController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/>
<StackPane>
<Label text="Just some Filler" visible="false" graphicTextGap="6">
<graphic>
<FontAwesome5IconView glyph="ANCHOR"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.correctKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyCorrect}">
<graphic>
<FontAwesome5IconView glyph="CHECK"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.wrongKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyWrong}">
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
<Label text="%recoveryKey.recover.invalidKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyInvalid}">
<graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic>
</Label>
</StackPane>
</children>
</VBox>
@@ -36,5 +36,13 @@
<fx:include source="vault_options_masterkey.fxml"/> <fx:include source="vault_options_masterkey.fxml"/>
</content> </content>
</Tab> </Tab>
<Tab fx:id="hubTab" id="HUB" text="%vaultOptions.hub"> <!-- is removed in controller, when config.keyid.scheme is not cryptomator-hub -->
<graphic>
<FontAwesome5IconView glyph="KEY"/>
</graphic>
<content>
<fx:include source="vault_options_hub.fxml"/>
</content>
</Tab>
</tabs> </tabs>
</TabPane> </TabPane>
@@ -0,0 +1,25 @@
<?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.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.vaultoptions.HubOptionsController"
spacing="6"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<Label maxWidth="-Infinity" text="%vaultOptions.hub.convertInfo" wrapText="true"/>
<VBox spacing="6" alignment="CENTER">
<Button fx:id="convertHubToPasswordButton" text="%vaultOptions.hub.convertBtn" onAction="#startConversion" maxWidth="Infinity">
<graphic>
<FontAwesome5IconView glyph="EXCHANGE_ALT"/>
</graphic>
</Button>
</VBox>
</VBox>
+21 -10
View File
@@ -275,9 +275,10 @@ preferences.interface.showMinimizeButton=Show minimize button
preferences.interface.showTrayIcon=Show tray icon (requires restart) preferences.interface.showTrayIcon=Show tray icon (requires restart)
## Volume ## Volume
preferences.volume=Virtual Drive preferences.volume=Virtual Drive
preferences.volume.type=Volume Type (requires restart) preferences.volume.type=Volume Type
preferences.volume.type.automatic=Automatic preferences.volume.type.automatic=Automatic
preferences.volume.docsTooltip=Open the documentation to learn more about the different volume types. preferences.volume.docsTooltip=Open the documentation to learn more about the different volume types.
preferences.volume.fuseRestartRequired=To apply the changes, Cryptomator needs to be restarted.
preferences.volume.tcp.port=TCP Port preferences.volume.tcp.port=TCP Port
preferences.volume.supportedFeatures=The chosen volume type supports the following features: preferences.volume.supportedFeatures=The chosen volume type supports the following features:
preferences.volume.feature.mountAuto=Automatic mount point selection preferences.volume.feature.mountAuto=Automatic mount point selection
@@ -307,27 +308,27 @@ stats.title=Statistics for %s
stats.cacheHitRate=Cache Hit Rate stats.cacheHitRate=Cache Hit Rate
## Read ## Read
stats.read.throughput.idle=Read: idle stats.read.throughput.idle=Read: idle
stats.read.throughput.kibs=Read: %.2f kiB/s stats.read.throughput.kibs=Read: %.2f KiB/s
stats.read.throughput.mibs=Read: %.2f MiB/s stats.read.throughput.mibs=Read: %.2f MiB/s
stats.read.total.data.none=Data read: - stats.read.total.data.none=Data read: -
stats.read.total.data.kib=Data read: %.1f kiB stats.read.total.data.kib=Data read: %.1f KiB
stats.read.total.data.mib=Data read: %.1f MiB stats.read.total.data.mib=Data read: %.1f MiB
stats.read.total.data.gib=Data read: %.1f GiB stats.read.total.data.gib=Data read: %.1f GiB
stats.decr.total.data.none=Data decrypted: - stats.decr.total.data.none=Data decrypted: -
stats.decr.total.data.kib=Data decrypted: %.1f kiB stats.decr.total.data.kib=Data decrypted: %.1f KiB
stats.decr.total.data.mib=Data decrypted: %.1f MiB stats.decr.total.data.mib=Data decrypted: %.1f MiB
stats.decr.total.data.gib=Data decrypted: %.1f GiB stats.decr.total.data.gib=Data decrypted: %.1f GiB
stats.read.accessCount=Total reads: %d stats.read.accessCount=Total reads: %d
## Write ## Write
stats.write.throughput.idle=Write: idle stats.write.throughput.idle=Write: idle
stats.write.throughput.kibs=Write: %.2f kiB/s stats.write.throughput.kibs=Write: %.2f KiB/s
stats.write.throughput.mibs=Write: %.2f MiB/s stats.write.throughput.mibs=Write: %.2f MiB/s
stats.write.total.data.none=Data written: - stats.write.total.data.none=Data written: -
stats.write.total.data.kib=Data written: %.1f kiB stats.write.total.data.kib=Data written: %.1f KiB
stats.write.total.data.mib=Data written: %.1f MiB stats.write.total.data.mib=Data written: %.1f MiB
stats.write.total.data.gib=Data written: %.1f GiB stats.write.total.data.gib=Data written: %.1f GiB
stats.encr.total.data.none=Data encrypted: - stats.encr.total.data.none=Data encrypted: -
stats.encr.total.data.kib=Data encrypted: %.1f kiB stats.encr.total.data.kib=Data encrypted: %.1f KiB
stats.encr.total.data.mib=Data encrypted: %.1f MiB stats.encr.total.data.mib=Data encrypted: %.1f MiB
stats.encr.total.data.gib=Data encrypted: %.1f GiB stats.encr.total.data.gib=Data encrypted: %.1f GiB
stats.write.accessCount=Total writes: %d stats.write.accessCount=Total writes: %d
@@ -370,7 +371,7 @@ main.vaultDetail.lockBtn=Lock
main.vaultDetail.bytesPerSecondRead=Read: main.vaultDetail.bytesPerSecondRead=Read:
main.vaultDetail.bytesPerSecondWritten=Write: main.vaultDetail.bytesPerSecondWritten=Write:
main.vaultDetail.throughput.idle=idle main.vaultDetail.throughput.idle=idle
main.vaultDetail.throughput.kbps=%.1f kiB/s main.vaultDetail.throughput.kbps=%.1f KiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Vault Statistics main.vaultDetail.stats=Vault Statistics
main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File
@@ -433,7 +434,10 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Forget Saved Password
vaultOptions.masterkey.recoveryKeyExplanation=A recovery key is your only means to restore access to a vault if you lose your password. vaultOptions.masterkey.recoveryKeyExplanation=A recovery key is your only means to restore access to a vault if you lose your password.
vaultOptions.masterkey.showRecoveryKeyBtn=Display Recovery Key vaultOptions.masterkey.showRecoveryKeyBtn=Display Recovery Key
vaultOptions.masterkey.recoverPasswordBtn=Reset Password vaultOptions.masterkey.recoverPasswordBtn=Reset Password
## Hub
vaultOptions.hub=Recovery
vaultOptions.hub.convertInfo=You can use the recovery key to convert this Hub vault to a password-based vault in an emergency.
vaultOptions.hub.convertBtn=Convert to Password-Based Vault
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -445,7 +449,7 @@ recoveryKey.display.StorageHints=Keep it somewhere very secure, e.g.:\n • Stor
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.title=Reset Password recoveryKey.recover.title=Reset Password
recoveryKey.recover.prompt=Enter your recovery key for "%s": recoveryKey.recover.prompt=Enter the recovery key for "%s":
recoveryKey.recover.correctKey=This recovery key is correct recoveryKey.recover.correctKey=This recovery key is correct
recoveryKey.recover.wrongKey=This recovery key belongs to a different vault recoveryKey.recover.wrongKey=This recovery key belongs to a different vault
recoveryKey.recover.invalidKey=This recovery key is not valid recoveryKey.recover.invalidKey=This recovery key is not valid
@@ -456,6 +460,13 @@ recoveryKey.recover.resetBtn=Reset
recoveryKey.recover.resetSuccess.message=Password reset successful recoveryKey.recover.resetSuccess.message=Password reset successful
recoveryKey.recover.resetSuccess.description=You can unlock your vault with the new password. recoveryKey.recover.resetSuccess.description=You can unlock your vault with the new password.
# Convert Vault
convertVault.title=Convert Vault
convertVault.convert.convertBtn.before=Convert
convertVault.convert.convertBtn.processing=Converting…
convertVault.success.message=Conversion successful
convertVault.hubToPassword.success.description=You can now unlock the vault with the chosen password without requiring Hub access.
# New Password # New Password
newPassword.promptText=Enter a new password newPassword.promptText=Enter a new password
newPassword.reenterPassword=Confirm the new password newPassword.reenterPassword=Confirm the new password
@@ -113,7 +113,6 @@ hub.register.registerBtn=تأكيد
### Unauthorized ### Unauthorized
### License Exceeded ### License Exceeded
# Lock # Lock
## Force ## Force
lock.forced.description=تم حظر قفل "%s" بواسطة العمليات المعلقة أو الملفات المفتوحة. يمكنك فرض قفل هذا المخزن، ولكن مقاطعة عمليات الادخال والاخراج I/O قد تؤدي لفقدان البيانات غير المحفوظة. lock.forced.description=تم حظر قفل "%s" بواسطة العمليات المعلقة أو الملفات المفتوحة. يمكنك فرض قفل هذا المخزن، ولكن مقاطعة عمليات الادخال والاخراج I/O قد تؤدي لفقدان البيانات غير المحفوظة.
@@ -234,7 +233,6 @@ main.vaultDetail.lockBtn=قفل
main.vaultDetail.bytesPerSecondRead=قراءة: main.vaultDetail.bytesPerSecondRead=قراءة:
main.vaultDetail.bytesPerSecondWritten=كتابة: main.vaultDetail.bytesPerSecondWritten=كتابة:
main.vaultDetail.throughput.idle=خمول main.vaultDetail.throughput.idle=خمول
main.vaultDetail.throughput.kbps=%.1f كيلوبايت/ث
main.vaultDetail.throughput.mbps=%.1f ميجابايت/ث main.vaultDetail.throughput.mbps=%.1f ميجابايت/ث
main.vaultDetail.stats=إحصائيات الخزنة main.vaultDetail.stats=إحصائيات الخزنة
main.vaultDetail.locateEncryptedFileBtn=تحديد موقع الملف المشفر main.vaultDetail.locateEncryptedFileBtn=تحديد موقع الملف المشفر
@@ -286,7 +284,7 @@ vaultOptions.masterkey.changePasswordBtn=تغيير كلمة المرور
vaultOptions.masterkey.forgetSavedPasswordBtn=نسيان كلمة المرور المحفوظة vaultOptions.masterkey.forgetSavedPasswordBtn=نسيان كلمة المرور المحفوظة
vaultOptions.masterkey.recoveryKeyExplanation=مفتاح الاسترداد هو وسيلتك الوحيدة لاستعادة الوصول إلى مخزنك إذا فقدت كلمة المرور. vaultOptions.masterkey.recoveryKeyExplanation=مفتاح الاسترداد هو وسيلتك الوحيدة لاستعادة الوصول إلى مخزنك إذا فقدت كلمة المرور.
vaultOptions.masterkey.showRecoveryKeyBtn=عرض مفتاح الاسترداد vaultOptions.masterkey.showRecoveryKeyBtn=عرض مفتاح الاسترداد
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -295,12 +293,13 @@ recoveryKey.display.description=يمكن استخدام مفتاح الاستر
recoveryKey.display.StorageHints=حافظ عليه في مكان ما آمن جداً، على سبيل المثال\n • تخزينه باستخدام مدير كلمات المرور\n • حفظه على محرك أقراص خارجي USB\n • كتابته أو طباعته على الورق recoveryKey.display.StorageHints=حافظ عليه في مكان ما آمن جداً، على سبيل المثال\n • تخزينه باستخدام مدير كلمات المرور\n • حفظه على محرك أقراص خارجي USB\n • كتابته أو طباعته على الورق
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.prompt=أدخل مفتاح الاسترداد الخاص لـ "%s":
recoveryKey.recover.correctKey=هذا مفتاح استرداد صالح recoveryKey.recover.correctKey=هذا مفتاح استرداد صالح
recoveryKey.printout.heading=مفتاح استرداد Cryptomator\n"%s"\n recoveryKey.printout.heading=مفتاح استرداد Cryptomator\n"%s"\n
### Reset Password ### Reset Password
### Recovery Key Password Reset Success ### Recovery Key Password Reset Success
# Convert Vault
# New Password # New Password
newPassword.promptText=أدخل كلمة مرور جديدة newPassword.promptText=أدخل كلمة مرور جديدة
newPassword.reenterPassword=تأكيد كلمة المرور الجديدة newPassword.reenterPassword=تأكيد كلمة المرور الجديدة
+81 -9
View File
@@ -123,7 +123,14 @@ unlock.success.description=Змест скарбніцы "%s" цяпер дас
unlock.success.rememberChoice=Запомні мой выбар і больш не пытай unlock.success.rememberChoice=Запомні мой выбар і больш не пытай
unlock.success.revealBtn=Паказаць дыск unlock.success.revealBtn=Паказаць дыск
## Failure ## Failure
unlock.error.customPath.message=Не магчыма змантажаваць скарбніцу да карыстальніцкай сцежкі
unlock.error.customPath.description.notSupported=Калі ты надалей жадаеш карыстацца адмысловаю сцежкаю, калі ласка, пайдзі ў налады ды абары там тып тому, які падтрымлівае яе. У іншым выпадку пайдзі ў опцыі скарбніцы ды абяры там пункт мантажавання, які падтрымліваецца.
unlock.error.customPath.description.notExists=Адмысловая сцежка мантажавання не існуе. Ствары яе ў сваёй файлавай сістэме, альбо змяні яе ў опцыях скарбніцы.
unlock.error.customPath.description.generic=Ты абраў адмысловую сцежку мантажавання для гэтай скарбніцы, але скарыстацца ёю не ўдалося. Вось паведамленне пра памылку: %s
## Hub ## Hub
hub.noKeychain.message=Няма доступу да ключа прылады
hub.noKeychain.description=Каб разамкнуць скарбніцы Hub, патрэбны ключ прылады, які захаваны ў звязку ключоў. Каб працягнуць, уключы "%s" ды абяры звязак ключоў у наладах.
hub.noKeychain.openBtn=Адчыніць налады
### Waiting ### Waiting
hub.auth.message=Чаканне спраўджання… hub.auth.message=Чаканне спраўджання…
hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу. hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу.
@@ -147,8 +154,8 @@ hub.registerFailed.description=Падчас прысваення імя адбы
hub.unauthorized.message=Адмова ў доступе hub.unauthorized.message=Адмова ў доступе
hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам. hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам.
### License Exceeded ### License Exceeded
hub.licenseExceeded.message=Ліцэнзія пратэрмінаваная hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
hub.invalidLicense.description=Твая інстанцыя Cryptomator Hub мае некарэктную ліцэнзію. Калі ласка, паведамі адміністратару Hub пра гэта, каб абнавіць альбо аднавіць ліцэнзію.
# Lock # Lock
## Force ## Force
@@ -164,6 +171,12 @@ lock.fail.description=Скарбніцу "%s" не мажліва замкнуц
migration.title=Абнавіць скарбніцу migration.title=Абнавіць скарбніцу
## Start ## Start
migration.start.header=Абнавіць скарбніцу migration.start.header=Абнавіць скарбніцу
migration.start.text=Каб адчыніць тваю скарбніцу "%s" у гэтай новай вэрсіі Cryptomator, скарбніца мусіць быць абноўленая да навейшага фармату. Перад тым, як пачаць, трэба ведаць наступнае:
migration.start.remarkUndone=Не магчыма скасаваць гэта абнаўленне.
migration.start.remarkVersions=Старэйшыя вэрсіі Cryptomator ня змогуць адчыніць абноўленую скарбніцу.
migration.start.remarkCanRun=Ты мусіш упэўніцца, што на кожнай прыладзе, якая мае доступ да скарбніцы, працуе гэта вэрсія Cryptomator.
migration.start.remarkSynced=Ты мусіш упэўніцца, што твая скарбніца цалкам сынхранаваная на гэтай ды іншых прыладах, перад тым як пачаць абнаўленне.
migration.start.confirm=Я прачытала і зразумела інфармацыю зверху
## Run ## Run
migration.run.enterPassword=Увядзіце пароль да "%s" migration.run.enterPassword=Увядзіце пароль да "%s"
migration.run.startMigrationBtn=Перамясціць скарбніцу migration.run.startMigrationBtn=Перамясціць скарбніцу
@@ -185,17 +198,47 @@ migration.impossible.heading=Не мажліва перанесці скарбн
## Start ## Start
health.title=Тэст на цэласнасць для "%s" health.title=Тэст на цэласнасць для "%s"
health.intro.header=Тэст на цэласнасць health.intro.header=Тэст на цэласнасць
health.intro.remarkFix=Ня ўсе хібы могуць быць выпраўленыя.
health.intro.remarkBackup=Калі даныя пашкоджаныя, дапаможа толькі рэзервовая копія. health.intro.remarkBackup=Калі даныя пашкоджаныя, дапаможа толькі рэзервовая копія.
health.intro.affirmation=Я прачытала і зразумела інфармацыю зверху
## Start Failure ## Start Failure
health.fail.header=Падчас загрузкі налад скарбніцы адбылася памылка
health.fail.ioError=Здарылася памылка падчас спробы прачытаць канфіґурацыйны файл.
health.fail.parseError=Здарылася памылка падчас ператварэння канфіґурацыі скарбніцы.
health.fail.moreInfo=Падрабязней health.fail.moreInfo=Падрабязней
## Check Selection ## Check Selection
health.checkList.description=Абяры тэсты ў левым спісе альбо скарыстайся кнопкамі ўнізе.
health.checkList.selectAllButton=Вылучыць усе элементы health.checkList.selectAllButton=Вылучыць усе элементы
health.checkList.deselectAllButton=Адрабіць вылучэнне ўсіх элементаў health.checkList.deselectAllButton=Адрабіць вылучэнне ўсіх элементаў
health.check.runBatchBtn=Выканаць вылучаныя элементы health.check.runBatchBtn=Выканаць вылучаныя элементы
## Detail view ## Detail view
health.check.detail.noSelectedCheck=Каб праглядзець вынікі, абяры тэсты цэласнасці ў левым спісе.
health.check.detail.checkScheduled=Тэст пастаўлены ў чаргу.
health.check.detail.checkRunning=Тэст адбываецца ў гэты момант…
health.check.detail.checkSkipped=Тэст ня быў абраны для выканання.
health.check.detail.checkFinished=Тэст паспяхова скончыўся.
health.check.detail.checkFinishedAndFound=Тэст завершаны. Калі ласка, праглядзі вынікі.
health.check.detail.checkFailed=Тэст быў спынены з-за памылкі.
health.check.detail.checkCancelled=Праверка была скасавана. health.check.detail.checkCancelled=Праверка была скасавана.
health.check.detail.listFilters.label=Фільтр
health.check.detail.fixAllSpecificBtn=Адрамантаваць усё гэтага тыпу
health.check.exportBtn=Экспартаваць справаздачу health.check.exportBtn=Экспартаваць справаздачу
## Result view ## Result view
health.result.severityFilter.all=Стан – Усе
health.result.severityFilter.good=Добра
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 ## Fix Application
health.fix.fixBtn=Выправіць health.fix.fixBtn=Выправіць
health.fix.successTip=Паспяхова выпраўлена health.fix.successTip=Паспяхова выпраўлена
@@ -228,6 +271,14 @@ preferences.interface.showTrayIcon=Паказваць іконку на інфа
## Volume ## Volume
preferences.volume=Віртуальны дыск preferences.volume=Віртуальны дыск
preferences.volume.type.automatic=Аўтаматычна preferences.volume.type.automatic=Аўтаматычна
preferences.volume.docsTooltip=Адчыні дакумэнтацыю, каб даведацца больш пра розныя тыпы тому.
preferences.volume.tcp.port=Порт TCP
preferences.volume.supportedFeatures=Абраны тып тому падтрымлівае наступныя функцыі:
preferences.volume.feature.mountAuto=Аўтаматычны выбар пункту мантажавання
preferences.volume.feature.mountToDir=Карыстальніцкая тэчка як пункт мантажавання
preferences.volume.feature.mountToDriveLetter=Літара тому як пункт мантажавання
preferences.volume.feature.mountFlags=Карыстальніцкія опцыі мантажавання
preferences.volume.feature.readOnly=Мантажаванне толькі для чытання
## Updates ## Updates
preferences.updates=Абнаўленні preferences.updates=Абнаўленні
preferences.updates.currentVersion=Бягучая версія: %s preferences.updates.currentVersion=Бягучая версія: %s
@@ -236,7 +287,10 @@ preferences.updates.checkNowBtn=Праверыць зараз
preferences.updates.updateAvailable=Даступна абнаўленне да версіі %s preferences.updates.updateAvailable=Даступна абнаўленне да версіі %s
## Contribution ## Contribution
preferences.contribute=Падтрымай нас preferences.contribute=Падтрымай нас
preferences.contribute.registeredFor=Сэртыфікат ахвяравальніка зарэгістраваны на %s
preferences.contribute.noCertificate=Ахвяруй на Cryptomator ды атрымай сэртыфікат ахвяравальніка. Гэта штось падобнае на ліцэнзійны ключ, але для цудоўных людзей, якія карыстаюцца бясплатнай праграмай. ;-)
preferences.contribute.getCertificate=Яшчэ ня маеш такога? Даведайся, як атрымаць. preferences.contribute.getCertificate=Яшчэ ня маеш такога? Даведайся, як атрымаць.
preferences.contribute.promptText=Устаў код сэртыфікату ахвяравальніка сюды
#<-- Add entries for donations and code/translation/documentation contribution --> #<-- Add entries for donations and code/translation/documentation contribution -->
## About ## About
@@ -244,9 +298,10 @@ preferences.about=Пра нас
# Vault Statistics # Vault Statistics
stats.title=Статыстыкі для %s stats.title=Статыстыкі для %s
stats.cacheHitRate=Трапнасць кэшу
## Read ## Read
stats.read.throughput.idle=Чытанне: - stats.read.throughput.idle=Чытанне: нічога
stats.read.throughput.kibs=Чытанне: %.2f КіБ/с stats.read.throughput.kibs=Чытанне: %.2f KiB/s
stats.read.throughput.mibs=Чытанне: %.2f МіБ/с stats.read.throughput.mibs=Чытанне: %.2f МіБ/с
stats.read.total.data.none=Прачытана: - stats.read.total.data.none=Прачытана: -
stats.read.total.data.kib=Прачытана: %.1f КіБ stats.read.total.data.kib=Прачытана: %.1f КіБ
@@ -256,9 +311,9 @@ stats.decr.total.data.none=Расшыфравана: -
stats.decr.total.data.kib=Расшыфравана: %.1f КіБ stats.decr.total.data.kib=Расшыфравана: %.1f КіБ
stats.decr.total.data.mib=Расшыфравана: %.1f МіБ stats.decr.total.data.mib=Расшыфравана: %.1f МіБ
stats.decr.total.data.gib=Расшыфравана: %.1f ҐіБ stats.decr.total.data.gib=Расшыфравана: %.1f ҐіБ
stats.read.accessCount=Агульная колькасць чытанняў: %d stats.read.accessCount=Прачытана агулам: %d
## Write ## Write
stats.write.throughput.idle=Чытанне: - stats.write.throughput.idle=Пісанне: нічога
stats.write.throughput.kibs=Пісанне: %.2f КіБ/с stats.write.throughput.kibs=Пісанне: %.2f КіБ/с
stats.write.throughput.mibs=Пісанне: %.2f МіБ/с stats.write.throughput.mibs=Пісанне: %.2f МіБ/с
stats.write.total.data.none=Напісана: - stats.write.total.data.none=Напісана: -
@@ -269,9 +324,11 @@ stats.encr.total.data.none=Зашыфравана: -
stats.encr.total.data.kib=Зашыфравана: %.1f КіБ stats.encr.total.data.kib=Зашыфравана: %.1f КіБ
stats.encr.total.data.mib=Зашыфравана: %.1f МіБ stats.encr.total.data.mib=Зашыфравана: %.1f МіБ
stats.encr.total.data.gib=Зашыфравана: %.1f ҐіБ stats.encr.total.data.gib=Зашыфравана: %.1f ҐіБ
stats.write.accessCount=Агульная колькасць запісаў: %d stats.write.accessCount=Запісана агулам: %d
## Accesses ## Accesses
stats.access.current=Доступ: %d
stats.access.total=Доступы агулам: %d
# Main Window # Main Window
@@ -301,6 +358,7 @@ main.vaultDetail.passwordSavedInKeychain=Пароль захаваны
main.vaultDetail.unlockedStatus=РАЗАМКНЁНА main.vaultDetail.unlockedStatus=РАЗАМКНЁНА
main.vaultDetail.accessLocation=Змест тваёй скарбніцы даступны тут: main.vaultDetail.accessLocation=Змест тваёй скарбніцы даступны тут:
main.vaultDetail.revealBtn=Паказаць дыск main.vaultDetail.revealBtn=Паказаць дыск
main.vaultDetail.copyUri=Скапіяваць URI
main.vaultDetail.lockBtn=Замкнуць main.vaultDetail.lockBtn=Замкнуць
main.vaultDetail.bytesPerSecondRead=Чытанне: main.vaultDetail.bytesPerSecondRead=Чытанне:
main.vaultDetail.bytesPerSecondWritten=Пісанне: main.vaultDetail.bytesPerSecondWritten=Пісанне:
@@ -308,6 +366,10 @@ main.vaultDetail.throughput.idle=бяздзейны
main.vaultDetail.throughput.kbps=%.1f КіБ/с main.vaultDetail.throughput.kbps=%.1f КіБ/с
main.vaultDetail.throughput.mbps=%.1f МіБ/с main.vaultDetail.throughput.mbps=%.1f МіБ/с
main.vaultDetail.stats=Статыстыка скарбніцы main.vaultDetail.stats=Статыстыка скарбніцы
main.vaultDetail.locateEncryptedFileBtn=Знайсці зашыфраваны файл
main.vaultDetail.locateEncryptedFileBtn.tooltip=Абяры файл у тваёй скрабніцы, каб знайсці ягоны зашыфраваны адпаведнік
main.vaultDetail.encryptedPathsCopied=Шлях скапіяваны ў буфер абмену!
main.vaultDetail.filePickerTitle=Абраць файл унутры скарбніцы
### Missing ### Missing
main.vaultDetail.missing.info=Cryptomator ня змог знайсці скарбніцу па гэтай сцежцы. main.vaultDetail.missing.info=Cryptomator ня змог знайсці скарбніцу па гэтай сцежцы.
main.vaultDetail.missing.recheck=Пераправерыць main.vaultDetail.missing.recheck=Пераправерыць
@@ -341,23 +403,29 @@ vaultOptions.general.actionAfterUnlock=Пасля паспяховага раз
vaultOptions.general.actionAfterUnlock.ignore=Нічога не рабі vaultOptions.general.actionAfterUnlock.ignore=Нічога не рабі
vaultOptions.general.actionAfterUnlock.reveal=Паказаць дыск vaultOptions.general.actionAfterUnlock.reveal=Паказаць дыск
vaultOptions.general.actionAfterUnlock.ask=Запытацца vaultOptions.general.actionAfterUnlock.ask=Запытацца
vaultOptions.general.startHealthCheckBtn=Пачаць тэст на цэласнасць
## Mount ## Mount
vaultOptions.mount=Мантажаванне vaultOptions.mount=Мантажаванне
vaultOptions.mount.info=Опцыі залежаць ад абранага тыпу тому.
vaultOptions.mount.linkToPreferences=Адчыніць налады віртуальнага дыску
vaultOptions.mount.readonly=Толькі для чытання vaultOptions.mount.readonly=Толькі для чытання
vaultOptions.mount.customMountFlags=Карыстальніцкія опцыі мантажавання vaultOptions.mount.customMountFlags=Карыстальніцкія опцыі мантажавання
vaultOptions.mount.winDriveLetterOccupied=занята vaultOptions.mount.winDriveLetterOccupied=занята
vaultOptions.mount.mountPoint=Пункт мантажавання vaultOptions.mount.mountPoint=Пункт мантажавання
vaultOptions.mount.mountPoint.auto=Аўтаматычны выбар пасуючага месцазнаходжання vaultOptions.mount.mountPoint.auto=Аўтаматычны выбар пасуючага месцазнаходжання
vaultOptions.mount.mountPoint.driveLetter=Назначыць літару для дыску vaultOptions.mount.mountPoint.driveLetter=Назначыць літару для дыску
vaultOptions.mount.mountPoint.custom=Выкарыстаць абраную тэчку
vaultOptions.mount.mountPoint.directoryPickerButton=Абраць… vaultOptions.mount.mountPoint.directoryPickerButton=Абраць…
vaultOptions.mount.mountPoint.directoryPickerTitle=Абраць тэчку
## Master Key ## Master Key
vaultOptions.masterkey=Пароль vaultOptions.masterkey=Пароль
vaultOptions.masterkey.changePasswordBtn=Змяніць пароль vaultOptions.masterkey.changePasswordBtn=Змяніць пароль
vaultOptions.masterkey.forgetSavedPasswordBtn=Забыцца на захаваны пароль vaultOptions.masterkey.forgetSavedPasswordBtn=Забыцца на захаваны пароль
vaultOptions.masterkey.recoveryKeyExplanation=Ключ аднаўлення – гэта адзіная мажлівасць аднавіць доступ да тваёй скарбніцы, калі ты згубіш пароль.
vaultOptions.masterkey.showRecoveryKeyBtn=Паказаць ключ аднаўлення vaultOptions.masterkey.showRecoveryKeyBtn=Паказаць ключ аднаўлення
vaultOptions.masterkey.recoverPasswordBtn=Скінуць пароль vaultOptions.masterkey.recoverPasswordBtn=Скінуць пароль
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -369,8 +437,9 @@ recoveryKey.display.StorageHints=Захоўвай іх у бяспечным м
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.title=Скінуць пароль recoveryKey.recover.title=Скінуць пароль
recoveryKey.recover.prompt=Увядзі свой ключ аднаўлення для "%s":
recoveryKey.recover.correctKey=Гэта валідны ключ аднаўлення recoveryKey.recover.correctKey=Гэта валідны ключ аднаўлення
recoveryKey.recover.wrongKey=Гэты ключ аднаўлення належыць іншай скарбніцы
recoveryKey.recover.invalidKey=Несапраўдны ключ аднаўлення
recoveryKey.printout.heading=Ключ аднаўлення Cryptomator\n"%s"\n recoveryKey.printout.heading=Ключ аднаўлення Cryptomator\n"%s"\n
### Reset Password ### Reset Password
recoveryKey.recover.resetBtn=Скінуць recoveryKey.recover.resetBtn=Скінуць
@@ -378,6 +447,8 @@ recoveryKey.recover.resetBtn=Скінуць
recoveryKey.recover.resetSuccess.message=Пароль паспяхова скінуты recoveryKey.recover.resetSuccess.message=Пароль паспяхова скінуты
recoveryKey.recover.resetSuccess.description=Ты можаш разамкнуць сваю скарбніцу з дапамогаю новага паролю. recoveryKey.recover.resetSuccess.description=Ты можаш разамкнуць сваю скарбніцу з дапамогаю новага паролю.
# Convert Vault
# New Password # New Password
newPassword.promptText=Увядзі новы пароль newPassword.promptText=Увядзі новы пароль
newPassword.reenterPassword=Пацвердзі новы пароль newPassword.reenterPassword=Пацвердзі новы пароль
@@ -398,4 +469,5 @@ quit.lockAndQuitBtn=Замкнуць ды вайсці
# Forced Quit # Forced Quit
quit.forced.message=Некаторыя скарбніцы не магчыма замкнуць quit.forced.message=Некаторыя скарбніцы не магчыма замкнуць
quit.forced.description=Замыканне скарбніц было заблакавана праз дзеючыя аперацыі альбо праз адчыненыя файлы. Ты можаш прымусова замкнуць скарбніцы, але гэта можа прывесці да страты незахаваных даных.
quit.forced.forceAndQuitBtn=Прымусіць і выйсці quit.forced.forceAndQuitBtn=Прымусіць і выйсці
@@ -97,7 +97,6 @@ hub.register.registerBtn=নিশ্চিত করুন
### Unauthorized ### Unauthorized
### License Exceeded ### License Exceeded
# Lock # Lock
## Force ## Force
lock.forced.retryBtn=পুনরায় চেষ্টা করুন lock.forced.retryBtn=পুনরায় চেষ্টা করুন
@@ -158,7 +157,7 @@ vaultOptions.general.vaultName=ভোল্ট এর নাম
## Mount ## Mount
vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন করুন… vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন করুন…
## Master Key ## Master Key
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -167,6 +166,8 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
### Reset Password ### Reset Password
### Recovery Key Password Reset Success ### Recovery Key Password Reset Success
# Convert Vault
# New Password # New Password
# Quit # Quit
+3 -10
View File
@@ -113,7 +113,6 @@ hub.register.registerBtn=Potvrdi
### Unauthorized ### Unauthorized
### License Exceeded ### License Exceeded
# Lock # Lock
## Force ## Force
lock.forced.description=Zaključavanje "%s" blokirano je operacijama na čekanju ili otvorenim datotekama. Možete prisilno zaključati ovaj sef, međutim prekid U / I-a može rezultirati gubitkom nespremljenih podataka. lock.forced.description=Zaključavanje "%s" blokirano je operacijama na čekanju ili otvorenim datotekama. Možete prisilno zaključati ovaj sef, međutim prekid U / I-a može rezultirati gubitkom nespremljenih podataka.
@@ -183,27 +182,21 @@ stats.title=Statistika za %s
stats.cacheHitRate=Brzina učitavanja u predmemoriju stats.cacheHitRate=Brzina učitavanja u predmemoriju
## Read ## Read
stats.read.throughput.idle=Čitaj: U pripravnosti stats.read.throughput.idle=Čitaj: U pripravnosti
stats.read.throughput.kibs=Čitanje: %.2f kiB/s
stats.read.throughput.mibs=Čitanje: %.2f MiB/s stats.read.throughput.mibs=Čitanje: %.2f MiB/s
stats.read.total.data.none=Pročitano podataka: - stats.read.total.data.none=Pročitano podataka: -
stats.read.total.data.kib=Pročitano podataka: %.1f kiB
stats.read.total.data.mib=Pročitano podataka: %.1f MiB stats.read.total.data.mib=Pročitano podataka: %.1f MiB
stats.read.total.data.gib=Pročitano podataka: %.1f GiB stats.read.total.data.gib=Pročitano podataka: %.1f GiB
stats.decr.total.data.none=Otključani podaci: - stats.decr.total.data.none=Otključani podaci: -
stats.decr.total.data.kib=Otključano podataka: %.1f kiB
stats.decr.total.data.mib=Otključano podataka: %.1f MiB stats.decr.total.data.mib=Otključano podataka: %.1f MiB
stats.decr.total.data.gib=Otključano podataka: %.1f GiB stats.decr.total.data.gib=Otključano podataka: %.1f GiB
stats.read.accessCount=Ukupno pročitano: %d stats.read.accessCount=Ukupno pročitano: %d
## Write ## Write
stats.write.throughput.idle=Pisanje: U pripravnosti stats.write.throughput.idle=Pisanje: U pripravnosti
stats.write.throughput.kibs=Upisano: %.2f kiB/s
stats.write.throughput.mibs=Upisano: %.2f MiB/s stats.write.throughput.mibs=Upisano: %.2f MiB/s
stats.write.total.data.none=Podaci su upisani:- stats.write.total.data.none=Podaci su upisani:-
stats.write.total.data.kib=Upisano podataka: %.1f kiB
stats.write.total.data.mib=Upisano podataka: %.1f MiB stats.write.total.data.mib=Upisano podataka: %.1f MiB
stats.write.total.data.gib=Upisano podataka: %.1f GiB stats.write.total.data.gib=Upisano podataka: %.1f GiB
stats.encr.total.data.none=Otključano podataka: - stats.encr.total.data.none=Otključano podataka: -
stats.encr.total.data.kib=Otključano podataka: %.1f kiB
stats.encr.total.data.mib=Otključano podataka: %.1f MiB stats.encr.total.data.mib=Otključano podataka: %.1f MiB
stats.encr.total.data.gib=Otključano podataka: %.1f GiB stats.encr.total.data.gib=Otključano podataka: %.1f GiB
stats.write.accessCount=Ukupno upisano: %d stats.write.accessCount=Ukupno upisano: %d
@@ -243,7 +236,6 @@ main.vaultDetail.lockBtn=Zaključaj
main.vaultDetail.bytesPerSecondRead=Čitaj: main.vaultDetail.bytesPerSecondRead=Čitaj:
main.vaultDetail.bytesPerSecondWritten=Piši: main.vaultDetail.bytesPerSecondWritten=Piši:
main.vaultDetail.throughput.idle=u stanju mirovanja main.vaultDetail.throughput.idle=u stanju mirovanja
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Statistika sefa main.vaultDetail.stats=Statistika sefa
### Missing ### Missing
@@ -291,7 +283,7 @@ vaultOptions.masterkey.changePasswordBtn=Promjeni lozinku
vaultOptions.masterkey.forgetSavedPasswordBtn=Zaborav spremljenu šifru vaultOptions.masterkey.forgetSavedPasswordBtn=Zaborav spremljenu šifru
vaultOptions.masterkey.recoveryKeyExplanation=Ključ za oporavak je vaše jedino sredstvo za vraćanje pristupa sefu ako izgubite lozinku. vaultOptions.masterkey.recoveryKeyExplanation=Ključ za oporavak je vaše jedino sredstvo za vraćanje pristupa sefu ako izgubite lozinku.
vaultOptions.masterkey.showRecoveryKeyBtn=Pokaži ključ za oporavak vaultOptions.masterkey.showRecoveryKeyBtn=Pokaži ključ za oporavak
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -300,12 +292,13 @@ recoveryKey.display.description=Sljedeći ključ za oporavak može se koristiti
recoveryKey.display.StorageHints=Pohranite ga negdje vrlo sigurno, npr.:\n • Spremite ga pomoću menadžera lozinki\n • Spremite ga na USB disk\n • Odštampajte ga na papiru recoveryKey.display.StorageHints=Pohranite ga negdje vrlo sigurno, npr.:\n • Spremite ga pomoću menadžera lozinki\n • Spremite ga na USB disk\n • Odštampajte ga na papiru
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.prompt=Unesite ključ za oporavak za "%s":
recoveryKey.recover.correctKey=Ključ za oporavak je ispravan recoveryKey.recover.correctKey=Ključ za oporavak je ispravan
recoveryKey.printout.heading=Cryptomator Ključ za oporavak za \n"%s"\n recoveryKey.printout.heading=Cryptomator Ključ za oporavak za \n"%s"\n
### Reset Password ### Reset Password
### Recovery Key Password Reset Success ### Recovery Key Password Reset Success
# Convert Vault
# New Password # New Password
newPassword.promptText=Unesi novu šifru newPassword.promptText=Unesi novu šifru
newPassword.reenterPassword=Potvrdi novu šifru newPassword.reenterPassword=Potvrdi novu šifru
+15 -13
View File
@@ -154,9 +154,8 @@ hub.registerFailed.description=S'ha produït un error en el procés de nomenamen
hub.unauthorized.message=Accés denegat hub.unauthorized.message=Accés denegat
hub.unauthorized.description=El vostre dispositiu no ha estat encara autoritzat a accedir a aquesta caixa forta. Demaneu autorització al propietari. hub.unauthorized.description=El vostre dispositiu no ha estat encara autoritzat a accedir a aquesta caixa forta. Demaneu autorització al propietari.
### License Exceeded ### License Exceeded
hub.licenseExceeded.message=Llicència caducada hub.invalidLicense.message=La llicència del Hub no és vàlida
hub.licenseExceeded.description=Cryptomator Hub ha donat accés a més usuaris dels permesos en la llicència. Si us plau contacteu amb l'administrador del Hub per actualitzar la llicència o amb l'administrador de la caixa forta per treure usuaris. hub.invalidLicense.description=Aquest Cryptomator Hub no té una llicència vàlida. Informa si us plau a l'administrador perquè actualitzi o renovi la llicència.
# Lock # Lock
## Force ## Force
@@ -275,8 +274,8 @@ preferences.interface.showMinimizeButton=Mostra el botó 'minimitzar'
preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar) preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
## Volume ## Volume
preferences.volume=Unitat virtual preferences.volume=Unitat virtual
preferences.volume.type=Tipus de volum (requereix reiniciar)
preferences.volume.type.automatic=Automàtic preferences.volume.type.automatic=Automàtic
preferences.volume.docsTooltip=Obre la documentació per aprendre més sobre els diferents tipus de volums.
preferences.volume.tcp.port=Port TCP preferences.volume.tcp.port=Port TCP
preferences.volume.supportedFeatures=El tipus de volum escollit suporta les següents característiques: preferences.volume.supportedFeatures=El tipus de volum escollit suporta les següents característiques:
preferences.volume.feature.mountAuto=Selecció automàtica del punt de muntatge preferences.volume.feature.mountAuto=Selecció automàtica del punt de muntatge
@@ -306,27 +305,27 @@ stats.title=Estadístiques per a %s
stats.cacheHitRate=Relació d'encerts de la memòria cau stats.cacheHitRate=Relació d'encerts de la memòria cau
## Read ## Read
stats.read.throughput.idle=Llegit: inactiu stats.read.throughput.idle=Llegit: inactiu
stats.read.throughput.kibs=Llegit: %.2f kiB/s stats.read.throughput.kibs=Lectura: %.2f KiB/s
stats.read.throughput.mibs=Llegit: %.2f MiB/s stats.read.throughput.mibs=Llegit: %.2f MiB/s
stats.read.total.data.none=Dades llegides: - stats.read.total.data.none=Dades llegides: -
stats.read.total.data.kib=Dades llegides: %.1f kiB stats.read.total.data.kib=Dades llegides: %.1f KiB
stats.read.total.data.mib=Dades llegides: %.1f MiB stats.read.total.data.mib=Dades llegides: %.1f MiB
stats.read.total.data.gib=Dades llegides: %.1f GiB stats.read.total.data.gib=Dades llegides: %.1f GiB
stats.decr.total.data.none=Dades desxifrades: - stats.decr.total.data.none=Dades desxifrades: -
stats.decr.total.data.kib=Dades desxifrades: %.1f kiB stats.decr.total.data.kib=Dades desxifrades: %.1f KiB
stats.decr.total.data.mib=Dades desxifrades: %.1f MiB stats.decr.total.data.mib=Dades desxifrades: %.1f MiB
stats.decr.total.data.gib=Dades desxifrades: %.1f GiB stats.decr.total.data.gib=Dades desxifrades: %.1f GiB
stats.read.accessCount=Lectures en total: %d stats.read.accessCount=Lectures en total: %d
## Write ## Write
stats.write.throughput.idle=Escriu: inactiu stats.write.throughput.idle=Escriu: inactiu
stats.write.throughput.kibs=Escriu: %.2f kiB/s stats.write.throughput.kibs=Escriptura: %.2f KiB/s
stats.write.throughput.mibs=Escriu: %.2f MiB/s stats.write.throughput.mibs=Escriu: %.2f MiB/s
stats.write.total.data.none=Dades escrites stats.write.total.data.none=Dades escrites
stats.write.total.data.kib=Dades escrites: %.1f kiB stats.write.total.data.kib=Dades escrites: %.1f KiB
stats.write.total.data.mib=Dades escrites: %.1f MiB stats.write.total.data.mib=Dades escrites: %.1f MiB
stats.write.total.data.gib=Dades escrites: %.1f GiB stats.write.total.data.gib=Dades escrites: %.1f GiB
stats.encr.total.data.none=Dades xifrades: - stats.encr.total.data.none=Dades xifrades: -
stats.encr.total.data.kib=Dades xifrades: %.1f kiB stats.encr.total.data.kib=Dades xifrades: %.1f KiB
stats.encr.total.data.mib=Dades xifrades: %.1f MiB stats.encr.total.data.mib=Dades xifrades: %.1f MiB
stats.encr.total.data.gib=Dades xifrades: %.1f GiB stats.encr.total.data.gib=Dades xifrades: %.1f GiB
stats.write.accessCount=Total escrits: %d stats.write.accessCount=Total escrits: %d
@@ -369,7 +368,7 @@ main.vaultDetail.lockBtn=Bloqueja
main.vaultDetail.bytesPerSecondRead=Lectura: main.vaultDetail.bytesPerSecondRead=Lectura:
main.vaultDetail.bytesPerSecondWritten=Escriu: main.vaultDetail.bytesPerSecondWritten=Escriu:
main.vaultDetail.throughput.idle=inactiu main.vaultDetail.throughput.idle=inactiu
main.vaultDetail.throughput.kbps=%.1f kiB/s main.vaultDetail.throughput.kbps=%.1f KiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Estadístiques de la caixa forta main.vaultDetail.stats=Estadístiques de la caixa forta
main.vaultDetail.locateEncryptedFileBtn=Trobar fitxer xifrat main.vaultDetail.locateEncryptedFileBtn=Trobar fitxer xifrat
@@ -432,7 +431,7 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Oblida la contrasenya desada
vaultOptions.masterkey.recoveryKeyExplanation=La clau de recuperació és l'unic mitjà de restaurar l'accès a la caixa forta en cas de perdre la contrasenya. vaultOptions.masterkey.recoveryKeyExplanation=La clau de recuperació és l'unic mitjà de restaurar l'accès a la caixa forta en cas de perdre la contrasenya.
vaultOptions.masterkey.showRecoveryKeyBtn=Mostra la clau de recuperació vaultOptions.masterkey.showRecoveryKeyBtn=Mostra la clau de recuperació
vaultOptions.masterkey.recoverPasswordBtn=Canviar contrasenya vaultOptions.masterkey.recoverPasswordBtn=Canviar contrasenya
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -444,8 +443,9 @@ recoveryKey.display.StorageHints=Conserveu-la en un lloc molt segur. P. ex.:\n
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.title=Canviar contrasenya recoveryKey.recover.title=Canviar contrasenya
recoveryKey.recover.prompt=Introduïu la vostra clau de recuperació de "%s":
recoveryKey.recover.correctKey=La clau de recuperació és vàlida recoveryKey.recover.correctKey=La clau de recuperació és vàlida
recoveryKey.recover.wrongKey=Aquesta clau de recuperació pertany a una caixa forta diferent
recoveryKey.recover.invalidKey=Aquesta clau de recuperació no és vàlida
recoveryKey.printout.heading=Clau de recuperació de Cryptomator\n"%s"\n recoveryKey.printout.heading=Clau de recuperació de Cryptomator\n"%s"\n
### Reset Password ### Reset Password
recoveryKey.recover.resetBtn=Reinicia recoveryKey.recover.resetBtn=Reinicia
@@ -453,6 +453,8 @@ recoveryKey.recover.resetBtn=Reinicia
recoveryKey.recover.resetSuccess.message=S'ha modificat la contrasenya correctament recoveryKey.recover.resetSuccess.message=S'ha modificat la contrasenya correctament
recoveryKey.recover.resetSuccess.description=Pots desbloquejar la caixa forta amb la nova contrasenya. recoveryKey.recover.resetSuccess.description=Pots desbloquejar la caixa forta amb la nova contrasenya.
# Convert Vault
# New Password # New Password
newPassword.promptText=Introdueix una contrasenya nova newPassword.promptText=Introdueix una contrasenya nova
newPassword.reenterPassword=Confirma la nova contrasenya newPassword.reenterPassword=Confirma la nova contrasenya
+3 -12
View File
@@ -142,9 +142,6 @@ hub.registerFailed.message=Pojmenování zařízení se nezdařilo
hub.unauthorized.message=Přístup odepřen hub.unauthorized.message=Přístup odepřen
hub.unauthorized.description=Vaše zařízení dosud nebylo oprávněno k přístupu k tomuto trezoru. Požádejte vlastníka trezoru, aby jej autorizoval. hub.unauthorized.description=Vaše zařízení dosud nebylo oprávněno k přístupu k tomuto trezoru. Požádejte vlastníka trezoru, aby jej autorizoval.
### License Exceeded ### License Exceeded
hub.licenseExceeded.message=Licence byla překročena
hub.licenseExceeded.description=Cryptomator Hub poskytl přístup více uživatelům, než jsou jeho licenční povolení. Obraťte se na správce Hubu pro aktualizaci licence nebo správce trezoru pro odstranění uživatelů z trezoru.
# Lock # Lock
## Force ## Force
@@ -275,27 +272,21 @@ stats.title=Statistika pro %s
stats.cacheHitRate=Rychlost dosažené mezipaměti stats.cacheHitRate=Rychlost dosažené mezipaměti
## Read ## Read
stats.read.throughput.idle=Čtení: nečinné stats.read.throughput.idle=Čtení: nečinné
stats.read.throughput.kibs=Čtení: %.2f kiB/s
stats.read.throughput.mibs=Čtení: %.2f MiB/s stats.read.throughput.mibs=Čtení: %.2f MiB/s
stats.read.total.data.none=Přečteno: - stats.read.total.data.none=Přečteno: -
stats.read.total.data.kib=Přečteno: %.1f kiB
stats.read.total.data.mib=Přečteno: %.1f MiB stats.read.total.data.mib=Přečteno: %.1f MiB
stats.read.total.data.gib=Přečteno: %.1f GiB stats.read.total.data.gib=Přečteno: %.1f GiB
stats.decr.total.data.none=Dešifrováno: - stats.decr.total.data.none=Dešifrováno: -
stats.decr.total.data.kib=Dešifrováno: %.1f kiB
stats.decr.total.data.mib=Dešifrováno: %.1f MiB stats.decr.total.data.mib=Dešifrováno: %.1f MiB
stats.decr.total.data.gib=Dešifrováno: %.1f GiB stats.decr.total.data.gib=Dešifrováno: %.1f GiB
stats.read.accessCount=Celkem přečteno: %d stats.read.accessCount=Celkem přečteno: %d
## Write ## Write
stats.write.throughput.idle=Zápis: nečinný stats.write.throughput.idle=Zápis: nečinný
stats.write.throughput.kibs=Zápis: %.2f kiB/s
stats.write.throughput.mibs=Zápis: %.2f MiB/s stats.write.throughput.mibs=Zápis: %.2f MiB/s
stats.write.total.data.none=Zapsaná data: - stats.write.total.data.none=Zapsaná data: -
stats.write.total.data.kib=Zapsáno: %.1f kiB
stats.write.total.data.mib=Zapsáno: %.1f MiB stats.write.total.data.mib=Zapsáno: %.1f MiB
stats.write.total.data.gib=Zapsáno: %.1f GiB stats.write.total.data.gib=Zapsáno: %.1f GiB
stats.encr.total.data.none=Zašifrováno: - stats.encr.total.data.none=Zašifrováno: -
stats.encr.total.data.kib=Zašifrováno: %.1f kiB
stats.encr.total.data.mib=Zašifrováno: %.1f MiB stats.encr.total.data.mib=Zašifrováno: %.1f MiB
stats.encr.total.data.gib=Zašifrováno: %.1f GiB stats.encr.total.data.gib=Zašifrováno: %.1f GiB
stats.write.accessCount=Celkem zapsáno: %d stats.write.accessCount=Celkem zapsáno: %d
@@ -337,7 +328,6 @@ main.vaultDetail.lockBtn=Zamknout
main.vaultDetail.bytesPerSecondRead=Přečteno: main.vaultDetail.bytesPerSecondRead=Přečteno:
main.vaultDetail.bytesPerSecondWritten=Zapsáno: main.vaultDetail.bytesPerSecondWritten=Zapsáno:
main.vaultDetail.throughput.idle=nečinný main.vaultDetail.throughput.idle=nečinný
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Statistiky trezoru main.vaultDetail.stats=Statistiky trezoru
### Missing ### Missing
@@ -392,7 +382,7 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Zapomenout uložené heslo
vaultOptions.masterkey.recoveryKeyExplanation=Obnovovací klíč je váš jediný způsob, jak obnovit přístup k trezoru, pokud ztratíte své heslo. vaultOptions.masterkey.recoveryKeyExplanation=Obnovovací klíč je váš jediný způsob, jak obnovit přístup k trezoru, pokud ztratíte své heslo.
vaultOptions.masterkey.showRecoveryKeyBtn=Zobrazit klíč k obnově vaultOptions.masterkey.showRecoveryKeyBtn=Zobrazit klíč k obnově
vaultOptions.masterkey.recoverPasswordBtn=Resetovat heslo vaultOptions.masterkey.recoverPasswordBtn=Resetovat heslo
## Hub
# Recovery Key # Recovery Key
## Display Recovery Key ## Display Recovery Key
@@ -404,13 +394,14 @@ recoveryKey.display.StorageHints=Uchovejte ho někde velmi bezpečně, např.\n
## Reset Password ## Reset Password
### Enter Recovery Key ### Enter Recovery Key
recoveryKey.recover.title=Resetovat heslo recoveryKey.recover.title=Resetovat heslo
recoveryKey.recover.prompt=Zadejte váš obnovovací klíč pro "%s":
recoveryKey.recover.correctKey=Toto je platný obnovovací klíč recoveryKey.recover.correctKey=Toto je platný obnovovací klíč
recoveryKey.printout.heading=Obnovovací klíč Cryptomator\n"%s"\n recoveryKey.printout.heading=Obnovovací klíč Cryptomator\n"%s"\n
### Reset Password ### Reset Password
recoveryKey.recover.resetBtn=Resetovat recoveryKey.recover.resetBtn=Resetovat
### Recovery Key Password Reset Success ### Recovery Key Password Reset Success
# Convert Vault
# New Password # New Password
newPassword.promptText=Zadejte nové heslo newPassword.promptText=Zadejte nové heslo
newPassword.reenterPassword=Potvrď nové heslo newPassword.reenterPassword=Potvrď nové heslo

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