mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
201
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c0060262a | ||
|
|
9af4ffe83b | ||
|
|
c6f963793d | ||
|
|
8c34fc76c5 | ||
|
|
785cf7a9a6 | ||
|
|
c63837c4ce | ||
|
|
b1a3ef9023 | ||
|
|
32436f779f | ||
|
|
ccc6f605ba | ||
|
|
f338d2447b | ||
|
|
179240b325 | ||
|
|
32a65bddce | ||
|
|
710cdf800d | ||
|
|
1d6edb8373 | ||
|
|
a3d30612ec | ||
|
|
6acda9b13c | ||
|
|
28cb812dab | ||
|
|
68ea4af0ad | ||
|
|
0af0a9e440 | ||
|
|
0989c735c0 | ||
|
|
a3492b9ea3 | ||
|
|
e345e6415f | ||
|
|
5b6d09308b | ||
|
|
49bda58993 | ||
|
|
32d7189a12 | ||
|
|
1253b7db2b | ||
|
|
067a7ad3ee | ||
|
|
a9ec76a344 | ||
|
|
085f762a35 | ||
|
|
7dd1c3576f | ||
|
|
d23bd2865a | ||
|
|
4429d57b5e | ||
|
|
2ff71ed7b0 | ||
|
|
82de8b6994 | ||
|
|
d4cba2fd6e | ||
|
|
ff80f634d2 | ||
|
|
6386dd3d50 | ||
|
|
a3f05db189 | ||
|
|
151ef6c7b2 | ||
|
|
72fd38baf1 | ||
|
|
532ffb1202 | ||
|
|
2a704d5eb4 | ||
|
|
e8f8466d9a | ||
|
|
9297562c99 | ||
|
|
7d62fc78de | ||
|
|
ba627d0d60 | ||
|
|
8e7e7de358 | ||
|
|
10c60d7492 | ||
|
|
aa03bd119a | ||
|
|
325ffda9af | ||
|
|
d1270ceeb2 | ||
|
|
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 |
@@ -38,7 +38,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Volume Type
|
label: Volume Type
|
||||||
description: What volume type is selected under Settings → Virtual Drive?
|
description: What volume type is selected under Settings → Virtual Drive?
|
||||||
value: |
|
options:
|
||||||
- WinFsp
|
- WinFsp
|
||||||
- WinFsp (Local Drive)
|
- WinFsp (Local Drive)
|
||||||
- FUSE-T
|
- FUSE-T
|
||||||
@@ -49,7 +49,7 @@ body:
|
|||||||
- WebDAV (HTTP Address)
|
- WebDAV (HTTP Address)
|
||||||
- Dokany (1.5)
|
- Dokany (1.5)
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: reproduction-steps
|
id: reproduction-steps
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -10,7 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -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: -->
|
||||||
@@ -19,9 +19,9 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19/openjfx-19_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.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/20.0.1/openjfx-20.0.1_linux-aarch64_bin-jmods.zip'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0
|
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ on:
|
|||||||
value: ${{ jobs.determine-version.outputs.type }}
|
value: ${{ jobs.determine-version.outputs.type }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_CACHE: 'maven'
|
JAVA_CACHE: 'maven'
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
- 'hotfix/**'
|
- 'hotfix/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -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: 20
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_CACHE: 'maven'
|
JAVA_CACHE: 'maven'
|
||||||
|
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip'
|
||||||
|
JFX_JMODS_HASH: 'D00767334C43B8832B5CF10267D34CA8F563D187C4655B73EB6020DD79C054B5'
|
||||||
|
|
||||||
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]) {
|
throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}.";
|
||||||
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
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress=1
|
--compress=1
|
||||||
|
- name: Change win-console flag if debug is active
|
||||||
|
if: ${{ inputs.isDebug }}
|
||||||
|
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -99,8 +123,10 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
|
||||||
|
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--icon dist/win/resources/Cryptomator.ico
|
--icon dist/win/resources/Cryptomator.ico
|
||||||
|
${WIN_CONSOLE_FLAG}
|
||||||
- name: Patch Application Directory
|
- name: Patch Application Directory
|
||||||
run: |
|
run: |
|
||||||
cp dist/win/contrib/* appdir/Cryptomator
|
cp dist/win/contrib/* appdir/Cryptomator
|
||||||
@@ -203,15 +229,6 @@ jobs:
|
|||||||
*.msi
|
*.msi
|
||||||
*.asc
|
*.asc
|
||||||
|
|
||||||
call-winget-flow:
|
|
||||||
needs: [get-version, build-msi]
|
|
||||||
if: github.event.action == 'published' && needs.get-version.outputs.versionType == 'stable'
|
|
||||||
uses: ./.github/workflows/winget.yml
|
|
||||||
with:
|
|
||||||
releaseTag: ${{ github.event.release.tag_name }}
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
|
|
||||||
build-exe:
|
build-exe:
|
||||||
name: Build .exe installer
|
name: Build .exe installer
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@@ -243,13 +260,14 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Download WinFsp
|
- name: Download WinFsp
|
||||||
run: |
|
run: |
|
||||||
$winfspUrl= (Select-String -Path ".\dist\win\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value
|
$winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||||
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
|
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Compile to wixObj file
|
- name: Compile to wixObj file
|
||||||
run: >
|
run: >
|
||||||
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
|
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
|
||||||
-ext WixBalExtension
|
-ext WixBalExtension
|
||||||
|
-ext WixUtilExtension
|
||||||
-out dist/win/bundle/
|
-out dist/win/bundle/
|
||||||
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
-dBundleVendor="Skymatic GmbH"
|
-dBundleVendor="Skymatic GmbH"
|
||||||
@@ -261,6 +279,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
|
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
|
||||||
-ext WixBalExtension
|
-ext WixBalExtension
|
||||||
|
-ext WixUtilExtension
|
||||||
-out installer/unsigned/Cryptomator-Installer.exe
|
-out installer/unsigned/Cryptomator-Installer.exe
|
||||||
- name: Detach burn engine in preparation to sign
|
- name: Detach burn engine in preparation to sign
|
||||||
run: >
|
run: >
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
Generated
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
+2
-2
@@ -2,7 +2,7 @@ Source: cryptomator
|
|||||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-19, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
@@ -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
@@ -4,7 +4,7 @@
|
|||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
|
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs
|
||||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||||
|
|||||||
Vendored
+5
-4
@@ -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
|
||||||
@@ -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
+3
-3
@@ -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
+10
-2
@@ -70,6 +70,9 @@
|
|||||||
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
|
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
|
||||||
|
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||||
|
|
||||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||||
<!-- Block installation if innosetup entry of Cryptomator is found -->
|
<!-- Block installation if innosetup entry of Cryptomator is found -->
|
||||||
<Property Id="OLDEXEINSTALLER">
|
<Property Id="OLDEXEINSTALLER">
|
||||||
@@ -172,11 +175,12 @@
|
|||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<?ifndef JpAllowUpgrades ?>
|
<?ifndef JpAllowUpgrades ?>
|
||||||
<Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
|
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
|
||||||
<?endif?>
|
<?endif?>
|
||||||
<?ifndef JpAllowDowngrades ?>
|
<?ifndef JpAllowDowngrades ?>
|
||||||
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
|
|
||||||
<!-- Check and fail if Cryptomator is running -->
|
<!-- Check and fail if Cryptomator is running -->
|
||||||
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
||||||
@@ -188,6 +192,10 @@
|
|||||||
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<InstallUISequence>
|
||||||
|
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
|
</InstallUISequence>
|
||||||
|
|
||||||
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
||||||
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
||||||
</Product>
|
</Product>
|
||||||
|
|||||||
@@ -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</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -17,60 +17,58 @@
|
|||||||
<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>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.jdk.version>19</project.jdk.version>
|
<project.jdk.version>20</project.jdk.version>
|
||||||
|
|
||||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||||
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
|
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
|
||||||
<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.4</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-beta3</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.1</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.integrations.linux.version>1.2.0-beta2</cryptomator.integrations.linux.version>
|
<cryptomator.fuse.version>3.0.0</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.3</cryptomator.webdav.version>
|
||||||
<cryptomator.webdav.version>2.0.0-beta5</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>20.0.1</javafx.version>
|
||||||
<jwt.version>4.2.1</jwt.version>
|
<jwt.version>4.4.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.7</logback.version>
|
||||||
<slf4j.version>2.0.3</slf4j.version>
|
<slf4j.version>2.0.7</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.3</junit.jupiter.version>
|
||||||
<mockito.version>4.8.0</mockito.version>
|
<mockito.version>5.3.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.9</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>
|
||||||
@@ -369,6 +332,9 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--enable-preview</argLine>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import ch.qos.logback.classic.spi.Configurator;
|
import ch.qos.logback.classic.spi.Configurator;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.GoogleDriveLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||||
@@ -37,6 +49,15 @@ open module org.cryptomator.desktop {
|
|||||||
/* TODO: filename-based modules: */
|
/* TODO: filename-based modules: */
|
||||||
requires static javax.inject; /* ugly dagger/guava crap */
|
requires static javax.inject; /* ugly dagger/guava crap */
|
||||||
|
|
||||||
|
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
|
||||||
provides TrayMenuController with AwtTrayMenuController;
|
provides TrayMenuController with AwtTrayMenuController;
|
||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
|
provides LocationPresetsProvider with DropboxMacLocationPresetsProvider, //
|
||||||
|
DropboxWindowsLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
||||||
|
ICloudMacLocationPresetsProvider, ICloudWindowsLocationPresetsProvider, //
|
||||||
|
GoogleDriveLocationPresetsProvider, //
|
||||||
|
PCloudLocationPresetsProvider, MegaLocationPresetsProvider, //
|
||||||
|
OneDriveLinuxLocationPresetsProvider, OneDriveWindowsLocationPresetsProvider, //
|
||||||
|
OneDriveMacLocationPresetsProvider;
|
||||||
}
|
}
|
||||||
@@ -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];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.cryptomator.common;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum of common cloud providers and their default local storage location path.
|
|
||||||
*/
|
|
||||||
public enum LocationPreset {
|
|
||||||
|
|
||||||
DROPBOX("Dropbox", "~/Dropbox"),
|
|
||||||
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
|
|
||||||
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
|
|
||||||
MEGA("MEGA", "~/MEGA"),
|
|
||||||
ONEDRIVE("OneDrive", "~/OneDrive"),
|
|
||||||
PCLOUD("pCloud", "~/pCloudDrive"),
|
|
||||||
|
|
||||||
LOCAL("local");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final List<Path> candidates;
|
|
||||||
|
|
||||||
LocationPreset(String name, String... candidates) {
|
|
||||||
this.name = name;
|
|
||||||
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks for this LocationPreset if any of the associated paths exist.
|
|
||||||
*
|
|
||||||
* @return the first existing path or null, if none exists.
|
|
||||||
*/
|
|
||||||
public Path existingPath() {
|
|
||||||
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getDisplayName();
|
|
||||||
}
|
|
||||||
|
|
||||||
//this contruct is needed, since static members are initialized after every enum member is initialized
|
|
||||||
//TODO: refactor this to normal class and use this also in different parts of the project
|
|
||||||
private static class UserHome {
|
|
||||||
|
|
||||||
private static final String USER_HOME = System.getProperty("user.home");
|
|
||||||
|
|
||||||
private static String resolve(String path) {
|
|
||||||
if (path.startsWith("~/")) {
|
|
||||||
return UserHome.USER_HOME + path.substring(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
|
||||||
|
|
||||||
|
@OperatingSystem(LINUX)
|
||||||
|
public final class DropboxLinuxLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path USER_HOME = LocationPresetsProvider.resolveLocation("~/.").toAbsolutePath();
|
||||||
|
private static final Predicate<String> PATTERN = Pattern.compile("Dropbox \\(.+\\)").asMatchPredicate();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
try (var dirStream = Files.list(USER_HOME)) {
|
||||||
|
var presets = dirStream.filter(p -> Files.isDirectory(p) && PATTERN.test(p.getFileName().toString())) //
|
||||||
|
.map(p -> new LocationPreset(p.getFileName().toString(), p)) //
|
||||||
|
.toList();
|
||||||
|
return presets.stream(); //workaround to ensure that the directory stream is always closed
|
||||||
|
} catch (IOException | UncheckedIOException e) { //UncheckedIOException thrown by the stream of Files.list()
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox");
|
||||||
|
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
|
||||||
|
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION) || Files.isDirectory(FALLBACK_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
if(Files.isDirectory(LOCATION)) {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||||
|
} else if(Files.isDirectory(FALLBACK_LOCATION)) {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", FALLBACK_LOCATION));
|
||||||
|
} else {
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
|
||||||
|
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class GoogleDriveLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION1 = LocationPresetsProvider.resolveLocation("~/GoogleDrive");
|
||||||
|
private static final Path LOCATION2 = LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive");
|
||||||
|
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION1) || Files.isDirectory(LOCATION2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
if(Files.isDirectory(LOCATION1)) {
|
||||||
|
return Stream.of(new LocationPreset("Google Drive", LOCATION1));
|
||||||
|
} else if(Files.isDirectory(LOCATION2)) {
|
||||||
|
return Stream.of(new LocationPreset("Google Drive", LOCATION2));
|
||||||
|
} else {
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public record LocationPreset(String name, Path path) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.IntegrationsLoader;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public interface LocationPresetsProvider {
|
||||||
|
|
||||||
|
Logger LOG = LoggerFactory.getLogger(LocationPresetsProvider.class);
|
||||||
|
String USER_HOME = System.getProperty("user.home");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streams account-separated location presets found by this provider
|
||||||
|
* @return Stream of LocationPresets
|
||||||
|
*/
|
||||||
|
Stream<LocationPreset> getLocations();
|
||||||
|
|
||||||
|
static Path resolveLocation(String p) {
|
||||||
|
if (p.startsWith("~/")) {
|
||||||
|
return Path.of(USER_HOME, p.substring(2));
|
||||||
|
} else {
|
||||||
|
return Path.of(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//copied from org.cryptomator.integrations.common.IntegrationsLoader
|
||||||
|
//TODO: delete, once migrated to integrations-api
|
||||||
|
static <T> Stream<T> loadAll(Class<T> clazz) {
|
||||||
|
return ServiceLoader.load(clazz)
|
||||||
|
.stream()
|
||||||
|
.filter(LocationPresetsProvider::isSupportedOperatingSystem)
|
||||||
|
.filter(LocationPresetsProvider::passesStaticAvailabilityCheck)
|
||||||
|
.map(ServiceLoader.Provider::get)
|
||||||
|
.peek(impl -> logServiceIsAvailable(clazz, impl.getClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isSupportedOperatingSystem(ServiceLoader.Provider<?> provider) {
|
||||||
|
var annotations = provider.type().getAnnotationsByType(OperatingSystem.class);
|
||||||
|
return annotations.length == 0 || Arrays.stream(annotations).anyMatch(OperatingSystem.Value::isCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean passesStaticAvailabilityCheck(ServiceLoader.Provider<?> provider) {
|
||||||
|
return passesStaticAvailabilityCheck(provider.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean passesStaticAvailabilityCheck(Class<?> type) {
|
||||||
|
return passesAvailabilityCheck(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logServiceIsAvailable(Class<?> apiType, Class<?> implType) {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("{}: Implementation is available: {}", apiType.getSimpleName(), implType.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean passesAvailabilityCheck(Class<? extends T> type, @Nullable T instance) {
|
||||||
|
if (!type.isAnnotationPresent(CheckAvailability.class)) {
|
||||||
|
return true; // if type is not annotated, skip tests
|
||||||
|
}
|
||||||
|
if (!type.getModule().isExported(type.getPackageName(), IntegrationsLoader.class.getModule())) {
|
||||||
|
LOG.error("Can't run @CheckAvailability tests for class {}. Make sure to export {} to {}!", type.getName(), type.getPackageName(), IntegrationsLoader.class.getPackageName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Arrays.stream(type.getMethods())
|
||||||
|
.filter(m -> isAvailabilityCheck(m, instance == null))
|
||||||
|
.allMatch(m -> passesAvailabilityCheck(m, instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean passesAvailabilityCheck(Method m, @Nullable Object instance) {
|
||||||
|
assert Boolean.TYPE.equals(m.getReturnType());
|
||||||
|
try {
|
||||||
|
return (boolean) m.invoke(instance);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
LOG.warn("Failed to invoke @CheckAvailability test {}#{}", m.getDeclaringClass(), m.getName(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAvailabilityCheck(Method m, boolean isStatic) {
|
||||||
|
return m.isAnnotationPresent(CheckAvailability.class)
|
||||||
|
&& Boolean.TYPE.equals(m.getReturnType())
|
||||||
|
&& m.getParameterCount() == 0
|
||||||
|
&& Modifier.isStatic(m.getModifiers()) == isStatic;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class MegaLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("MEGA", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
|
||||||
|
|
||||||
|
@OperatingSystem(LINUX)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class OneDriveLinuxLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("OneDrive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
|
||||||
|
private static final Path PARENT_LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
var newLocations = getNewLocations().toList();
|
||||||
|
if (newLocations.size() >= 1) {
|
||||||
|
return newLocations.stream();
|
||||||
|
} else {
|
||||||
|
return getOldLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<LocationPreset> getNewLocations() {
|
||||||
|
try (var dirStream = Files.newDirectoryStream(PARENT_LOCATION, "OneDrive*")) {
|
||||||
|
return StreamSupport.stream(dirStream.spliterator(), false) //
|
||||||
|
.filter(Files::isDirectory) //
|
||||||
|
.map(p -> new LocationPreset(String.join(" - ", p.getFileName().toString().split("-")), p));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<LocationPreset> getOldLocation() {
|
||||||
|
return Stream.of(new LocationPreset("OneDrive", FALLBACK_LOCATION)).filter(preset -> Files.isDirectory(preset.path()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.jetbrains.annotations.Blocking;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(OneDriveWindowsLocationPresetsProvider.class);
|
||||||
|
private static final String REGSTR_TOKEN = "REG_SZ";
|
||||||
|
private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
try {
|
||||||
|
var accountRegKeys = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList();
|
||||||
|
var cloudLocations = new ArrayList<LocationPreset>();
|
||||||
|
for (var accountRegKey : accountRegKeys) {
|
||||||
|
var path = queryRegistry(accountRegKey, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||||
|
.map(Path::of) //
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
var name = "OneDrive"; //we assume personal oneDrive account by default
|
||||||
|
if (!accountRegKey.endsWith("Personal")) {
|
||||||
|
name = queryRegistry(accountRegKey, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||||
|
.map("OneDrive - "::concat) //
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
cloudLocations.add(new LocationPreset(name, path));
|
||||||
|
}
|
||||||
|
return cloudLocations.stream();
|
||||||
|
} catch (IOException | CommandFailedException | TimeoutException e) {
|
||||||
|
LOG.error("Unable to determine OneDrive location", e);
|
||||||
|
return Stream.of();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
LOG.error("Determination of OneDrive location interrupted", e);
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<String> queryRegistry(String keyname, List<String> moreArgs, Predicate<String> outputFilter) throws InterruptedException, CommandFailedException, TimeoutException, IOException {
|
||||||
|
var args = new ArrayList<String>();
|
||||||
|
args.add("reg");
|
||||||
|
args.add("query");
|
||||||
|
args.add(keyname);
|
||||||
|
args.addAll(moreArgs);
|
||||||
|
ProcessBuilder command = new ProcessBuilder(args);
|
||||||
|
Process p = command.start();
|
||||||
|
waitForSuccess(p, 3, "`reg query`");
|
||||||
|
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}.
|
||||||
|
*
|
||||||
|
* @param process The process to wait for
|
||||||
|
* @param timeoutSeconds How long to wait (in seconds)
|
||||||
|
* @param cmdDescription A short description of the process used to generate log and exception messages
|
||||||
|
* @throws TimeoutException Thrown when the process doesn't finish in time
|
||||||
|
* @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish
|
||||||
|
* @throws CommandFailedException Thrown when the process exit code is non-zero
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
private static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException {
|
||||||
|
boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
if (!exited) {
|
||||||
|
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
|
||||||
|
}
|
||||||
|
if (process.exitValue() != 0) {
|
||||||
|
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
|
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
|
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CommandFailedException extends Exception {
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
String stdout;
|
||||||
|
String stderr;
|
||||||
|
|
||||||
|
private CommandFailedException(String cmdDescription, int exitCode, String stdout, String stderr) {
|
||||||
|
super(cmdDescription + " returned with non-zero exit code " + exitCode);
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
this.stdout = stdout;
|
||||||
|
this.stderr = stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class PCloudLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/pCloudDrive");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("pCloud", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import java.nio.channels.ReadableByteChannel;
|
|||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
// TODO make sealed, remove enum
|
//TODO can the enum be removed?
|
||||||
interface IpcMessage {
|
sealed interface IpcMessage permits HandleLaunchArgsMessage, RevealRunningAppMessage {
|
||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
|
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import java.util.List;
|
|||||||
public interface IpcMessageListener {
|
public interface IpcMessageListener {
|
||||||
|
|
||||||
default void handleMessage(IpcMessage message) {
|
default void handleMessage(IpcMessage message) {
|
||||||
if (message instanceof RevealRunningAppMessage) {
|
switch (message) {
|
||||||
revealRunningApp();
|
case RevealRunningAppMessage m -> revealRunningApp(); // TODO: rename to _ with JEP 443
|
||||||
} else if (message instanceof HandleLaunchArgsMessage m) {
|
case HandleLaunchArgsMessage m -> handleLaunchArgs(m.args());
|
||||||
handleLaunchArgs(m.args());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
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.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;
|
||||||
|
|
||||||
@@ -14,26 +16,39 @@ import java.util.Locale;
|
|||||||
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 "_":
|
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_".
|
||||||
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", //
|
// "en" is not part of this list - it is always inserted at the top.
|
||||||
"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", //
|
public static final List<String> LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", //
|
||||||
"ta", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", //
|
||||||
|
"ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
||||||
|
public static final String ENGLISH = "en";
|
||||||
|
|
||||||
@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);
|
||||||
|
var sorted = new ArrayList<String>();
|
||||||
|
sorted.add(0, Settings.DEFAULT_LANGUAGE);
|
||||||
|
sorted.add(1, ENGLISH);
|
||||||
|
LANGUAGE_TAGS.stream() //
|
||||||
|
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
|
||||||
|
.forEach(sorted::add);
|
||||||
|
sortedLanguageTags = Collections.unmodifiableList(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+63
-84
@@ -1,6 +1,9 @@
|
|||||||
package org.cryptomator.ui.addvaultwizard;
|
package org.cryptomator.ui.addvaultwizard;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.common.ObservableUtil;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPreset;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
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;
|
||||||
@@ -10,22 +13,19 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.Toggle;
|
import javafx.scene.control.Toggle;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.DirectoryChooser;
|
import javafx.stage.DirectoryChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -34,6 +34,9 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@@ -46,70 +49,72 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Lazy<Scene> chooseNameScene;
|
private final Lazy<Scene> chooseNameScene;
|
||||||
private final Lazy<Scene> choosePasswordScene;
|
private final Lazy<Scene> choosePasswordScene;
|
||||||
private final ObservedLocationPresets locationPresets;
|
private final List<RadioButton> locationPresetBtns;
|
||||||
private final ObjectProperty<Path> vaultPath;
|
private final ObjectProperty<Path> vaultPath;
|
||||||
private final StringProperty vaultName;
|
private final StringProperty vaultName;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final BooleanBinding validVaultPath;
|
private final ObservableValue<VaultPathStatus> vaultPathStatus;
|
||||||
|
private final ObservableValue<Boolean> validVaultPath;
|
||||||
private final BooleanProperty usePresetPath;
|
private final BooleanProperty usePresetPath;
|
||||||
private final StringProperty statusText;
|
|
||||||
private final ObjectProperty<Node> statusGraphic;
|
|
||||||
|
|
||||||
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
||||||
|
|
||||||
//FXML
|
//FXML
|
||||||
public ToggleGroup predefinedLocationToggler;
|
public ToggleGroup locationPresetsToggler;
|
||||||
public RadioButton iclouddriveRadioButton;
|
public VBox radioButtonVBox;
|
||||||
public RadioButton dropboxRadioButton;
|
|
||||||
public RadioButton gdriveRadioButton;
|
|
||||||
public RadioButton onedriveRadioButton;
|
|
||||||
public RadioButton megaRadioButton;
|
|
||||||
public RadioButton pcloudRadioButton;
|
|
||||||
public RadioButton customRadioButton;
|
public RadioButton customRadioButton;
|
||||||
public Label vaultPathStatus;
|
public Label locationStatusLabel;
|
||||||
public FontAwesome5IconView goodLocation;
|
public FontAwesome5IconView goodLocation;
|
||||||
public FontAwesome5IconView badLocation;
|
public FontAwesome5IconView badLocation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.chooseNameScene = chooseNameScene;
|
this.chooseNameScene = chooseNameScene;
|
||||||
this.choosePasswordScene = choosePasswordScene;
|
this.choosePasswordScene = choosePasswordScene;
|
||||||
this.locationPresets = locationPresets;
|
|
||||||
this.vaultPath = vaultPath;
|
this.vaultPath = vaultPath;
|
||||||
this.vaultName = vaultName;
|
this.vaultName = vaultName;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.validVaultPath = Bindings.createBooleanBinding(this::validateVaultPathAndSetStatus, this.vaultPath);
|
this.vaultPathStatus = ObservableUtil.mapWithDefault(vaultPath, this::validatePath, new VaultPathStatus(false, "error.message"));
|
||||||
|
this.validVaultPath = ObservableUtil.mapWithDefault(vaultPathStatus, VaultPathStatus::valid, false);
|
||||||
|
this.vaultPathStatus.addListener(this::updateStatusLabel);
|
||||||
this.usePresetPath = new SimpleBooleanProperty();
|
this.usePresetPath = new SimpleBooleanProperty();
|
||||||
this.statusText = new SimpleStringProperty();
|
this.locationPresetBtns = LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
|
||||||
this.statusGraphic = new SimpleObjectProperty<>();
|
.flatMap(LocationPresetsProvider::getLocations) //
|
||||||
|
.sorted(Comparator.comparing(LocationPreset::name)) //
|
||||||
|
.map(preset -> { //
|
||||||
|
var btn = new RadioButton(preset.name());
|
||||||
|
btn.setUserData(preset.path());
|
||||||
|
return btn;
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateVaultPathAndSetStatus() {
|
private VaultPathStatus validatePath(Path p) throws NullPointerException {
|
||||||
final Path p = vaultPath.get();
|
if (!Files.exists(p.getParent())) {
|
||||||
if (p == null) {
|
return new VaultPathStatus(false, "addvaultwizard.new.locationDoesNotExist");
|
||||||
statusText.set("Error: Path is NULL.");
|
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!Files.exists(p.getParent())) {
|
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
|
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!isActuallyWritable(p.getParent())) {
|
} else if (!isActuallyWritable(p.getParent())) {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
|
return new VaultPathStatus(false, "addvaultwizard.new.locationIsNotWritable");
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!Files.notExists(p)) {
|
} else if (!Files.notExists(p)) {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
return new VaultPathStatus(false, "addvaultwizard.new.fileAlreadyExists");
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsOk"));
|
return new VaultPathStatus(true, "addvaultwizard.new.locationIsOk");
|
||||||
statusGraphic.set(goodLocation);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateStatusLabel(ObservableValue<? extends VaultPathStatus> observable, VaultPathStatus oldValue, VaultPathStatus newValue) {
|
||||||
|
if (newValue.valid()) {
|
||||||
|
locationStatusLabel.setGraphic(goodLocation);
|
||||||
|
locationStatusLabel.getStyleClass().remove("label-red");
|
||||||
|
locationStatusLabel.getStyleClass().add("label-muted");
|
||||||
|
} else {
|
||||||
|
locationStatusLabel.setGraphic(badLocation);
|
||||||
|
locationStatusLabel.getStyleClass().remove("label-muted");
|
||||||
|
locationStatusLabel.getStyleClass().add("label-red");
|
||||||
|
}
|
||||||
|
this.locationStatusLabel.setText(resourceBundle.getString(newValue.localizationKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isActuallyWritable(Path p) {
|
private boolean isActuallyWritable(Path p) {
|
||||||
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
|
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
|
||||||
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
|
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
|
||||||
@@ -127,26 +132,15 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
radioButtonVBox.getChildren().addAll(1, locationPresetBtns); //first item is the list header
|
||||||
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
locationPresetsToggler.getToggles().addAll(locationPresetBtns);
|
||||||
|
locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
||||||
|
usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
||||||
if (iclouddriveRadioButton.equals(newValue)) {
|
var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath);
|
||||||
vaultPath.set(locationPresets.getIclouddriveLocation().resolve(vaultName.get()));
|
vaultPath.set(storagePath.resolve(vaultName.get()));
|
||||||
} else if (dropboxRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getDropboxLocation().resolve(vaultName.get()));
|
|
||||||
} else if (gdriveRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
|
|
||||||
} else if (onedriveRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get()));
|
|
||||||
} else if (megaRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get()));
|
|
||||||
} else if (pcloudRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get()));
|
|
||||||
} else if (customRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(customVaultPath.resolve(vaultName.get()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -156,10 +150,8 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void next() {
|
public void next() {
|
||||||
if (validateVaultPathAndSetStatus()) {
|
if (validVaultPath.getValue()) {
|
||||||
window.setScene(choosePasswordScene.get());
|
window.setScene(choosePasswordScene.get());
|
||||||
} else {
|
|
||||||
validVaultPath.invalidate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +171,12 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Internal classes */
|
||||||
|
|
||||||
|
private record VaultPathStatus(boolean valid, String localizationKey) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public Path getVaultPath() {
|
public Path getVaultPath() {
|
||||||
@@ -189,47 +187,28 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
return vaultPath;
|
return vaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding validVaultPathProperty() {
|
public ObservableValue<Boolean> validVaultPathProperty() {
|
||||||
return validVaultPath;
|
return validVaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getValidVaultPath() {
|
public boolean isValidVaultPath() {
|
||||||
return validVaultPath.get();
|
return validVaultPath.getValue();
|
||||||
}
|
|
||||||
|
|
||||||
public ObservedLocationPresets getObservedLocationPresets() {
|
|
||||||
return locationPresets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty usePresetPathProperty() {
|
public BooleanProperty usePresetPathProperty() {
|
||||||
return usePresetPath;
|
return usePresetPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getUsePresetPath() {
|
public boolean isUsePresetPath() {
|
||||||
return usePresetPath.get();
|
return usePresetPath.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding anyRadioButtonSelectedProperty() {
|
public BooleanBinding anyRadioButtonSelectedProperty() {
|
||||||
return predefinedLocationToggler.selectedToggleProperty().isNotNull();
|
return locationPresetsToggler.selectedToggleProperty().isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAnyRadioButtonSelected() {
|
public boolean isAnyRadioButtonSelected() {
|
||||||
return anyRadioButtonSelectedProperty().get();
|
return anyRadioButtonSelectedProperty().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringProperty statusTextProperty() {
|
|
||||||
return statusText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatusText() {
|
|
||||||
return statusText.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Node> statusGraphicProperty() {
|
|
||||||
return statusGraphic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getStatusGraphic() {
|
|
||||||
return statusGraphic.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+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;
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
package org.cryptomator.ui.addvaultwizard;
|
|
||||||
|
|
||||||
import org.cryptomator.common.LocationPreset;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
|
||||||
public class ObservedLocationPresets {
|
|
||||||
|
|
||||||
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> dropboxLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> gdriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> onedriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> megaLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> pcloudLocation;
|
|
||||||
private final BooleanBinding foundIclouddrive;
|
|
||||||
private final BooleanBinding foundDropbox;
|
|
||||||
private final BooleanBinding foundGdrive;
|
|
||||||
private final BooleanBinding foundOnedrive;
|
|
||||||
private final BooleanBinding foundMega;
|
|
||||||
private final BooleanBinding foundPcloud;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ObservedLocationPresets() {
|
|
||||||
this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath());
|
|
||||||
this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath());
|
|
||||||
this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath());
|
|
||||||
this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath());
|
|
||||||
this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath());
|
|
||||||
this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath());
|
|
||||||
this.foundIclouddrive = iclouddriveLocation.isNotNull();
|
|
||||||
this.foundDropbox = dropboxLocation.isNotNull();
|
|
||||||
this.foundGdrive = gdriveLocation.isNotNull();
|
|
||||||
this.foundOnedrive = onedriveLocation.isNotNull();
|
|
||||||
this.foundMega = megaLocation.isNotNull();
|
|
||||||
this.foundPcloud = pcloudLocation.isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Observables */
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
|
|
||||||
return iclouddriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getIclouddriveLocation() {
|
|
||||||
return iclouddriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundIclouddriveProperty() {
|
|
||||||
return foundIclouddrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundIclouddrive() {
|
|
||||||
return foundIclouddrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> dropboxLocationProperty() {
|
|
||||||
return dropboxLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getDropboxLocation() {
|
|
||||||
return dropboxLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundDropboxProperty() {
|
|
||||||
return foundDropbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundDropbox() {
|
|
||||||
return foundDropbox.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> gdriveLocationProperty() {
|
|
||||||
return gdriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getGdriveLocation() {
|
|
||||||
return gdriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundGdriveProperty() {
|
|
||||||
return foundGdrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundGdrive() {
|
|
||||||
return foundGdrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> onedriveLocationProperty() {
|
|
||||||
return onedriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getOnedriveLocation() {
|
|
||||||
return onedriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundOnedriveProperty() {
|
|
||||||
return foundOnedrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundOnedrive() {
|
|
||||||
return foundOnedrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> megaLocationProperty() {
|
|
||||||
return megaLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getMegaLocation() {
|
|
||||||
return megaLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundMegaProperty() {
|
|
||||||
return foundMega;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundMega() {
|
|
||||||
return foundMega.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> pcloudLocationProperty() {
|
|
||||||
return pcloudLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getPcloudLocation() {
|
|
||||||
return pcloudLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundPcloudProperty() {
|
|
||||||
return foundPcloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundPcloud() {
|
|
||||||
return foundPcloud.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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"), //
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -1,30 +1,85 @@
|
|||||||
package org.cryptomator.ui.fxapp;
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class AutoUnlocker {
|
public class AutoUnlocker {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AutoUnlocker.class);
|
||||||
|
|
||||||
private final ObservableList<Vault> vaults;
|
private final ObservableList<Vault> vaults;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
|
private ScheduledFuture<?> unlockMissingFuture;
|
||||||
|
private ScheduledFuture<?> timeoutFuture;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows) {
|
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows, ScheduledExecutorService scheduler) {
|
||||||
this.vaults = vaults;
|
this.vaults = vaults;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
|
this.scheduler = scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlock() {
|
public void tryUnlockForTimespan(int timespan, TimeUnit timeUnit) {
|
||||||
vaults.stream().filter(Vault::isLocked) //
|
// Unlock all available auto unlock vaults
|
||||||
.filter(v -> v.getVaultSettings().unlockAfterStartup().get()) //
|
Predicate<Vault> shouldAutoUnlock = v -> v.getVaultSettings().unlockAfterStartup().get();
|
||||||
.<CompletionStage<Void>>reduce(CompletableFuture.completedFuture(null), //
|
unlockSequentially(vaults.stream().filter(shouldAutoUnlock)).thenRun(() -> startUnlockMissing(timespan, timeUnit));
|
||||||
(unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), //we don't care here about the exception, logged elsewhere
|
|
||||||
(unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletionStage<Void> unlockSequentially(Stream<Vault> vaultStream) {
|
||||||
|
// this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow.
|
||||||
|
return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null),
|
||||||
|
(prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)),
|
||||||
|
(prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startUnlockMissing(int timespan, TimeUnit timeUnit) {
|
||||||
|
// Start a temporary service if there are missing auto unlock vaults
|
||||||
|
if (getMissingAutoUnlockVaults().findAny().isPresent()) {
|
||||||
|
LOG.info("Found MISSING vaults, starting periodic check");
|
||||||
|
unlockMissingFuture = scheduler.scheduleWithFixedDelay(this::unlockMissing, 0, 1, TimeUnit.SECONDS);
|
||||||
|
timeoutFuture = scheduler.schedule(this::timeout, timespan, timeUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unlockMissing() {
|
||||||
|
List<Vault> missingAutoUnlockVaults = getMissingAutoUnlockVaults().toList();
|
||||||
|
missingAutoUnlockVaults.forEach(VaultListManager::redetermineVaultState);
|
||||||
|
unlockSequentially(missingAutoUnlockVaults.stream()).thenRun(this::stopUnlockMissing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopUnlockMissing() {
|
||||||
|
// Stop checking if there are no more missing vaults
|
||||||
|
if (getMissingAutoUnlockVaults().findAny().isEmpty()) {
|
||||||
|
LOG.info("No more MISSING vaults, stopping periodic check");
|
||||||
|
unlockMissingFuture.cancel(false);
|
||||||
|
timeoutFuture.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timeout() {
|
||||||
|
LOG.info("MISSING vaults periodic check timed out");
|
||||||
|
unlockMissingFuture.cancel(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<Vault> getMissingAutoUnlockVaults() {
|
||||||
|
return vaults.stream()
|
||||||
|
.filter(Vault::isMissing)
|
||||||
|
.filter(v -> v.getVaultSettings().unlockAfterStartup().get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class FxApplication {
|
public class FxApplication {
|
||||||
@@ -68,7 +69,6 @@ public class FxApplication {
|
|||||||
});
|
});
|
||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents();
|
launchEventHandler.startHandlingLaunchEvents();
|
||||||
autoUnlocker.unlock();
|
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,13 +5,14 @@ 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;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.cryptomator.ui.quit.QuitComponent;
|
import org.cryptomator.ui.quit.QuitComponent;
|
||||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||||
|
import org.cryptomator.ui.unlock.UnlockWorkflow;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -114,7 +115,7 @@ public class FxApplicationWindows {
|
|||||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||||
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
||||||
}, Platform::runLater) //
|
}, Platform::runLater) //
|
||||||
.thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
|
.thenAcceptAsync(UnlockWorkflow::run, executor)
|
||||||
.exceptionally(e -> {
|
.exceptionally(e -> {
|
||||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -102,14 +102,16 @@ public class StartController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadingKeyFailed(Throwable e) {
|
private void loadingKeyFailed(Throwable e) {
|
||||||
if (e instanceof UnlockCancelledException) {
|
switch (e) {
|
||||||
// ok
|
case UnlockCancelledException uce -> {} //ok
|
||||||
} else if (e instanceof VaultKeyInvalidException) {
|
case VaultKeyInvalidException vkie -> {
|
||||||
LOG.error("Invalid key"); //TODO: specific error screen
|
LOG.error("Invalid key"); //TODO: specific error screen
|
||||||
appWindows.showErrorWindow(e, window, null);
|
appWindows.showErrorWindow(e, window, null);
|
||||||
} else {
|
}
|
||||||
LOG.error("Failed to load key.", e);
|
default -> {
|
||||||
appWindows.showErrorWindow(e, window, null);
|
LOG.error("Failed to load key.", e);
|
||||||
|
appWindows.showErrorWindow(e, window, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -101,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();
|
||||||
@@ -166,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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user