mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
177
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d013497c48 | ||
|
|
8691eec42b | ||
|
|
0f9d572677 | ||
|
|
baee23b6d4 | ||
|
|
532ffb1202 | ||
|
|
2a704d5eb4 | ||
|
|
e8f8466d9a | ||
|
|
9297562c99 | ||
|
|
7d62fc78de | ||
|
|
8e7e7de358 | ||
|
|
10c60d7492 | ||
|
|
aa03bd119a | ||
|
|
325ffda9af | ||
|
|
d1270ceeb2 | ||
|
|
7bfd0ed341 | ||
|
|
901a290dd9 | ||
|
|
35b9dadfc2 | ||
|
|
5f57678edc | ||
|
|
30e1922bc9 | ||
|
|
2e0908ab15 | ||
|
|
689ce5b985 | ||
|
|
a71a23aa31 | ||
|
|
864454e6fc | ||
|
|
94c3381723 | ||
|
|
d9f945e70a | ||
|
|
dc9b39202f | ||
|
|
2a01aba3cf | ||
|
|
4305fd3285 | ||
|
|
a24cd1ba7f | ||
|
|
2ba0d963ec | ||
|
|
cd0c6fbd33 | ||
|
|
f4374a2606 | ||
|
|
a1d5b8a4e2 | ||
|
|
34e430aff6 | ||
|
|
c79766cdf6 | ||
|
|
bf76bad626 | ||
|
|
c3f654b454 | ||
|
|
d1d990d47c | ||
|
|
6052c0589e | ||
|
|
db2560fccf | ||
|
|
3a50c32e50 | ||
|
|
65eca31d26 | ||
|
|
3462e0b540 | ||
|
|
697529136e | ||
|
|
50d31bdc18 | ||
|
|
84caf96d3f | ||
|
|
a1a5fd3609 | ||
|
|
6c11cc8f1d | ||
|
|
2b391a6ee3 | ||
|
|
b7fc03213d | ||
|
|
dfe17569e1 | ||
|
|
827f9ad141 | ||
|
|
c8a6d0339e | ||
|
|
b5bbd21f25 | ||
|
|
771468c8c6 | ||
|
|
ea2a48771f | ||
|
|
0e10da25b3 | ||
|
|
943a3e9cfd | ||
|
|
c988fb50a7 | ||
|
|
219ee0da9a | ||
|
|
5665e92839 | ||
|
|
04ff188624 | ||
|
|
ec7d6eafec | ||
|
|
28bb2ff9b1 | ||
|
|
a92ebfdc7b | ||
|
|
f1e97fa64b | ||
|
|
b9d5cf04c2 | ||
|
|
75cd3e44d8 | ||
|
|
3cf1b829b8 | ||
|
|
6e4e9cd261 | ||
|
|
e15dd7565f | ||
|
|
77bc60fe5b | ||
|
|
9f633a1ecb | ||
|
|
fcf59d12a8 | ||
|
|
6721075831 | ||
|
|
4bb0026415 | ||
|
|
bebae14744 | ||
|
|
997315eaf5 | ||
|
|
16d677c40f | ||
|
|
42a1913c17 | ||
|
|
c3f6655e48 | ||
|
|
fa1b0f2de8 | ||
|
|
385574a618 | ||
|
|
a67477bf3b | ||
|
|
03a362e9b4 | ||
|
|
2223bc5e78 | ||
|
|
fdc0d2d6b5 | ||
|
|
58ed48b097 | ||
|
|
7a0d255bd3 | ||
|
|
228fa099cb | ||
|
|
a60ff20f15 | ||
|
|
2328a5e3a8 | ||
|
|
e7e181b1a5 | ||
|
|
f3e7c08b43 | ||
|
|
2a41afcfa9 | ||
|
|
5bf38a328c | ||
|
|
f983b29034 | ||
|
|
4bc217e489 | ||
|
|
b24f52a4ec | ||
|
|
91e7fa3de3 | ||
|
|
1365efab3f | ||
|
|
852963b785 | ||
|
|
c47d4eaf02 | ||
|
|
36da26c6b9 | ||
|
|
33e1cb3131 | ||
|
|
ed4b433df0 | ||
|
|
d2ece543c8 | ||
|
|
4bb2df7d8f | ||
|
|
4d5c6ca29a | ||
|
|
f0d597ba73 | ||
|
|
310329d0b5 | ||
|
|
30f86a7f2a | ||
|
|
5237dc8cae | ||
|
|
67ff4da05b | ||
|
|
947eb11198 | ||
|
|
c6a9af1a04 | ||
|
|
0ddf221e26 | ||
|
|
3f2b5ccde9 | ||
|
|
4fc07c27b3 | ||
|
|
3d0b6dde28 | ||
|
|
45534b0a2b | ||
|
|
6d906beeb8 | ||
|
|
e577c64c35 | ||
|
|
b848e78372 | ||
|
|
39e9395b4b | ||
|
|
ca3157cc70 | ||
|
|
f94c1f07d6 | ||
|
|
d43f197c11 | ||
|
|
67e566832c | ||
|
|
6f5a88af1d | ||
|
|
210b891616 | ||
|
|
20e820c903 | ||
|
|
5307e93c1b | ||
|
|
ec1d25bf65 | ||
|
|
7aa554498b | ||
|
|
85ac3c244d | ||
|
|
5db5346c88 | ||
|
|
e221019b57 | ||
|
|
eeaaced976 | ||
|
|
d246b54268 | ||
|
|
8f4bf144c3 | ||
|
|
e94572960e | ||
|
|
2d1c59bea9 | ||
|
|
921cbd6439 | ||
|
|
f811144ffa | ||
|
|
2ecc711f50 | ||
|
|
cfcc8782ef | ||
|
|
a1379bac40 | ||
|
|
fe3f2b5bd3 | ||
|
|
7fc07ae02c | ||
|
|
268c66a108 | ||
|
|
2a70e2f0f4 | ||
|
|
ffe0302fba | ||
|
|
c5b86bc355 | ||
|
|
5f86473e3e | ||
|
|
a91c74b6eb | ||
|
|
76949d6ccb | ||
|
|
71a09eae67 | ||
|
|
8087fcecfb | ||
|
|
29c73e1bc8 | ||
|
|
8bbdb69cda | ||
|
|
e9ee15dcd5 | ||
|
|
03f6e0a33c | ||
|
|
c5b21b0d8c | ||
|
|
c5c5e297b7 | ||
|
|
24839985e9 | ||
|
|
a7b2802f34 | ||
|
|
c85d0b4beb | ||
|
|
3dff3a8664 | ||
|
|
5b0bbf539b | ||
|
|
1ff7c40fd9 | ||
|
|
d0875ef49e | ||
|
|
e35ed9af72 | ||
|
|
ac43efe149 | ||
|
|
202a2ea79f | ||
|
|
c19a86a348 | ||
|
|
539dcaa85a |
@@ -37,12 +37,17 @@ body:
|
|||||||
id: volume-type
|
id: volume-type
|
||||||
attributes:
|
attributes:
|
||||||
label: Volume Type
|
label: Volume Type
|
||||||
description: What is selected under Settings → Virtual Drive?
|
description: What volume type is selected under Settings → Virtual Drive?
|
||||||
multiple: true
|
|
||||||
options:
|
options:
|
||||||
- FUSE
|
- WinFsp
|
||||||
- Dokany
|
- WinFsp (Local Drive)
|
||||||
- WebDAV
|
- FUSE-T
|
||||||
|
- macFUSE
|
||||||
|
- WebDAV (Windows Explorer)
|
||||||
|
- WebDAV (AppleScript)
|
||||||
|
- WebDAV (gio)
|
||||||
|
- WebDAV (HTTP Address)
|
||||||
|
- Dokany (1.5)
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -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
|
||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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: -->
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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'
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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.'
|
||||||
@@ -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
|
||||||
@@ -156,7 +182,7 @@ jobs:
|
|||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||||
--win-menu
|
--win-menu
|
||||||
--win-dir-chooser
|
--win-dir-chooser
|
||||||
--win-shortcut-prompt
|
--win-shortcut-prompt
|
||||||
@@ -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: >
|
||||||
|
|||||||
@@ -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
|
|
||||||
Generated
+6
-4
@@ -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" />
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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"/>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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 \
|
||||||
|
|||||||
Vendored
+6
-5
@@ -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.
Vendored
+8
@@ -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>
|
||||||
Vendored
+5
-5
@@ -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 `
|
||||||
@@ -129,7 +129,7 @@ $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
|||||||
--name $AppName `
|
--name $AppName `
|
||||||
--vendor $Vendor `
|
--vendor $Vendor `
|
||||||
--copyright $copyright `
|
--copyright $copyright `
|
||||||
--app-version "$semVerNo" `
|
--app-version "$semVerNo.$revisionNo" `
|
||||||
--win-menu `
|
--win-menu `
|
||||||
--win-dir-chooser `
|
--win-dir-chooser `
|
||||||
--win-shortcut-prompt `
|
--win-shortcut-prompt `
|
||||||
@@ -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
|
||||||
Vendored
+19
-1
@@ -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 "WinFsp"
|
||||||
|
3. Uninstall the listed application
|
||||||
|
4. Reboot your device
|
||||||
|
5. Restart this installer">(InstalledWinFspVersion = v0.0.0.0) OR ($(var.BundledWinFspVersion) <= 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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[InternetShortcut]
|
|
||||||
URL=https://github.com/winfsp/winfsp/releases/download/v1.12/winfsp-1.12.22301.msi
|
|
||||||
+3
-13
@@ -7,19 +7,9 @@
|
|||||||
|
|
||||||
#Get all active, local user profiles
|
#Get all active, local user profiles
|
||||||
$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
|
$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
|
||||||
$localUsers = Get-LocalUser | Where-Object {$_.Enabled} | ForEach-Object { $_.Name}
|
Get-ChildItem $profileList | ForEach-Object {
|
||||||
|
$profilePath = $_.GetValue("ProfileImagePath")
|
||||||
Get-ChildItem $profileList | ForEach-Object { $_.GetValue("ProfileImagePath") } | Where-Object {
|
$settingsPath = "$profilePath\AppData\Roaming\Cryptomator\settings.json"
|
||||||
$profileNameMatches = ($_ | Select-String -Pattern "\\([^\\]+)$").Matches
|
|
||||||
if($profileNameMatches.Count -eq 1) {
|
|
||||||
#check if the last path part is contained in the local user name list
|
|
||||||
#otherwise do not touch it
|
|
||||||
return $localUsers.Contains($profileNameMatches[0].Groups[1].Value)
|
|
||||||
} else {
|
|
||||||
return $false;
|
|
||||||
}
|
|
||||||
} | ForEach-Object {
|
|
||||||
$settingsPath = "$_\AppData\Roaming\Cryptomator\settings.json"
|
|
||||||
if(!(Test-Path -Path $settingsPath -PathType Leaf)) {
|
if(!(Test-Path -Path $settingsPath -PathType Leaf)) {
|
||||||
#No settings file, nothing to do.
|
#No settings file, nothing to do.
|
||||||
return;
|
return;
|
||||||
|
|||||||
Vendored
+4
-4
@@ -54,7 +54,7 @@
|
|||||||
Property="JP_UPGRADABLE_FOUND"
|
Property="JP_UPGRADABLE_FOUND"
|
||||||
Maximum="$(var.JpAppVersion)"
|
Maximum="$(var.JpAppVersion)"
|
||||||
MigrateFeatures="yes"
|
MigrateFeatures="yes"
|
||||||
IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
|
IncludeMaximum="yes" /> <!-- TODO: check if this needs to be set to yes-->
|
||||||
<UpgradeVersion
|
<UpgradeVersion
|
||||||
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
|
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
|
||||||
Property="JP_DOWNGRADABLE_FOUND"
|
Property="JP_DOWNGRADABLE_FOUND"
|
||||||
@@ -143,11 +143,11 @@
|
|||||||
CloseMessage="no"
|
CloseMessage="no"
|
||||||
RebootPrompt="no"
|
RebootPrompt="no"
|
||||||
PromptToContinue="yes"
|
PromptToContinue="yes"
|
||||||
Description="A running instance of $(var.JpAppName) is found. Please close it to continue."
|
Description="A running instance of $(var.JpAppName) is found, using files marked for update. Please close it to continue."
|
||||||
Property="FOUNDRUNNINGAPP"
|
Property="FOUNDRUNNINGAPP"
|
||||||
>
|
>
|
||||||
</util:CloseApplication>
|
</util:CloseApplication>
|
||||||
<CustomAction Id="FailOnRunningApp" Impersonate="no" ExeCommand="[SystemFolder]\cmd.exe /c "exit 1"" Directory="INSTALLDIR" Execute="immediate" Return="check" />
|
<CustomAction Id="FailOnRunningApp" Error="Installation aborted, because files marked for update are used by a running instance of $(var.JpAppName)."/>
|
||||||
|
|
||||||
<?ifdef JpIcon ?>
|
<?ifdef JpIcon ?>
|
||||||
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
|
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
||||||
<Custom Action="FailOnRunningApp" After="WixCloseApplications" >FOUNDRUNNINGAPP</Custom>
|
<Custom Action="FailOnRunningApp" After="WixCloseApplications" >FOUNDRUNNINGAPP</Custom>
|
||||||
|
|
||||||
<RemoveExistingProducts After="InstallValidate"/>
|
<RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to WixCloseApplications -->
|
||||||
|
|
||||||
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||||
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||||
|
|||||||
@@ -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,49 +33,42 @@
|
|||||||
<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-beta4</cryptomator.integrations.version>
|
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.win.version>1.2.0-beta2</cryptomator.integrations.win.version>
|
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.0-beta2</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.integrations.linux.version>1.2.0-beta1</cryptomator.integrations.linux.version>
|
<cryptomator.fuse.version>2.0.5</cryptomator.fuse.version>
|
||||||
<cryptomator.fuse.version>2.0.0-beta5</cryptomator.fuse.version>
|
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||||
<cryptomator.dokany.version>2.0.0-beta2</cryptomator.dokany.version>
|
<cryptomator.webdav.version>2.0.2</cryptomator.webdav.version>
|
||||||
<cryptomator.webdav.version>2.0.0-beta4</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>
|
||||||
<dagger.version>2.44</dagger.version>
|
<dagger.version>2.45</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<guava.version>31.1-jre</guava.version>
|
<guava.version>31.1-jre</guava.version>
|
||||||
<gson.version>2.9.1</gson.version>
|
<gson.version>2.10.1</gson.version>
|
||||||
<javafx.version>19</javafx.version>
|
<javafx.version>19.0.2.1</javafx.version>
|
||||||
<jwt.version>4.2.1</jwt.version>
|
<jwt.version>4.3.0</jwt.version>
|
||||||
<nimbus-jose.version>9.25.4</nimbus-jose.version>
|
<nimbus-jose.version>9.31</nimbus-jose.version>
|
||||||
<logback.version>1.4.4</logback.version>
|
<logback.version>1.4.5</logback.version>
|
||||||
<slf4j.version>2.0.3</slf4j.version>
|
<slf4j.version>2.0.6</slf4j.version>
|
||||||
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.7.0</zxcvbn.version>
|
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.9.1</junit.jupiter.version>
|
<junit.jupiter.version>5.9.2</junit.jupiter.version>
|
||||||
<mockito.version>4.8.0</mockito.version>
|
<mockito.version>5.1.1</mockito.version>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>2.2</hamcrest.version>
|
||||||
|
|
||||||
<!-- 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>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.cryptomator.common.settings.SettingsProvider;
|
|||||||
import org.cryptomator.common.vaults.VaultComponent;
|
import org.cryptomator.common.vaults.VaultComponent;
|
||||||
import org.cryptomator.common.vaults.VaultListModule;
|
import org.cryptomator.common.vaults.VaultListModule;
|
||||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||||
|
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ import java.net.InetSocketAddress;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.SynchronousQueue;
|
import java.util.concurrent.SynchronousQueue;
|
||||||
@@ -83,6 +85,13 @@ public abstract class CommonsModule {
|
|||||||
return new SemVerComparator();
|
return new SemVerComparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static Optional<RevealPathService> provideRevealPathService() {
|
||||||
|
return RevealPathService.get().findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static Settings provideSettings(SettingsProvider settingsProvider) {
|
static Settings provideSettings(SettingsProvider settingsProvider) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import javafx.beans.value.ObservableValue;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
|
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
|
||||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
|
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
|
||||||
@@ -60,7 +59,7 @@ public class Mounter {
|
|||||||
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
|
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
|
||||||
case MOUNT_FLAGS -> {
|
case MOUNT_FLAGS -> {
|
||||||
var mountFlags = vaultSettings.mountFlags().get();
|
var mountFlags = vaultSettings.mountFlags().get();
|
||||||
if( mountFlags == null || mountFlags.isBlank()) {
|
if (mountFlags == null || mountFlags.isBlank()) {
|
||||||
builder.setMountFlags(service.getDefaultMountFlags());
|
builder.setMountFlags(service.getDefaultMountFlags());
|
||||||
} else {
|
} else {
|
||||||
builder.setMountFlags(mountFlags);
|
builder.setMountFlags(mountFlags);
|
||||||
@@ -81,9 +80,10 @@ public class Mounter {
|
|||||||
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
|
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
|
||||||
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
|
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
|
||||||
var canMountToDir = service.hasCapability(MOUNT_TO_EXISTING_DIR);
|
var canMountToDir = service.hasCapability(MOUNT_TO_EXISTING_DIR);
|
||||||
|
var canMountToSystem = service.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH);
|
||||||
|
|
||||||
if (userChosenMountPoint == null) {
|
if (userChosenMountPoint == null) {
|
||||||
if (service.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
|
if (canMountToSystem) {
|
||||||
// no need to set a mount point
|
// no need to set a mount point
|
||||||
} else if (canMountToDriveLetter) {
|
} else if (canMountToDriveLetter) {
|
||||||
builder.setMountpoint(driveLetters.getFirstDesiredAvailable().orElseThrow()); //TODO: catch exception and translate
|
builder.setMountpoint(driveLetters.getFirstDesiredAvailable().orElseThrow()); //TODO: catch exception and translate
|
||||||
@@ -105,8 +105,11 @@ public class Mounter {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
builder.setMountpoint(userChosenMountPoint);
|
builder.setMountpoint(userChosenMountPoint);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException | UnsupportedOperationException e) {
|
||||||
var configNotSupported = (!canMountToDriveLetter && mpIsDriveLetter) || (!canMountToDir && !mpIsDriveLetter) || (!canMountToParent && !mpIsDriveLetter);
|
var configNotSupported = (!canMountToDriveLetter && mpIsDriveLetter) //mounting as driveletter, albeit not supported
|
||||||
|
|| (!canMountToDir && !mpIsDriveLetter) //mounting to directory, albeit not supported
|
||||||
|
|| (!canMountToParent && !mpIsDriveLetter) //
|
||||||
|
|| (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter);
|
||||||
if (configNotSupported) {
|
if (configNotSupported) {
|
||||||
throw new MountPointNotSupportedException(e.getMessage());
|
throw new MountPointNotSupportedException(e.getMessage());
|
||||||
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
|
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
|
||||||
|
|||||||
@@ -37,30 +37,30 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Settings value) throws IOException {
|
public void write(JsonWriter out, Settings value) throws IOException {
|
||||||
out.beginObject();
|
out.beginObject();
|
||||||
out.name("lastWrittenByVersion").value(env.getAppVersion()+env.getBuildNumber().map("-"::concat).orElse(""));
|
out.name("writtenByVersion").value(env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse(""));
|
||||||
out.name("directories");
|
out.name("directories");
|
||||||
writeVaultSettingsArray(out, value.getDirectories());
|
writeVaultSettingsArray(out, value.getDirectories());
|
||||||
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
|
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
|
||||||
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
|
|
||||||
out.name("startHidden").value(value.startHidden().get());
|
|
||||||
out.name("autoCloseVaults").value(value.autoCloseVaults().get());
|
out.name("autoCloseVaults").value(value.autoCloseVaults().get());
|
||||||
out.name("port").value(value.port().get());
|
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
|
||||||
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
|
||||||
out.name("debugMode").value(value.debugMode().get());
|
out.name("debugMode").value(value.debugMode().get());
|
||||||
out.name("theme").value(value.theme().get().name());
|
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
|
||||||
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
|
|
||||||
out.name("keychainProvider").value(value.keychainProvider().get());
|
out.name("keychainProvider").value(value.keychainProvider().get());
|
||||||
out.name("useKeychain").value(value.useKeychain().get());
|
out.name("language").value((value.languageProperty().get()));
|
||||||
out.name("licenseKey").value(value.licenseKey().get());
|
out.name("licenseKey").value(value.licenseKey().get());
|
||||||
|
out.name("mountService").value(value.mountService().get());
|
||||||
|
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
||||||
|
out.name("port").value(value.port().get());
|
||||||
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
|
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
|
||||||
out.name("showTrayIcon").value(value.showTrayIcon().get());
|
out.name("showTrayIcon").value(value.showTrayIcon().get());
|
||||||
|
out.name("startHidden").value(value.startHidden().get());
|
||||||
|
out.name("theme").value(value.theme().get().name());
|
||||||
|
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
|
||||||
|
out.name("useKeychain").value(value.useKeychain().get());
|
||||||
|
out.name("windowHeight").value((value.windowHeightProperty().get()));
|
||||||
|
out.name("windowWidth").value((value.windowWidthProperty().get()));
|
||||||
out.name("windowXPosition").value((value.windowXPositionProperty().get()));
|
out.name("windowXPosition").value((value.windowXPositionProperty().get()));
|
||||||
out.name("windowYPosition").value((value.windowYPositionProperty().get()));
|
out.name("windowYPosition").value((value.windowYPositionProperty().get()));
|
||||||
out.name("windowWidth").value((value.windowWidthProperty().get()));
|
|
||||||
out.name("windowHeight").value((value.windowHeightProperty().get()));
|
|
||||||
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
|
|
||||||
out.name("language").value((value.languageProperty().get()));
|
|
||||||
out.name("mountService").value(value.mountService().get());
|
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,33 +82,34 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
String name = in.nextName();
|
String name = in.nextName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
case "writtenByVersion" -> in.skipValue(); //noop
|
||||||
case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
|
case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||||
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
|
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
|
||||||
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
|
|
||||||
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
|
|
||||||
case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
|
case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
|
||||||
case "port" -> settings.port().set(in.nextInt());
|
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
|
||||||
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
|
|
||||||
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
|
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
|
||||||
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
|
|
||||||
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
|
|
||||||
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
|
|
||||||
case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
|
|
||||||
case "licenseKey" -> settings.licenseKey().set(in.nextString());
|
|
||||||
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
|
|
||||||
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
|
|
||||||
case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
|
|
||||||
case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
|
|
||||||
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
|
|
||||||
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
|
|
||||||
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
|
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
|
||||||
|
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
|
||||||
case "language" -> settings.languageProperty().set(in.nextString());
|
case "language" -> settings.languageProperty().set(in.nextString());
|
||||||
|
case "licenseKey" -> settings.licenseKey().set(in.nextString());
|
||||||
case "mountService" -> {
|
case "mountService" -> {
|
||||||
var token = in.peek();
|
var token = in.peek();
|
||||||
if (JsonToken.STRING == token) {
|
if (JsonToken.STRING == token) {
|
||||||
settings.mountService().set(in.nextString());
|
settings.mountService().set(in.nextString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
|
||||||
|
case "port" -> settings.port().set(in.nextInt());
|
||||||
|
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
|
||||||
|
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
|
||||||
|
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
|
||||||
|
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
|
||||||
|
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
|
||||||
|
case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
|
||||||
|
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
|
||||||
|
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
|
||||||
|
case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
|
||||||
|
case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
|
||||||
//1.6.x legacy
|
//1.6.x legacy
|
||||||
case "preferredVolumeImpl" -> volumeImpl = in.nextString();
|
case "preferredVolumeImpl" -> volumeImpl = in.nextString();
|
||||||
//legacy end
|
//legacy end
|
||||||
@@ -134,7 +135,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
if (volumeImpl.equals("Dokany")) {
|
if (volumeImpl.equals("Dokany")) {
|
||||||
return "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
|
return "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
|
||||||
} else if (volumeImpl.equals("FUSE")) {
|
} else if (volumeImpl.equals("FUSE")) {
|
||||||
if(SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
return "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
|
return "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
|
||||||
} else if (SystemUtils.IS_OS_MAC) {
|
} else if (SystemUtils.IS_OS_MAC) {
|
||||||
return "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
|
return "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
|
||||||
@@ -142,7 +143,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
return "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
|
return "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
return "org.cryptomator.frontend.webdav.mount.WindowsMounter";
|
return "org.cryptomator.frontend.webdav.mount.WindowsMounter";
|
||||||
} else if (SystemUtils.IS_OS_MAC) {
|
} else if (SystemUtils.IS_OS_MAC) {
|
||||||
return "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
|
return "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -316,18 +316,29 @@ public class Vault {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets from the cleartext path its ciphertext counterpart.
|
* Gets from the cleartext path its ciphertext counterpart.
|
||||||
* The cleartext path has to start from the vault root (by starting with "/").
|
|
||||||
*
|
*
|
||||||
* @return Local os path to the ciphertext resource
|
* @return Local os path to the ciphertext resource
|
||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
|
* @throws IllegalStateException if the vault is not unlocked
|
||||||
*/
|
*/
|
||||||
public Path getCiphertextPath(String cleartextPath) throws IOException {
|
public Path getCiphertextPath(Path cleartextPath) throws IOException {
|
||||||
if (!cleartextPath.startsWith("/")) {
|
if (!state.getValue().equals(VaultState.Value.UNLOCKED)) {
|
||||||
throw new IllegalArgumentException("Input path must be absolute from vault root by starting with \"/\".");
|
throw new IllegalStateException("Vault is not unlocked");
|
||||||
}
|
}
|
||||||
var fs = cryptoFileSystem.get();
|
var fs = cryptoFileSystem.get();
|
||||||
var cryptoPath = fs.getPath(cleartextPath);
|
var osPathSeparator = cleartextPath.getFileSystem().getSeparator();
|
||||||
return fs.getCiphertextPath(cryptoPath);
|
var cryptoFsPathSeparator = fs.getSeparator();
|
||||||
|
|
||||||
|
if (getMountPoint() instanceof Mountpoint.WithPath mp) {
|
||||||
|
var absoluteCryptoFsPath = cryptoFsPathSeparator + mp.path().relativize(cleartextPath).toString();
|
||||||
|
if (!cryptoFsPathSeparator.equals(osPathSeparator)) {
|
||||||
|
absoluteCryptoFsPath = absoluteCryptoFsPath.replace(osPathSeparator, cryptoFsPathSeparator);
|
||||||
|
}
|
||||||
|
var cryptoPath = fs.getPath(absoluteCryptoFsPath);
|
||||||
|
return fs.getCiphertextPath(cryptoPath);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("URI mount points not supported.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VaultConfigCache getVaultConfigCache() {
|
public VaultConfigCache getVaultConfigCache() {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+2
-2
@@ -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;
|
||||||
|
|||||||
+7
-1
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -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,13 +9,16 @@ 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"), //
|
||||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||||
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
|
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
|
||||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||||
HUB_LICENSE_EXCEEDED("/fxml/hub_license_exceeded.fxml"), //
|
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||||
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
|
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.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"), //
|
||||||
@@ -42,6 +43,7 @@ public enum FontAwesome5Icon {
|
|||||||
PLUS("\uF067"), //
|
PLUS("\uF067"), //
|
||||||
PRINT("\uF02F"), //
|
PRINT("\uF02F"), //
|
||||||
QUESTION("\uF128"), //
|
QUESTION("\uF128"), //
|
||||||
|
QUESTION_CIRCLE("\uf059"), //
|
||||||
REDO("\uF01E"), //
|
REDO("\uF01E"), //
|
||||||
SEARCH("\uF002"), //
|
SEARCH("\uF002"), //
|
||||||
SPINNER("\uF110"), //
|
SPINNER("\uF110"), //
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -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;
|
||||||
+7
-2
@@ -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;
|
||||||
@@ -29,9 +30,13 @@ public class ErrorController implements FxController {
|
|||||||
OS: %s / %s
|
OS: %s / %s
|
||||||
App: %s / %s
|
App: %s / %s
|
||||||
|
|
||||||
<!-- ✏️ Please describe what happened as accurately as possible. -->
|
<!-- ✏ Please describe what happened as accurately as possible. -->
|
||||||
|
|
||||||
<!-- 📋 Please also copy and paste the detail text from the error window. -->
|
<!-- 📋 Please also copy and paste the detail text from the error window. -->
|
||||||
|
|
||||||
|
<!-- ℹ Text enclosed like this (chevrons, exclamation mark, two dashes) is not visible to others! -->
|
||||||
|
|
||||||
|
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
|
||||||
""";
|
""";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
+7
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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;
|
||||||
@@ -102,10 +100,10 @@ public abstract class HubKeyLoadingModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxmlScene(FxmlFile.HUB_LICENSE_EXCEEDED)
|
@FxmlScene(FxmlFile.HUB_INVALID_LICENSE)
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static Scene provideLicenseExceededScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
static Scene provideInvalidLicenseScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_LICENSE_EXCEEDED);
|
return fxmlLoaders.createScene(FxmlFile.HUB_INVALID_LICENSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -153,17 +151,10 @@ 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(LicenseExceededController.class)
|
@FxControllerKey(InvalidLicenseController.class)
|
||||||
abstract FxController bindLicenseExceededController(LicenseExceededController controller);
|
abstract FxController bindInvalidLicenseController(InvalidLicenseController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+2
-2
@@ -7,12 +7,12 @@ import javax.inject.Inject;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
public class LicenseExceededController implements FxController {
|
public class InvalidLicenseController implements FxController {
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public LicenseExceededController(@KeyLoading Stage window) {
|
public InvalidLicenseController(@KeyLoading Stage window) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ public class ReceiveKeyController implements FxController {
|
|||||||
private final Lazy<Scene> registerDeviceScene;
|
private final Lazy<Scene> registerDeviceScene;
|
||||||
private final Lazy<Scene> unauthorizedScene;
|
private final Lazy<Scene> unauthorizedScene;
|
||||||
private final URI vaultBaseUri;
|
private final URI vaultBaseUri;
|
||||||
private final Lazy<Scene> licenseExceededScene;
|
private final Lazy<Scene> invalidLicenseScene;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_LICENSE_EXCEEDED) Lazy<Scene> licenseExceededScene) {
|
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||||
@@ -54,7 +54,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
this.registerDeviceScene = registerDeviceScene;
|
this.registerDeviceScene = registerDeviceScene;
|
||||||
this.unauthorizedScene = unauthorizedScene;
|
this.unauthorizedScene = unauthorizedScene;
|
||||||
this.vaultBaseUri = getVaultBaseUri(vault);
|
this.vaultBaseUri = getVaultBaseUri(vault);
|
||||||
this.licenseExceededScene = licenseExceededScene;
|
this.invalidLicenseScene = invalidLicenseScene;
|
||||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void licenseExceeded() {
|
private void licenseExceeded() {
|
||||||
window.setScene(licenseExceededScene.get());
|
window.setScene(invalidLicenseScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void needsDeviceRegistration() {
|
private void needsDeviceRegistration() {
|
||||||
|
|||||||
+1
-1
@@ -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;
|
||||||
|
|||||||
+1
-1
@@ -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;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.tobiasdiez.easybind.EasyBind;
|
import com.tobiasdiez.easybind.EasyBind;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.integrations.mount.Mountpoint;
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||||
@@ -54,6 +55,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
private final VaultService vaultService;
|
private final VaultService vaultService;
|
||||||
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
||||||
private final Stage mainWindow;
|
private final Stage mainWindow;
|
||||||
|
private final Optional<RevealPathService> revealPathService;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||||
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
||||||
@@ -67,12 +69,13 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
public Button dropZone;
|
public Button dropZone;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, ResourceBundle resourceBundle) {
|
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, Optional<RevealPathService> revealPathService, ResourceBundle resourceBundle) {
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultService = vaultService;
|
this.vaultService = vaultService;
|
||||||
this.wrongFileAlert = wrongFileAlert;
|
this.wrongFileAlert = wrongFileAlert;
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
|
this.revealPathService = revealPathService;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
|
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
|
||||||
this.vaultStatsBuilder = vaultStatsBuilder;
|
this.vaultStatsBuilder = vaultStatsBuilder;
|
||||||
@@ -99,7 +102,11 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
|
|
||||||
private void handleDragEvent(DragEvent event) {
|
private void handleDragEvent(DragEvent event) {
|
||||||
if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
||||||
event.acceptTransferModes(TransferMode.LINK);
|
if(SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) {
|
||||||
|
event.acceptTransferModes(TransferMode.LINK);
|
||||||
|
} else {
|
||||||
|
event.acceptTransferModes(TransferMode.ANY);
|
||||||
|
}
|
||||||
draggingOver.set(true);
|
draggingOver.set(true);
|
||||||
} else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
} else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
||||||
List<Path> ciphertextPaths = event.getDragboard().getFiles().stream().map(File::toPath).map(this::getCiphertextPath).flatMap(Optional::stream).toList();
|
List<Path> ciphertextPaths = event.getDragboard().getFiles().stream().map(File::toPath).map(this::getCiphertextPath).flatMap(Optional::stream).toList();
|
||||||
@@ -164,12 +171,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var accessPoint = mountPoint.getValue();
|
return Optional.of(vault.get().getCiphertextPath(path));
|
||||||
var cleartextPath = path.toString().substring(accessPoint.length());
|
|
||||||
if (!cleartextPath.startsWith("/")) {
|
|
||||||
cleartextPath = "/" + cleartextPath;
|
|
||||||
}
|
|
||||||
return Optional.of(vault.get().getCiphertextPath(cleartextPath));
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warn("Unable to get ciphertext path from path: {}", path, e);
|
LOG.warn("Unable to get ciphertext path from path: {}", path, e);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -177,29 +179,21 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void revealOrCopyPaths(List<Path> paths) {
|
private void revealOrCopyPaths(List<Path> paths) {
|
||||||
if (!revealPaths(paths)) {
|
revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> {
|
||||||
LOG.warn("No service provider to reveal files found.");
|
LOG.warn("No service provider to reveal files found.");
|
||||||
copyPathsToClipboard(paths);
|
copyPathsToClipboard(paths);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void revealPaths(RevealPathService service, List<Path> paths) {
|
||||||
* Reveals the paths over the {@link RevealPathService} in the file system
|
paths.forEach(path -> {
|
||||||
*
|
try {
|
||||||
* @param paths List of Paths to reveal
|
LOG.debug("Revealing {}", path);
|
||||||
* @return true, if at least one service provider was present, false otherwise
|
service.reveal(path);
|
||||||
*/
|
} catch (RevealFailedException e) {
|
||||||
private boolean revealPaths(List<Path> paths) {
|
LOG.error("Revealing ciphertext file failed.", e);
|
||||||
return RevealPathService.get().findAny().map(s -> {
|
}
|
||||||
paths.forEach(path -> {
|
});
|
||||||
try {
|
|
||||||
s.reveal(path);
|
|
||||||
} catch (RevealFailedException e) {
|
|
||||||
LOG.error("Revealing ciphertext file failed.", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}).orElse(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyPathsToClipboard(List<Path> paths) {
|
private void copyPathsToClipboard(List<Path> paths) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import javafx.fxml.FXML;
|
|||||||
public class WelcomeController implements FxController {
|
public class WelcomeController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
|
||||||
private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/en/1.6/desktop/getting-started/";
|
private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/en/1.7/desktop/getting-started/";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final BooleanBinding noVaultPresent;
|
private final BooleanBinding noVaultPresent;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import javafx.stage.Stage;
|
|||||||
|
|
||||||
public class MigrationImpossibleController implements FxController {
|
public class MigrationImpossibleController implements FxController {
|
||||||
|
|
||||||
private static final String HELP_URI = "https://docs.cryptomator.org/en/1.6/help/manual-migration/";
|
private static final String HELP_URI = "https://docs.cryptomator.org/en/1.7/help/manual-migration/";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -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 + ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package org.cryptomator.ui.preferences;
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
|
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.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.BooleanExpression;
|
import javafx.beans.binding.BooleanExpression;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
@@ -17,10 +21,13 @@ 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/volume-type/";
|
||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final ObservableValue<MountService> selectedMountService;
|
private final ObservableValue<MountService> selectedMountService;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
@@ -29,14 +36,17 @@ 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 List<MountService> mountProviders;
|
private final List<MountService> mountProviders;
|
||||||
public ChoiceBox<MountService> volumeTypeChoiceBox;
|
public ChoiceBox<MountService> volumeTypeChoiceBox;
|
||||||
public TextField loopbackPortField;
|
public TextField loopbackPortField;
|
||||||
public Button loopbackPortApplyButton;
|
public Button loopbackPortApplyButton;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VolumePreferencesController(Settings settings, 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.mountProviders = mountProviders;
|
this.mountProviders = mountProviders;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
|
||||||
@@ -47,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() {
|
||||||
@@ -123,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> {
|
||||||
@@ -141,4 +165,8 @@ public class VolumePreferencesController implements FxController {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void openDocs() {
|
||||||
|
application.get().getHostServices().showDocument(DOCS_MOUNTING_URL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,13 +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.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;
|
||||||
@@ -15,86 +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.binding.Bindings;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
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 RecoveryKeyFactory recoveryKeyFactory;
|
|
||||||
private final BooleanBinding validRecoveryKey;
|
|
||||||
private final Lazy<Scene> resetPasswordScene;
|
private final Lazy<Scene> resetPasswordScene;
|
||||||
private final AutoCompleter autoCompleter;
|
|
||||||
|
|
||||||
public TextArea textarea;
|
@FXML
|
||||||
|
RecoveryKeyValidateController recoveryKeyValidateController;
|
||||||
|
|
||||||
@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.validRecoveryKey = Bindings.createBooleanBinding(this::isValidRecoveryKey, recoveryKey);
|
|
||||||
this.resetPasswordScene = resetPasswordScene;
|
this.resetPasswordScene = resetPasswordScene;
|
||||||
this.autoCompleter = new AutoCompleter(recoveryKeyFactory.getDictionary());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
recoveryKey.bind(textarea.textProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
@@ -107,39 +51,10 @@ public class RecoveryKeyRecoverController implements FxController {
|
|||||||
window.setScene(resetPasswordScene.get());
|
window.setScene(resetPasswordScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
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.");
|
|
||||||
return false;
|
|
||||||
} catch (VaultConfigLoadException e) {
|
|
||||||
LOG.error("Failed to parse vault config", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public Vault getVault() {
|
public RecoveryKeyValidateController getValidateController() {
|
||||||
return vault;
|
return recoveryKeyValidateController;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding validRecoveryKeyProperty() {
|
|
||||||
return validRecoveryKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValidRecoveryKey() {
|
|
||||||
if (unverifiedVaultConfig != null) {
|
|
||||||
return recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), this::checkKeyAgainstVaultConfig);
|
|
||||||
} else {
|
|
||||||
return recoveryKeyFactory.validateRecoveryKey(recoveryKey.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextFormatter getRecoveryKeyTextFormatter() {
|
|
||||||
return new TextFormatter<>(this::filterTextChange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import java.io.UncheckedIOException;
|
|||||||
@WrongFileAlertScoped
|
@WrongFileAlertScoped
|
||||||
public class WrongFileAlertController implements FxController {
|
public class WrongFileAlertController implements FxController {
|
||||||
|
|
||||||
private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/en/1.6/desktop/accessing-vaults/";
|
private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/en/1.7/desktop/accessing-vaults/";
|
||||||
|
|
||||||
private final Application app;
|
private final Application app;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@
|
|||||||
<?import javafx.scene.shape.Circle?>
|
<?import javafx.scene.shape.Circle?>
|
||||||
<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.keyloading.hub.LicenseExceededController"
|
fx:controller="org.cryptomator.ui.keyloading.hub.InvalidLicenseController"
|
||||||
minWidth="400"
|
minWidth="400"
|
||||||
maxWidth="400"
|
maxWidth="400"
|
||||||
minHeight="145"
|
minHeight="145"
|
||||||
@@ -33,12 +33,12 @@
|
|||||||
</StackPane>
|
</StackPane>
|
||||||
</Group>
|
</Group>
|
||||||
<VBox HBox.hgrow="ALWAYS">
|
<VBox HBox.hgrow="ALWAYS">
|
||||||
<Label styleClass="label-large" text="%hub.licenseExceeded.message" wrapText="true" textAlignment="LEFT">
|
<Label styleClass="label-large" text="%hub.invalidLicense.message" wrapText="true" textAlignment="LEFT">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="6" top="6"/>
|
<Insets bottom="6" top="6"/>
|
||||||
</padding>
|
</padding>
|
||||||
</Label>
|
</Label>
|
||||||
<Label text="%hub.licenseExceeded.description" wrapText="true"/>
|
<Label text="%hub.invalidLicense.description" wrapText="true"/>
|
||||||
|
|
||||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user