mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 16:51:28 +00:00
Compare commits
429 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4f6f2b9b | ||
|
|
bf84598131 | ||
|
|
943be4557f | ||
|
|
8bc0b3a700 | ||
|
|
57110ff417 | ||
|
|
d5bdea4a75 | ||
|
|
72c8ca3368 | ||
|
|
43d8f7c47f | ||
|
|
9209e7698c | ||
|
|
2bdb0c3e6a | ||
|
|
677634930d | ||
|
|
546da7d11f | ||
|
|
8662abae5a | ||
|
|
f98782ba4e | ||
|
|
422077ac08 | ||
|
|
10b9e75972 | ||
|
|
00e420a248 | ||
|
|
f7ddb6e714 | ||
|
|
f1c8e3d3f2 | ||
|
|
cfc8c7bed7 | ||
|
|
71b876b32d | ||
|
|
cdc60abf66 | ||
|
|
b73ab4645b | ||
|
|
c70961405c | ||
|
|
d814c1cfca | ||
|
|
50413ca9d8 | ||
|
|
2a91403f2f | ||
|
|
5ec499ff01 | ||
|
|
86cf0d4d4e | ||
|
|
f2af6f355c | ||
|
|
58d01b5bf5 | ||
|
|
5e19cd409f | ||
|
|
4dd58a664e | ||
|
|
a7fc8d6fc4 | ||
|
|
5c31a34d5f | ||
|
|
67264c0d8c | ||
|
|
153ccef8a2 | ||
|
|
0e853b25f7 | ||
|
|
12302162b1 | ||
|
|
7d14b55bcb | ||
|
|
69fe5b8c27 | ||
|
|
df099df334 | ||
|
|
6bbdb4baa0 | ||
|
|
ea6318dfc4 | ||
|
|
93500a4efd | ||
|
|
b132bb6412 | ||
|
|
cf38a10284 | ||
|
|
15781b685d | ||
|
|
73442cddc8 | ||
|
|
bce9833929 | ||
|
|
29d0e258f1 | ||
|
|
1305e45564 | ||
|
|
9e18215d10 | ||
|
|
683276a6c6 | ||
|
|
f16a30ef29 | ||
|
|
82db3b59fb | ||
|
|
c191df9ee3 | ||
|
|
a2d6db0415 | ||
|
|
077825f98a | ||
|
|
381c1cd8d3 | ||
|
|
ae2c67a88b | ||
|
|
3ce0270cde | ||
|
|
e011a98fa0 | ||
|
|
9386804216 | ||
|
|
b791df01f7 | ||
|
|
376733106b | ||
|
|
8e6d2cf45a | ||
|
|
63cd3a02de | ||
|
|
1795a7aaa4 | ||
|
|
c8e4ed1ce4 | ||
|
|
99b25d0792 | ||
|
|
e640e36744 | ||
|
|
d10d8fb208 | ||
|
|
43e6ca41aa | ||
|
|
e2b4566174 | ||
|
|
d203db8075 | ||
|
|
7c4f5b28a7 | ||
|
|
7edd1dfa40 | ||
|
|
09550b77dc | ||
|
|
3bd43908ac | ||
|
|
f69efb85a5 | ||
|
|
bc5091a0d7 | ||
|
|
a199a2a06b | ||
|
|
dffd4d9dd9 | ||
|
|
737d98775b | ||
|
|
bee9c9f452 | ||
|
|
2b19fd6852 | ||
|
|
a5e9837c99 | ||
|
|
81a1052a76 | ||
|
|
ab2d740722 | ||
|
|
bf3a62a6b4 | ||
|
|
d163d02077 | ||
|
|
677607d210 | ||
|
|
fd3c8d7f78 | ||
|
|
cce74ef5e4 | ||
|
|
74058238ec | ||
|
|
78e458f01a | ||
|
|
07cfef2e41 | ||
|
|
35c5bd03de | ||
|
|
7cc1a1728c | ||
|
|
a79bf1952d | ||
|
|
c7ad587aa2 | ||
|
|
22b842e6c2 | ||
|
|
f96c52cdb1 | ||
|
|
ca426f6535 | ||
|
|
d6f489ea98 | ||
|
|
8043a4ea10 | ||
|
|
e5cb271e28 | ||
|
|
45f038bae2 | ||
|
|
3580728a5a | ||
|
|
749c5d9f24 | ||
|
|
6ae872fa85 | ||
|
|
773fa9804a | ||
|
|
cfa9beec8b | ||
|
|
5f4d13f662 | ||
|
|
f0d69b645b | ||
|
|
797724e640 | ||
|
|
3488db8c0e | ||
|
|
d53e0b24a4 | ||
|
|
347a7b3ef7 | ||
|
|
ff80585a2f | ||
|
|
8c65bd19c2 | ||
|
|
ad7a8835b3 | ||
|
|
9f2b5b6ee3 | ||
|
|
15726c6b21 | ||
|
|
1c824a37b0 | ||
|
|
7c04ba48a8 | ||
|
|
6f33cf8df7 | ||
|
|
8f85777887 | ||
|
|
931d6f0048 | ||
|
|
692b3828b8 | ||
|
|
320be05a66 | ||
|
|
2c7b407e94 | ||
|
|
20c66a6005 | ||
|
|
e6ff7a6d88 | ||
|
|
aca55734b0 | ||
|
|
f740a93b6c | ||
|
|
e9a71827ed | ||
|
|
dc3f80ffc9 | ||
|
|
75633a10bd | ||
|
|
dda526b83e | ||
|
|
47f7c437fe | ||
|
|
15e46f8ca5 | ||
|
|
5b13553866 | ||
|
|
e14d1f4028 | ||
|
|
a7405b4fca | ||
|
|
8c52c740d3 | ||
|
|
1d70eecbb7 | ||
|
|
3d95276a7f | ||
|
|
88020fcaf4 | ||
|
|
4e89b6365a | ||
|
|
52396f68f5 | ||
|
|
5233e6b690 | ||
|
|
3c469c80be | ||
|
|
0040c8a5f8 | ||
|
|
e8751d2116 | ||
|
|
9bc439a913 | ||
|
|
4989eacaf9 | ||
|
|
c7b257eb66 | ||
|
|
2f76b724a7 | ||
|
|
cd709260ce | ||
|
|
87202453b1 | ||
|
|
4786193731 | ||
|
|
90f523bee8 | ||
|
|
78bb996072 | ||
|
|
822963e1a9 | ||
|
|
773a5d3570 | ||
|
|
acc5277258 | ||
|
|
4ecb98a5e6 | ||
|
|
75093f1c09 | ||
|
|
0351595243 | ||
|
|
5ab74affb5 | ||
|
|
8230b74c78 | ||
|
|
5832e8cd49 | ||
|
|
0220af24d0 | ||
|
|
3e6d81ef1b | ||
|
|
852daaa88e | ||
|
|
7dfc4d9ded | ||
|
|
98002f63dc | ||
|
|
80696972cb | ||
|
|
a3d4eb6048 | ||
|
|
0f88e6c2fe | ||
|
|
3524f273de | ||
|
|
fb3d64c43f | ||
|
|
90d43f28d9 | ||
|
|
a29f10a504 | ||
|
|
6702747652 | ||
|
|
a8b920698d | ||
|
|
0b2685f271 | ||
|
|
120be431ee | ||
|
|
f50eb1f047 | ||
|
|
32d6661a72 | ||
|
|
f57d8dab6d | ||
|
|
5ba0e674f2 | ||
|
|
55f67785cd | ||
|
|
6d082330e6 | ||
|
|
0fdc5b2826 | ||
|
|
31f77e62ea | ||
|
|
3435a6701a | ||
|
|
d4367b97a6 | ||
|
|
1151157dff | ||
|
|
e9c58ac3d5 | ||
|
|
2833015ff1 | ||
|
|
12ef32835c | ||
|
|
eae5f4d870 | ||
|
|
30e1c989d0 | ||
|
|
d4b3eff42f | ||
|
|
9f0e269baf | ||
|
|
c6e9e33feb | ||
|
|
62c7f95f7c | ||
|
|
a304427973 | ||
|
|
c04597eca5 | ||
|
|
ba1945ebb8 | ||
|
|
e9551a076d | ||
|
|
ccd3da3b09 | ||
|
|
5e06fa7385 | ||
|
|
c3969f2f33 | ||
|
|
0a8cc7ed1e | ||
|
|
74f5be1605 | ||
|
|
3c376c4bf6 | ||
|
|
fedb62d99a | ||
|
|
8f97235b9a | ||
|
|
d127121007 | ||
|
|
d6a55d3006 | ||
|
|
d3d0cef2bb | ||
|
|
c514dc6235 | ||
|
|
98b3b14e6a | ||
|
|
9d4f9c12b9 | ||
|
|
d1c4eda072 | ||
|
|
835a5592fc | ||
|
|
c429de6a66 | ||
|
|
afe7737129 | ||
|
|
94b3ea37d6 | ||
|
|
4d7e16d58e | ||
|
|
2f17d80fe6 | ||
|
|
fbc598fed7 | ||
|
|
d053367f83 | ||
|
|
9f5c31e4e7 | ||
|
|
68d4b440e1 | ||
|
|
bcfba21900 | ||
|
|
c1801e3d22 | ||
|
|
bff0194f3e | ||
|
|
75f96eb08f | ||
|
|
4d1d6f449e | ||
|
|
eb0a2c1267 | ||
|
|
51d715caa1 | ||
|
|
b55bd8075c | ||
|
|
d368b01038 | ||
|
|
dc3894290b | ||
|
|
597bbdbdb8 | ||
|
|
2b255ed101 | ||
|
|
9ece1f66a1 | ||
|
|
e71b375437 | ||
|
|
bcb2b088ba | ||
|
|
f123dff0cd | ||
|
|
0ace5bb42b | ||
|
|
b656b591ed | ||
|
|
564370ec7a | ||
|
|
7dd0f0cb31 | ||
|
|
79e1285b38 | ||
|
|
9c26d17733 | ||
|
|
69f3a2bd5a | ||
|
|
9a9ef6c583 | ||
|
|
a8e73350a6 | ||
|
|
c8e131c49f | ||
|
|
3b8f2adedf | ||
|
|
9b00cd923c | ||
|
|
12d38335d8 | ||
|
|
2108a77e12 | ||
|
|
f3bfe7b64c | ||
|
|
752e61219c | ||
|
|
d308ee626a | ||
|
|
d861ef0d22 | ||
|
|
78a50548ab | ||
|
|
4c363a9abc | ||
|
|
9ed7438f05 | ||
|
|
5a4f714c93 | ||
|
|
c03bc9ed0b | ||
|
|
7c772e2767 | ||
|
|
3136e22414 | ||
|
|
508b9f5c64 | ||
|
|
d10c8fcf17 | ||
|
|
ceb3cbc43f | ||
|
|
ec909ce723 | ||
|
|
2ce7e84e3c | ||
|
|
d7da78fc07 | ||
|
|
e93e31e745 | ||
|
|
5dc8fd2582 | ||
|
|
77d81acb1e | ||
|
|
71d346eddd | ||
|
|
d9aa6ae91a | ||
|
|
c130441700 | ||
|
|
6af016f1fe | ||
|
|
ec2524f6ff | ||
|
|
27d4e00210 | ||
|
|
0cd2ecb0b3 | ||
|
|
a143ecdcf8 | ||
|
|
2d830cdb31 | ||
|
|
887e0332da | ||
|
|
c1504e29c1 | ||
|
|
0bb6e64d83 | ||
|
|
c7d1b9dbd6 | ||
|
|
a4c50da222 | ||
|
|
a558135fec | ||
|
|
7df449524a | ||
|
|
d475444829 | ||
|
|
828f32d3f6 | ||
|
|
dc6b5774b8 | ||
|
|
28db04e621 | ||
|
|
e2184ec009 | ||
|
|
5f1f95da74 | ||
|
|
4b29f0b4f3 | ||
|
|
f8bcbda8d7 | ||
|
|
e368087c3e | ||
|
|
ddb5da1d79 | ||
|
|
d2374fb8cd | ||
|
|
c73aa49b59 | ||
|
|
de86760a75 | ||
|
|
bfd8a62015 | ||
|
|
9292a3c89c | ||
|
|
732fc2f3dd | ||
|
|
9fb00fcc38 | ||
|
|
12b58a8a32 | ||
|
|
338eb45e24 | ||
|
|
e8c63dd3f3 | ||
|
|
2de23f8fff | ||
|
|
1a0624cc81 | ||
|
|
e907360419 | ||
|
|
ef6f87bb19 | ||
|
|
73bbcdcca1 | ||
|
|
6da8fc1f70 | ||
|
|
49e90515a1 | ||
|
|
983c35eb37 | ||
|
|
1f0c12ef7a | ||
|
|
ba514c8896 | ||
|
|
76c310f1fc | ||
|
|
874f0ee611 | ||
|
|
957f067e9b | ||
|
|
b1931f2c13 | ||
|
|
156e7bbb91 | ||
|
|
4abb41aebd | ||
|
|
67e1626de0 | ||
|
|
aebe848bef | ||
|
|
c40ef2f0f2 | ||
|
|
8fa562aa60 | ||
|
|
303f84166f | ||
|
|
9e594ea127 | ||
|
|
88977ecf20 | ||
|
|
538cac3749 | ||
|
|
30196f6a68 | ||
|
|
8bc79e684d | ||
|
|
7183efa073 | ||
|
|
92f621cd25 | ||
|
|
19d26de4ec | ||
|
|
d39c3969df | ||
|
|
c781755c6e | ||
|
|
4a3d579b76 | ||
|
|
6bad74720f | ||
|
|
77e2be22de | ||
|
|
7b4a3e1313 | ||
|
|
504a384225 | ||
|
|
550546c4b7 | ||
|
|
8dce21ea40 | ||
|
|
b565f1d0c0 | ||
|
|
fab70ef8c9 | ||
|
|
1c76c50a4b | ||
|
|
79e6a4cd48 | ||
|
|
ad6d5bfae9 | ||
|
|
a404740ceb | ||
|
|
9f7442c1c0 | ||
|
|
64845f0ffa | ||
|
|
ee68f9d47b | ||
|
|
65bb6773cb | ||
|
|
4bd4cd671b | ||
|
|
64abbc8205 | ||
|
|
165f190004 | ||
|
|
12a21bbb71 | ||
|
|
e1a72c41a5 | ||
|
|
4d4098e0e0 | ||
|
|
d52e59d7a4 | ||
|
|
9440fb186f | ||
|
|
9856792921 | ||
|
|
b142904cc3 | ||
|
|
806ffe704c | ||
|
|
6ca87d13f5 | ||
|
|
983a4d0b0f | ||
|
|
e95853deac | ||
|
|
d72d9f533c | ||
|
|
85a5146d4b | ||
|
|
0bece0f591 | ||
|
|
abc0f952e0 | ||
|
|
e6f43d1f05 | ||
|
|
25b9722019 | ||
|
|
157bef5df6 | ||
|
|
ef281f810f | ||
|
|
921dd8fe67 | ||
|
|
fb580ff79d | ||
|
|
0110e5bedd | ||
|
|
3cd99f680a | ||
|
|
34e4383c1e | ||
|
|
d4c3f02d8a | ||
|
|
839175a5e6 | ||
|
|
5924e443fb | ||
|
|
346ce67bc4 | ||
|
|
1477bf07a9 | ||
|
|
056990151a | ||
|
|
fa86d890fe | ||
|
|
5922743f19 | ||
|
|
afc853f5f5 | ||
|
|
be8243d9d1 | ||
|
|
1322b872b6 | ||
|
|
e46072c726 | ||
|
|
33ed41307e | ||
|
|
e865eaf412 | ||
|
|
e866b64352 | ||
|
|
a3a96496b6 | ||
|
|
75644a35ec | ||
|
|
ec09413575 | ||
|
|
f9c2807ce1 | ||
|
|
8075d33d39 | ||
|
|
d7dcc46988 | ||
|
|
01b2b47823 | ||
|
|
d087a5fdde | ||
|
|
43dbdb3e8f | ||
|
|
d938b1c3f7 | ||
|
|
59eda3159b | ||
|
|
7fabc6f52d | ||
|
|
b21ea61342 | ||
|
|
2952733a11 |
6
.github/stale.yml
vendored
6
.github/stale.yml
vendored
@@ -1,11 +1,13 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 180
|
||||
daysUntilStale: 365
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 30
|
||||
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
|
||||
|
||||
27
.github/workflows/appimage.yml
vendored
27
.github/workflows/appimage.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -28,6 +32,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -53,11 +60,17 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${JAVA_HOME}/jmods"
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress=1
|
||||
- name: Prepare additional launcher
|
||||
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||
env:
|
||||
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
|
||||
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
|
||||
- name: Run jpackage
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jpackage
|
||||
@@ -79,16 +92,17 @@ jobs:
|
||||
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=false"
|
||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
|
||||
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
||||
--resource-dir dist/linux/resources
|
||||
- name: Patch Cryptomator.AppDir
|
||||
run: |
|
||||
mv appdir/Cryptomator Cryptomator.AppDir
|
||||
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
|
||||
envsubst '${REVISION_NO} ${SEMVER_STR}' < dist/linux/appimage/resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
|
||||
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
||||
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
||||
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
||||
@@ -100,9 +114,6 @@ jobs:
|
||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
|
||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||
env:
|
||||
REVISION_NO: ${{ steps.versions.outputs.revNum }}
|
||||
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
|
||||
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
||||
run: |
|
||||
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
|
||||
@@ -146,6 +157,6 @@ jobs:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
*.AppImage
|
||||
*.zsync
|
||||
*.asc
|
||||
cryptomator-*.AppImage
|
||||
cryptomator-*.zsync
|
||||
cryptomator-*.asc
|
||||
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@@ -32,6 +32,7 @@ jobs:
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
- name: Build and Test
|
||||
run: >
|
||||
xvfb-run
|
||||
mvn -B verify
|
||||
jacoco:report
|
||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
@@ -42,13 +43,15 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
- name: Upload code coverage report
|
||||
id: codacyCoverageReporter
|
||||
if: "github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'pr:safe')"
|
||||
run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
|
||||
- name: Sign source tarball with key 615D449FE6E6A235
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
git archive --prefix="cryptomator-${{ github.ref_name }}/" -o "cryptomator-${{ github.ref_name }}.tar.gz" ${{ github.ref }}
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
||||
env:
|
||||
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
continue-on-error: true
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Draft a release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v1
|
||||
@@ -57,6 +60,9 @@ jobs:
|
||||
discussion_category_name: releases
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
cryptomator-*.tar.gz.asc
|
||||
fail_on_unmatched_files: true
|
||||
body: |-
|
||||
:construction: Work in Progress
|
||||
|
||||
|
||||
8
.github/workflows/debian.yml
vendored
8
.github/workflows/debian.yml
vendored
@@ -10,6 +10,9 @@ on:
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -38,6 +41,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -105,7 +111,7 @@ jobs:
|
||||
cryptomator_*_amd64.deb
|
||||
cryptomator_*.asc
|
||||
- name: Publish on PPA
|
||||
if: startsWith(github.ref, 'refs/tags/') || github.event.inputs.dput == 'true'
|
||||
if: startsWith(github.ref, 'refs/tags/') || inputs.dput
|
||||
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
|
||||
- name: Publish Debian package on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
63
.github/workflows/dl-stats.yml
vendored
Normal file
63
.github/workflows/dl-stats.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Report Download Stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0/15 * * * *' # run every 15 min - don't forget to adjust the "interval" in the json sent to the metrics endpoint
|
||||
|
||||
jobs:
|
||||
report-download-stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get download count of latest releases
|
||||
id: get-stats
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const query = `query($owner:String!, $name:String!) {
|
||||
repository(owner:$owner, name:$name){
|
||||
releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
isPrerelease
|
||||
tagName
|
||||
releaseAssets(first: 20) {
|
||||
nodes {
|
||||
name
|
||||
downloadCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo
|
||||
}
|
||||
return await github.graphql(query, variables)
|
||||
- name: Transform Results
|
||||
id: transform-stats
|
||||
run: |
|
||||
TIME=$(($(date +%s) / $INTERVAL * $INTERVAL))
|
||||
echo ${JSON_DATA} | jq --arg TIME "$TIME" --arg INTERVAL "$INTERVAL" -c '.repository.releases.nodes[] | select(.isPrerelease == false) | .tagName as $tagName | .releaseAssets.nodes[] | {filename: .name, downloads: .downloadCount, release: $tagName, time: ($TIME|tonumber), interval: ($INTERVAL|tonumber)}' > input.json
|
||||
|
||||
jq -c 'select(.filename|endswith("-x86_64.AppImage")) | {name: "github.releases.downloads", tags: ["file=AppImage", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
jq -c 'select(.filename|endswith("_amd64.deb")) | {name: "github.releases.downloads", tags: ["file=deb", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
jq -c 'select(.filename|endswith("-x64.msi")) | {name: "github.releases.downloads", tags: ["file=msi", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
jq -c 'select(.filename|endswith("-x64.exe")) | {name: "github.releases.downloads", tags: ["file=exe", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
jq -c 'select(.filename|endswith("-arm64.dmg")) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=arm64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
jq -c 'select(.filename|endswith(".dmg")) | select(.filename|endswith("-arm64.dmg")|not) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
|
||||
|
||||
RESULT=$(jq -s -c "." output.json)
|
||||
echo "::set-output name=result::${RESULT}"
|
||||
env:
|
||||
INTERVAL: 900
|
||||
JSON_DATA: ${{ steps.get-stats.outputs.result }}
|
||||
- name: Upload Results
|
||||
uses: fjogeleit/http-request-action@v1
|
||||
with:
|
||||
url: 'https://graphite-us-central1.grafana.net/metrics'
|
||||
method: 'POST'
|
||||
contentType: 'application/json'
|
||||
bearerToken: ${{ secrets.GRAFANA_GRAPHITE_TOKEN }}
|
||||
data: ${{ steps.transform-stats.outputs.result }}
|
||||
continue-on-error: true # currently there seems to be a problem with the metrics endpoint, failing every now and then
|
||||
60
.github/workflows/error-db.yml
vendored
Normal file
60
.github/workflows/error-db.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Update Error Database
|
||||
|
||||
on:
|
||||
discussion:
|
||||
types: [created, edited, category_changed, answered, unanswered]
|
||||
discussion_comment:
|
||||
types: [created, edited, deleted]
|
||||
|
||||
jobs:
|
||||
update-error-db:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.discussion.category.name == 'Errors'
|
||||
steps:
|
||||
- name: Query Discussion Data
|
||||
id: query-data
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
discussion(number: $discussionNumber) {
|
||||
id
|
||||
upvoteCount
|
||||
title
|
||||
url
|
||||
answer {
|
||||
url
|
||||
upvoteCount
|
||||
}
|
||||
comments {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo,
|
||||
discussionNumber: context.payload.discussion.number
|
||||
}
|
||||
return await github.graphql(query, variables)
|
||||
- name: Get Gist
|
||||
id: get-gist
|
||||
uses: andymckay/get-gist-action@master
|
||||
with:
|
||||
gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230
|
||||
- name: Merge Error Code Data
|
||||
run: |
|
||||
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
|
||||
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
|
||||
jq -s '.[0] * .[1]' original.json new.json > merged.json
|
||||
env:
|
||||
DISCUSSION: ${{ steps.query-data.outputs.result }}
|
||||
- name: Patch Gist
|
||||
uses: exuanbo/actions-deploy-gist@v1
|
||||
with:
|
||||
token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }}
|
||||
gist_id: accba9fb9555e7192271b85606f97230
|
||||
gist_file_name: errorcodes.json
|
||||
file_path: merged.json
|
||||
63
.github/workflows/mac-dmg.yml
vendored
63
.github/workflows/mac-dmg.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -28,6 +32,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -53,7 +60,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${JAVA_HOME}/jmods"
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -75,13 +82,16 @@ jobs:
|
||||
--app-version "${{ steps.versions.outputs.semVerNum }}"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||
--java-options "-Dsun.java2d.metal=true"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\""
|
||||
--mac-package-identifier org.cryptomator
|
||||
@@ -95,6 +105,16 @@ jobs:
|
||||
env:
|
||||
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
|
||||
REVISION_NO: ${{ steps.versions.outputs.revNum }}
|
||||
- name: Generate license for dmg
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
-Dlicense.thirdPartyFilename=license.rtf
|
||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||
-Dlicense.includedScopes=compile
|
||||
-Dlicense.excludedGroups=^org\.cryptomator
|
||||
-Dlicense.failOnMissing=true
|
||||
-Dlicense.licenseMergesUrl=file://${{ github.workspace }}/license/merges
|
||||
- name: Install codesign certificate
|
||||
run: |
|
||||
# create variables
|
||||
@@ -169,33 +189,14 @@ jobs:
|
||||
Cryptomator-${VERSION_NO}.dmg dmg
|
||||
env:
|
||||
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
|
||||
- name: Install notarization credentials
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
# create temporary keychain
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
|
||||
security create-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
|
||||
security set-keychain-settings -lut 900 ${KEYCHAIN_PATH}
|
||||
security unlock-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
|
||||
|
||||
# import credentials from secrets
|
||||
sudo xcode-select -s /Applications/Xcode_13.0.app
|
||||
xcrun notarytool store-credentials "${NOTARIZATION_KEYCHAIN_PROFILE}" --apple-id "${NOTARIZATION_APPLE_ID}" --password "${NOTARIZATION_PW}" --team-id "${NOTARIZATION_TEAM_ID}" --keychain "${KEYCHAIN_PATH}"
|
||||
env:
|
||||
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
|
||||
NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||
NOTARIZATION_PW: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||
NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||
NOTARIZATION_TMP_KEYCHAIN_PW: ${{ secrets.MACOS_NOTARIZATION_TMP_KEYCHAIN_PW }}
|
||||
- name: Notarize .dmg
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
|
||||
sudo xcode-select -s /Applications/Xcode_13.0.app
|
||||
xcrun notarytool submit Cryptomator-*.dmg --keychain-profile "${NOTARIZATION_KEYCHAIN_PROFILE}" --keychain "${KEYCHAIN_PATH}" --wait
|
||||
xcrun stapler staple Cryptomator-*.dmg
|
||||
env:
|
||||
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
|
||||
uses: cocoalibs/xcode-notarization-action@v1
|
||||
with:
|
||||
app-path: 'Cryptomator-*.dmg'
|
||||
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv Cryptomator-*.dmg Cryptomator-${{ steps.versions.outputs.semVerStr }}.dmg
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
@@ -209,10 +210,6 @@ jobs:
|
||||
if: ${{ always() }}
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Clean up notarization credentials
|
||||
if: ${{ always() }}
|
||||
run: security delete-keychain $RUNNER_TEMP/notarization.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -226,7 +223,7 @@ jobs:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
*.dmg
|
||||
*.asc
|
||||
Cryptomator-*.dmg
|
||||
Cryptomator-*.asc
|
||||
|
||||
|
||||
|
||||
2
.github/workflows/pullrequest.yml
vendored
2
.github/workflows/pullrequest.yml
vendored
@@ -23,4 +23,4 @@ jobs:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
129
.github/workflows/win-exe.yml
vendored
129
.github/workflows/win-exe.yml
vendored
@@ -4,10 +4,18 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
winget-release:
|
||||
description: 'Release artifacts to winget'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
WINFSP_MSI: https://github.com/winfsp/winfsp/releases/download/v1.10/winfsp-1.10.22006.msi
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -33,6 +41,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -58,7 +69,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${JAVA_HOME}/jmods"
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -80,15 +91,18 @@ jobs:
|
||||
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
|
||||
--java-options "-Dcryptomator.keychainPath=\"~/AppData/Roaming/Cryptomator/keychain.json\""
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\""
|
||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
|
||||
--resource-dir dist/win/resources
|
||||
--icon dist/win/resources/Cryptomator.ico
|
||||
- name: Patch Application Directory
|
||||
@@ -102,17 +116,22 @@ jobs:
|
||||
with:
|
||||
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
|
||||
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
|
||||
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
|
||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||
description: Cryptomator
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: appdir/Cryptomator
|
||||
recursive: true
|
||||
- name: Generate license
|
||||
- name: Generate license for MSI
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
||||
"-Dlicense.outputDirectory=dist/win/resources"
|
||||
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
||||
"-Dlicense.includedScopes=compile"
|
||||
"-Dlicense.excludedGroups=^org\.cryptomator"
|
||||
"-Dlicense.failOnMissing=true"
|
||||
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
||||
shell: pwsh
|
||||
- name: Create MSI
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jpackage
|
||||
@@ -140,7 +159,7 @@ jobs:
|
||||
with:
|
||||
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
|
||||
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
|
||||
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
|
||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||
description: Cryptomator Installer
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: installer
|
||||
@@ -175,6 +194,20 @@ jobs:
|
||||
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
||||
revNum: ${{ steps.versions.outputs.revNum }}
|
||||
|
||||
publish-winget:
|
||||
name: Publish on winget repo
|
||||
runs-on: windows-latest
|
||||
needs: [build-msi]
|
||||
if: github.event.action == 'release' || inputs.winget-release
|
||||
steps:
|
||||
- name: Submit package to Windows Package Manager Community Repository
|
||||
run: |
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
||||
$installerUrl = $github.release.assets | Where-Object -Property name -match 'Cryptomator-*.msi' | Select -ExpandProperty browser_download_url -First 1
|
||||
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
||||
shell: pwsh
|
||||
|
||||
build-exe:
|
||||
name: Build .exe installer
|
||||
runs-on: windows-latest
|
||||
@@ -193,15 +226,22 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Generate license
|
||||
- name: Generate license for exe
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
||||
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
||||
"-Dlicense.includedScopes=compile"
|
||||
"-Dlicense.excludedGroups=^org\.cryptomator"
|
||||
"-Dlicense.failOnMissing=true"
|
||||
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
||||
shell: pwsh
|
||||
- name: Download WinFsp
|
||||
run:
|
||||
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
|
||||
run: |
|
||||
$winfspUrl= (Select-String -Path ".\dist\win\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value
|
||||
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
|
||||
shell: pwsh
|
||||
- name: Compile to wixObj file
|
||||
run: >
|
||||
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
|
||||
@@ -217,18 +257,37 @@ jobs:
|
||||
run: >
|
||||
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
|
||||
-ext WixBalExtension
|
||||
-out installer/Cryptomator.exe
|
||||
-out installer/unsigned/Cryptomator-Installer.exe
|
||||
- name: Detach burn engine in preparation to sign
|
||||
run: >
|
||||
"${WIX}/bin/insignia.exe"
|
||||
-ib installer/unsigned/Cryptomator-Installer.exe
|
||||
-o tmp/engine.exe
|
||||
- name: Codesign burn engine
|
||||
uses: skymatic/code-sign-action@v1
|
||||
with:
|
||||
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
|
||||
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
|
||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||
description: Cryptomator Installer
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: tmp
|
||||
- name: Reattach signed burn engine to installer
|
||||
run : >
|
||||
"${WIX}/bin/insignia.exe"
|
||||
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
|
||||
-o installer/Cryptomator-Installer.exe
|
||||
- name: Codesign EXE
|
||||
uses: skymatic/code-sign-action@v1
|
||||
with:
|
||||
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
|
||||
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
|
||||
certificatesha1: FF52240075AD7D14AF25629FDF69635357C7D14B
|
||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||
description: Cryptomator Installer
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: installer
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv installer/Cryptomator.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
|
||||
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -251,5 +310,45 @@ jobs:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
*.exe
|
||||
*.asc
|
||||
Cryptomator-*.exe
|
||||
Cryptomator-*.asc
|
||||
|
||||
allowlist:
|
||||
name: Anti Virus Allowlisting
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-msi, build-exe]
|
||||
steps:
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: msi
|
||||
path: msi
|
||||
- name: Download .exe
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: exe
|
||||
path: exe
|
||||
- name: Collect files
|
||||
run: |
|
||||
mkdir files
|
||||
cp msi/*.msi files
|
||||
cp exe/*.exe files
|
||||
- name: Upload to Kaspersky
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.0
|
||||
with:
|
||||
protocol: ftps
|
||||
server: allowlist.kaspersky-labs.com
|
||||
port: 990
|
||||
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
|
||||
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
|
||||
local-dir: files/
|
||||
- name: Upload to Avast
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.0
|
||||
with:
|
||||
protocol: ftp
|
||||
server: whitelisting.avast.com
|
||||
port: 21
|
||||
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
||||
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
||||
local-dir: files/
|
||||
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -5,26 +5,9 @@
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# Eclipse Settings Files #
|
||||
.settings
|
||||
.project
|
||||
.classpath
|
||||
|
||||
# Maven #
|
||||
target/
|
||||
pom.xml.versionsBackup
|
||||
|
||||
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/shelf
|
||||
.idea/dictionaries/**
|
||||
!.idea/dictionaries/dict_*
|
||||
.idea/compiler.xml
|
||||
.idea/encodings.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/uiDesigner.xml
|
||||
.idea/**/libraries/
|
||||
*.iml
|
||||
|
||||
# Java Crash Logs
|
||||
hs_err_pid*.log
|
||||
13
.idea/.gitignore
generated
vendored
Normal file
13
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems
|
||||
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
/usage.statistics.xml
|
||||
/dictionaries/
|
||||
|
||||
# generated from Maven
|
||||
/jarRepositories.xml
|
||||
/modules.xml
|
||||
/*.iml
|
||||
/libraries/*.xml
|
||||
51
.idea/compiler.xml
generated
Normal file
51
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
</profile>
|
||||
<profile name="Annotation profile for Cryptomator Desktop App" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<option name="dagger.fastInit" value="enabled" />
|
||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.41/dagger-compiler-2.41.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.41/dagger-2.41.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.41/dagger-producers-2.41.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.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$/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.41/dagger-spi-2.41.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.5.30-1.0.0/symbol-processing-api-1.5.30-1.0.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.32/kotlin-stdlib-jdk8-1.5.32.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.32/kotlin-stdlib-1.5.32.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.32/kotlin-stdlib-common-1.5.32.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.32/kotlin-stdlib-jdk7-1.5.32.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.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$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.3.0/kotlinx-metadata-jvm-0.3.0.jar" />
|
||||
</processorPath>
|
||||
<module name="cryptomator" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/encodings.xml
generated
Normal file
8
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath="~/.config/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="~/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="~/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="~/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator/Plugins" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="~/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.p12Path="~/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="~/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator-Dev" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator-Dev/Plugins" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator-Dev" -Dfuse.experimental="true" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator-Dev" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="~/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.p12Path="~/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="~/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="~/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="~/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="~/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="~/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="~/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="~/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="~/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="~/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="~/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="~/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
|
||||
[](https://snyk.io/test/github/cryptomator/cryptomator)
|
||||
[](https://www.codacy.com/gh/cryptomator/cryptomator/dashboard)
|
||||
[](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
|
||||
[](http://twitter.com/Cryptomator)
|
||||
[](https://translate.cryptomator.org/)
|
||||
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
||||
@@ -36,8 +36,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
- [Jameson Lopp](https://www.lopp.net/)
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
@@ -82,7 +80,6 @@ For more information on the security details visit [cryptomator.org](https://doc
|
||||
|
||||
* JDK 17 (e.g. temurin)
|
||||
* Maven 3
|
||||
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
|
||||
|
||||
### Run Maven
|
||||
|
||||
|
||||
5
dist/linux/appimage/build.sh
vendored
5
dist/linux/appimage/build.sh
vendored
@@ -19,13 +19,15 @@ cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||
${JAVA_HOME}/bin/jlink \
|
||||
--output runtime \
|
||||
--module-path "${JAVA_HOME}/jmods" \
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
--compress=1
|
||||
|
||||
# create app dir
|
||||
envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||
${JAVA_HOME}/bin/jpackage \
|
||||
--verbose \
|
||||
--type app-image \
|
||||
@@ -48,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
||||
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
||||
--resource-dir ../resources
|
||||
|
||||
# transform AppDir
|
||||
|
||||
@@ -15,26 +15,11 @@ elif command -v pacman &> /dev/null; then # don't forget arch
|
||||
GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
|
||||
fi
|
||||
|
||||
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
|
||||
GTK_FLAG="-Djdk.gtk.version=2"
|
||||
fi
|
||||
|
||||
# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
|
||||
export LD_PRELOAD=lib/app/libjffi.so
|
||||
|
||||
# start Cryptomator
|
||||
./lib/runtime/bin/java \
|
||||
-p "lib/app/mods" \
|
||||
-cp "lib/app/*" \
|
||||
-Dfile.encoding="utf-8" \
|
||||
-Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
|
||||
-Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" \
|
||||
-Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
|
||||
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json" \
|
||||
-Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
|
||||
-Dcryptomator.buildNumber="appimage-${REVISION_NO}" \
|
||||
-Dcryptomator.appVersion="${SEMVER_STR}" \
|
||||
$GTK_FLAG \
|
||||
-Xss5m \
|
||||
-Xmx256m \
|
||||
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
|
||||
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
|
||||
bin/Cryptomator-gtk2
|
||||
else
|
||||
bin/Cryptomator
|
||||
fi
|
||||
@@ -7,5 +7,5 @@ Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Security;FileTools;
|
||||
StartupNotify=true
|
||||
StartupWMClass=org.cryptomator.launcher.Cryptomator
|
||||
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
|
||||
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2022-07-27" version="1.6.12"/>
|
||||
<release date="2022-07-26" version="1.6.11"/>
|
||||
<release date="2022-05-03" version="1.6.10"/>
|
||||
<release date="2022-04-27" version="1.6.9"/>
|
||||
<release date="2022-03-30" version="1.6.8"/>
|
||||
<release date="2021-12-16" version="1.6.5"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
4
dist/linux/debian/rules
vendored
4
dist/linux/debian/rules
vendored
@@ -18,7 +18,8 @@ override_dh_auto_build:
|
||||
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
|
||||
jlink \
|
||||
--output runtime \
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
@@ -39,6 +40,7 @@ override_dh_auto_build:
|
||||
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
|
||||
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
||||
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||
|
||||
12
dist/linux/launcher-gtk2.properties
vendored
Normal file
12
dist/linux/launcher-gtk2.properties
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
java-options=-Xss5m \
|
||||
-Xmx256m \
|
||||
-Dfile.encoding=\"utf-8\" \
|
||||
-Dcryptomator.appVersion=\"${SEMVER_STR}\" \
|
||||
-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\" \
|
||||
-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\" \
|
||||
-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\" \
|
||||
-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\" \
|
||||
-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\" \
|
||||
-Dcryptomator.showTrayIcon=false \
|
||||
-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\" \
|
||||
-Djdk.gtk.version=2
|
||||
2
dist/mac/dmg/.gitignore
vendored
2
dist/mac/dmg/.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
# created during build
|
||||
Cryptomator.app/
|
||||
runtime/
|
||||
dmg/
|
||||
*.dmg
|
||||
license.rtf
|
||||
80
dist/mac/dmg/build.sh
vendored
80
dist/mac/dmg/build.sh
vendored
@@ -14,9 +14,17 @@ while getopts ":s:" o; do
|
||||
done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
# prepare working dir and variables
|
||||
# prepare working dir
|
||||
cd $(dirname $0)
|
||||
rm -rf runtime dmg
|
||||
rm -rf runtime dmg *.app *.dmg
|
||||
|
||||
# set variables
|
||||
APP_NAME="Cryptomator"
|
||||
VENDOR="Skymatic GmbH"
|
||||
COPYRIGHT_YEARS="2016 - 2022"
|
||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||
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'`
|
||||
|
||||
@@ -31,13 +39,14 @@ fi
|
||||
|
||||
# compile
|
||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||
|
||||
# add runtime
|
||||
${JAVA_HOME}/bin/jlink \
|
||||
--output runtime \
|
||||
--module-path "${JAVA_HOME}/jmods" \
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
@@ -50,35 +59,48 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--runtime-image runtime \
|
||||
--input ../../../target/libs \
|
||||
--module-path ../../../target/mods \
|
||||
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
|
||||
--module ${MODULE_AND_MAIN_CLASS} \
|
||||
--dest . \
|
||||
--name Cryptomator \
|
||||
--vendor "Skymatic GmbH" \
|
||||
--copyright "(C) 2016 - 2022 Skymatic GmbH" \
|
||||
--name ${APP_NAME} \
|
||||
--vendor "${VENDOR}" \
|
||||
--copyright "(C) ${COPYRIGHT_YEARS} ${VENDOR}" \
|
||||
--app-version "${VERSION_NO}" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||
--app-version "${VERSION_NO}" \
|
||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
||||
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\"" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" \
|
||||
--java-options "-Dsun.java2d.metal=true" \
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
||||
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/${APP_NAME}/key.p12\"" \
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
|
||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||
--mac-package-identifier org.cryptomator \
|
||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||
--resource-dir ../resources
|
||||
|
||||
# transform app dir
|
||||
cp ../resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||
|
||||
# generate license
|
||||
mvn -B -f../../../pom.xml license:add-third-party \
|
||||
-Dlicense.thirdPartyFilename=license.rtf \
|
||||
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
||||
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
||||
-Dlicense.includedScopes=compile \
|
||||
-Dlicense.excludedGroups=^org\.cryptomator \
|
||||
-Dlicense.failOnMissing=true \
|
||||
-Dlicense.licenseMergesUrl=file://$(pwd)/../../../license/merges
|
||||
|
||||
# codesign
|
||||
if [ -n "${CODESIGN_IDENTITY}" ]; then
|
||||
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
|
||||
find ${APP_NAME}.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||
for JAR_PATH in `find ${APP_NAME}.app -name "*.jar"`; do
|
||||
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
|
||||
JAR_FILENAME=$(basename ${JAR_PATH})
|
||||
OUTPUT_PATH=${JAR_PATH%.*}
|
||||
@@ -93,25 +115,25 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
|
||||
rm -r ${OUTPUT_PATH}
|
||||
fi
|
||||
done
|
||||
echo "Codesigning Cryptomator.app..."
|
||||
codesign --force --deep --entitlements ../Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
|
||||
echo "Codesigning ${APP_NAME}.app..."
|
||||
codesign --force --deep --entitlements ../${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
|
||||
fi
|
||||
|
||||
# prepare dmg contents
|
||||
mkdir dmg
|
||||
mv Cryptomator.app dmg
|
||||
mv ${APP_NAME}.app dmg
|
||||
cp resources/macFUSE.webloc dmg
|
||||
|
||||
# create dmg
|
||||
create-dmg \
|
||||
--volname Cryptomator \
|
||||
--volicon "resources/Cryptomator-Volume.icns" \
|
||||
--background "resources/Cryptomator-background.tiff" \
|
||||
--volname ${APP_NAME} \
|
||||
--volicon "resources/${APP_NAME}-Volume.icns" \
|
||||
--background "resources/${APP_NAME}-background.tiff" \
|
||||
--window-pos 400 100 \
|
||||
--window-size 640 694 \
|
||||
--icon-size 128 \
|
||||
--icon "Cryptomator.app" 128 245 \
|
||||
--hide-extension "Cryptomator.app" \
|
||||
--icon "${APP_NAME}.app" 128 245 \
|
||||
--hide-extension "${APP_NAME}.app" \
|
||||
--icon "macFUSE.webloc" 320 501 \
|
||||
--hide-extension "macFUSE.webloc" \
|
||||
--app-drop-link 512 245 \
|
||||
@@ -119,4 +141,4 @@ create-dmg \
|
||||
--icon ".background" 128 758 \
|
||||
--icon ".fseventsd" 320 758 \
|
||||
--icon ".VolumeIcon.icns" 512 758 \
|
||||
Cryptomator-${VERSION_NO}.dmg dmg
|
||||
${APP_NAME}-${VERSION_NO}.dmg dmg
|
||||
|
||||
100
dist/mac/dmg/resources/license.rtf
vendored
100
dist/mac/dmg/resources/license.rtf
vendored
@@ -1,100 +0,0 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2512
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
{\*\expandedcolortbl;;}
|
||||
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
|
||||
\deftab720
|
||||
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
|
||||
|
||||
\f0\b\fs24 \cf0 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.
|
||||
\f1\b0 \
|
||||
\
|
||||
|
||||
\f0\b \'a9 2016 \'96 2022 Skymatic GmbH
|
||||
\f1\b0 \
|
||||
\
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
||||
\
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
|
||||
\
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
|
||||
\
|
||||
|
||||
\f0\b Cryptomator uses 49 third-party dependencies under the following licenses:
|
||||
\f1\b0 \
|
||||
Apache License v2.0:\
|
||||
- jffi (com.github.jnr:jffi:1.2.23 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jffi"}}{\fldrslt http://github.com/jnr/jffi}})\
|
||||
- jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm}})\
|
||||
- jnr-constants (com.github.jnr:jnr-constants:0.9.15 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-constants"}}{\fldrslt http://github.com/jnr/jnr-constants}})\
|
||||
- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-ffi"}}{\fldrslt http://github.com/jnr/jnr-ffi}})\
|
||||
- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - {\field{\*\fldinst{HYPERLINK "http://findbugs.sourceforge.net/"}}{\fldrslt http://findbugs.sourceforge.net/}})\
|
||||
- Gson (com.google.code.gson:gson:2.8.6 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/gson/gson"}}{\fldrslt https://github.com/google/gson/gson}})\
|
||||
- Dagger (com.google.dagger:dagger:2.29.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/dagger"}}{\fldrslt https://github.com/google/dagger}})\
|
||||
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation}} )\
|
||||
- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/failureaccess"}}{\fldrslt https://github.com/google/guava/failureaccess}})\
|
||||
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/guava"}}{\fldrslt https://github.com/google/guava/guava}})\
|
||||
- Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/listenablefuture"}}{\fldrslt https://github.com/google/guava/listenablefuture}})\
|
||||
- J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/j2objc/"}}{\fldrslt https://github.com/google/j2objc/}})\
|
||||
- Apache Commons CLI (commons-cli:commons-cli:1.4 - {\field{\*\fldinst{HYPERLINK "http://commons.apache.org/proper/commons-cli/"}}{\fldrslt http://commons.apache.org/proper/commons-cli/}})\
|
||||
- javax.inject (javax.inject:javax.inject:1 - {\field{\*\fldinst{HYPERLINK "http://code.google.com/p/atinject/"}}{\fldrslt http://code.google.com/p/atinject/}})\
|
||||
- Java Native Access (net.java.dev.jna:jna:5.6.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
|
||||
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
|
||||
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - {\field{\*\fldinst{HYPERLINK "https://commons.apache.org/proper/commons-lang/"}}{\fldrslt https://commons.apache.org/proper/commons-lang/}})\
|
||||
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - {\field{\*\fldinst{HYPERLINK "http://hc.apache.org/httpcomponents-core-ga"}}{\fldrslt http://hc.apache.org/httpcomponents-core-ga}})\
|
||||
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - {\field{\*\fldinst{HYPERLINK "http://jackrabbit.apache.org/jackrabbit-webdav/"}}{\fldrslt http://jackrabbit.apache.org/jackrabbit-webdav/}})\
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
|
||||
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
|
||||
BSD:\
|
||||
- asm (org.ow2.asm:asm:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
|
||||
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
|
||||
- asm-commons (org.ow2.asm:asm-commons:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
|
||||
- asm-tree (org.ow2.asm:asm-tree:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
|
||||
- asm-util (org.ow2.asm:asm-util:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
|
||||
Eclipse Public License - Version 1.0:\
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
|
||||
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
|
||||
Eclipse Public License - v 2.0:\
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
|
||||
GPLv2:\
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
|
||||
GPLv2+CE:\
|
||||
- Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - {\field{\*\fldinst{HYPERLINK "http://servlet-spec.java.net"}}{\fldrslt http://servlet-spec.java.net}})\
|
||||
- javafx-base (org.openjfx:javafx-base:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-base/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-base/}})\
|
||||
- javafx-controls (org.openjfx:javafx-controls:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-controls/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-controls/}})\
|
||||
- javafx-fxml (org.openjfx:javafx-fxml:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-fxml/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-fxml/}})\
|
||||
- javafx-graphics (org.openjfx:javafx-graphics:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-graphics/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-graphics/}})\
|
||||
LGPL 2.1:\
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
|
||||
- Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)\
|
||||
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
|
||||
MIT License:\
|
||||
- java jwt (com.auth0:java-jwt:3.12.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/auth0/java-jwt"}}{\fldrslt https://github.com/auth0/java-jwt}})\
|
||||
- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-x86asm"}}{\fldrslt http://github.com/jnr/jnr-x86asm}})\
|
||||
- jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined)\
|
||||
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/nulab/zxcvbn4j"}}{\fldrslt https://github.com/nulab/zxcvbn4j}})\
|
||||
- Checker Qual (org.checkerframework:checker-qual:3.5.0 - {\field{\*\fldinst{HYPERLINK "https://checkerframework.org"}}{\fldrslt https://checkerframework.org}})\
|
||||
- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - {\field{\*\fldinst{HYPERLINK "http://www.slf4j.org"}}{\fldrslt http://www.slf4j.org}})\
|
||||
The BSD 2-Clause License:\
|
||||
- EasyBind (com.tobiasdiez:easybind:2.1.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/tobiasdiez/EasyBind"}}{\fldrslt https://github.com/tobiasdiez/EasyBind}})\
|
||||
\
|
||||
|
||||
\f0\b Cryptomator uses other third-party assets under the following licenses:
|
||||
\f1\b0 \
|
||||
SIL OFL 1.1 License:\
|
||||
- Font Awesome 5.12.0 ({\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt https://fontawesome.com/}})\
|
||||
\
|
||||
}
|
||||
49
dist/mac/dmg/resources/licenseTemplate.ftl
vendored
Normal file
49
dist/mac/dmg/resources/licenseTemplate.ftl
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<#function artifactFormat p>
|
||||
<#if p.name?index_of('Unnamed') > -1>
|
||||
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.artifactId + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
|
||||
<#else>
|
||||
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.name + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
|
||||
</#if>
|
||||
</#function>
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2512
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
{\*\expandedcolortbl;;}
|
||||
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
|
||||
\deftab720
|
||||
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
|
||||
|
||||
\f0\b\fs24 \cf0 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.
|
||||
\f1\b0 \
|
||||
\
|
||||
|
||||
\f0\b \'a9 2016 \'96 2022 Skymatic GmbH
|
||||
\f1\b0 \
|
||||
\
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
||||
\
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
|
||||
\
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
|
||||
\
|
||||
|
||||
\f0\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:
|
||||
\f1\b0 \
|
||||
<#list licenseMap as e>
|
||||
<#assign license = e.getKey()/>
|
||||
<#assign projects = e.getValue()/>
|
||||
<#if projects?size > 0>
|
||||
${license}:\
|
||||
<#list projects as project>
|
||||
- ${artifactFormat(project)}\
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
\
|
||||
|
||||
\f0\b Cryptomator uses other third-party assets under the following licenses:
|
||||
\f1\b0 \
|
||||
SIL OFL 1.1 License:\
|
||||
- {\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt Font Awesome}} (5.12.0)\
|
||||
\
|
||||
}
|
||||
23
dist/win/build.bat
vendored
23
dist/win/build.bat
vendored
@@ -1,2 +1,23 @@
|
||||
@echo off
|
||||
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1
|
||||
:: Default values for Cryptomator builds
|
||||
SET APPNAME="Cryptomator"
|
||||
SET MAIN_JAR_GLOB="cryptomator-*"
|
||||
SET UPGRADE_UUID="bda45523-42b1-4cae-9354-a45475ed4775"
|
||||
SET VENDOR="Skymatic GmbH"
|
||||
SET FIRST_COPYRIGHT_YEAR=2016
|
||||
SET ABOUT_URL="https://cryptomator.org"
|
||||
SET UPDATE_URL="https://cryptomator.org/downloads/"
|
||||
SET HELP_URL="https://cryptomator.org/contact/"
|
||||
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||
|
||||
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1^
|
||||
-AppName %APPNAME%^
|
||||
-MainJarGlob "%MAIN_JAR_GLOB%"^
|
||||
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
|
||||
-UpgradeUUID "%UPGRADE_UUID%"^
|
||||
-Vendor ""%VENDOR%""^
|
||||
-CopyrightStartYear %FIRST_COPYRIGHT_YEAR%^
|
||||
-AboutUrl "%ABOUT_URL%"^
|
||||
-HelpUrl "%HELP_URL%"^
|
||||
-UpdateUrl "%UPDATE_URL%"^
|
||||
-Clean 1
|
||||
94
dist/win/build.ps1
vendored
94
dist/win/build.ps1
vendored
@@ -1,5 +1,15 @@
|
||||
# check parameters
|
||||
$clean = $args[0] -eq "fresh"
|
||||
Param(
|
||||
[Parameter(Mandatory, HelpMessage="Please provide a name for the app")][string] $AppName,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide the glob pattern to identify the main jar")][string] $MainJarGlob,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide the module- and main class path to start the app")][string] $ModuleAndMainClass,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide the windows upgrade uuid for the installer")][string] $UpgradeUUID,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide the name of the vendor")][string] $Vendor,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide the starting year for the copyright notice")][int] $CopyrightStartYear,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide a help url")][string] $HelpUrl,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
|
||||
[Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
|
||||
[bool] $clean
|
||||
)
|
||||
|
||||
# check preconditions
|
||||
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
|
||||
@@ -24,12 +34,11 @@ Write-Output "`$revisionNo=$revisionNo"
|
||||
Write-Output "`$buildDir=$buildDir"
|
||||
Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
|
||||
|
||||
$vendor = "Skymatic GmbH"
|
||||
$copyright = "(C) 2016 - 2022 Skymatic GmbH"
|
||||
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
||||
|
||||
# compile
|
||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
|
||||
Copy-Item "$buildDir\..\..\target\cryptomator-*.jar" -Destination "$buildDir\..\..\target\mods"
|
||||
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
||||
|
||||
# add runtime
|
||||
$runtimeImagePath = '.\runtime'
|
||||
@@ -41,13 +50,14 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
||||
--verbose `
|
||||
--output runtime `
|
||||
--module-path "$Env:JAVA_HOME/jmods" `
|
||||
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
|
||||
--strip-native-commands `
|
||||
--no-header-files `
|
||||
--no-man-pages `
|
||||
--strip-debug `
|
||||
--compress=1
|
||||
|
||||
$appPath = '.\Cryptomator'
|
||||
$appPath = ".\$AppName"
|
||||
if ($clean -and (Test-Path -Path $appPath)) {
|
||||
Remove-Item -Path $appPath -Force -Recurse
|
||||
}
|
||||
@@ -59,85 +69,91 @@ if ($clean -and (Test-Path -Path $appPath)) {
|
||||
--runtime-image runtime `
|
||||
--input ../../target/libs `
|
||||
--module-path ../../target/mods `
|
||||
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator `
|
||||
--module $ModuleAndMainClass `
|
||||
--dest . `
|
||||
--name Cryptomator `
|
||||
--vendor $vendor `
|
||||
--name $AppName `
|
||||
--vendor $Vendor `
|
||||
--copyright $copyright `
|
||||
--java-options "-Xss5m" `
|
||||
--java-options "-Xmx256m" `
|
||||
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
|
||||
--app-version "$semVerNo.$revisionNo" `
|
||||
--java-options "-Dfile.encoding=`"utf-8`"" `
|
||||
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/Cryptomator`"" `
|
||||
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/Cryptomator/Plugins`"" `
|
||||
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/Cryptomator/settings.json`"" `
|
||||
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/Cryptomator/ipc.socket`"" `
|
||||
--java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/Cryptomator/keychain.json`"" `
|
||||
--java-options "-Dcryptomator.mountPointsDir=`"~/Cryptomator`"" `
|
||||
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
|
||||
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
|
||||
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
|
||||
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
|
||||
--java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
|
||||
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
|
||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
|
||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"~/AppData/Roaming/$AppName/keychain.json`"" `
|
||||
--java-options "-Dcryptomator.showTrayIcon=true" `
|
||||
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
|
||||
--resource-dir resources `
|
||||
--icon resources/Cryptomator.ico
|
||||
--icon resources/$AppName.ico
|
||||
|
||||
#Create RTF license for msi
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
||||
"-Dlicense.outputDirectory=$buildDir\resources\"
|
||||
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
||||
"-Dlicense.includedScopes=compile" `
|
||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
||||
"-Dlicense.failOnMissing=true" `
|
||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
||||
|
||||
# patch app dir
|
||||
Copy-Item "contrib\*" -Destination "Cryptomator"
|
||||
attrib -r "Cryptomator\Cryptomator.exe"
|
||||
|
||||
$aboutUrl="https://cryptomator.org"
|
||||
$updateUrl="https://cryptomator.org/downloads/"
|
||||
$helpUrl="https://cryptomator.org/contact/"
|
||||
Copy-Item "contrib\*" -Destination "$AppName"
|
||||
attrib -r "$AppName\$AppName.exe"
|
||||
|
||||
# create .msi
|
||||
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
||||
& "$Env:JAVA_HOME\bin\jpackage" `
|
||||
--verbose `
|
||||
--type msi `
|
||||
--win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 `
|
||||
--app-image Cryptomator `
|
||||
--win-upgrade-uuid $UpgradeUUID `
|
||||
--app-image $AppName `
|
||||
--dest installer `
|
||||
--name Cryptomator `
|
||||
--vendor $vendor `
|
||||
--name $AppName `
|
||||
--vendor $Vendor `
|
||||
--copyright $copyright `
|
||||
--app-version "$semVerNo" `
|
||||
--win-menu `
|
||||
--win-dir-chooser `
|
||||
--win-shortcut-prompt `
|
||||
--win-update-url $updateUrl `
|
||||
--win-menu-group Cryptomator `
|
||||
--win-menu-group $AppName `
|
||||
--resource-dir resources `
|
||||
--about-url $aboutUrl `
|
||||
--license-file resources/license.rtf `
|
||||
--win-update-url $UpdateUrl `
|
||||
--about-url $AboutUrl `
|
||||
--file-associations resources/FAvaultFile.properties
|
||||
|
||||
#Create RTF license for bundle
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\"
|
||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
||||
"-Dlicense.includedScopes=compile" `
|
||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
||||
"-Dlicense.failOnMissing=true" `
|
||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
||||
|
||||
# download Winfsp
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
|
||||
$winfspMsiUrl = "https://github.com/winfsp/winfsp/releases/download/v1.10/winfsp-1.10.22006.msi"
|
||||
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winfsp-download.url" -Pattern 'https:.*').Matches.Value
|
||||
Write-Output "Downloading ${winfspMsiUrl}..."
|
||||
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
||||
|
||||
# copy MSI to bundle resources
|
||||
Copy-Item ".\installer\Cryptomator-*.msi" -Destination ".\bundle\resources\Cryptomator.msi"
|
||||
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
|
||||
|
||||
# create bundle including winfsp
|
||||
& "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ `
|
||||
-dBundleVersion="$semVerNo.$revisionNo" `
|
||||
-dBundleVendor="$vendor" `
|
||||
-dBundleVendor="$Vendor" `
|
||||
-dBundleCopyright="$copyright" `
|
||||
-dAboutUrl="$aboutUrl" `
|
||||
-dHelpUrl="$helpUrl" `
|
||||
-dUpdateUrl="$updateUrl"
|
||||
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\CryptomatorBundle.exe
|
||||
-dAboutUrl="$AboutUrl" `
|
||||
-dHelpUrl="$HelpUrl" `
|
||||
-dUpdateUrl="$UpdateUrl"
|
||||
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\$AppName-Installer.exe
|
||||
14
dist/win/bundle/resources/licenseTemplate.ftl
vendored
14
dist/win/bundle/resources/licenseTemplate.ftl
vendored
@@ -5,18 +5,18 @@
|
||||
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
|
||||
</#if>
|
||||
</#function>
|
||||
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
|
||||
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
|
||||
{\colortbl ;\red0\green0\blue255;}
|
||||
\viewkind4\uc1
|
||||
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||
\vieww12000\viewh15840\viewkind0
|
||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||
\par
|
||||
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par
|
||||
\b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
|
||||
\par
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||
\par
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
|
||||
\par
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
|
||||
\par
|
||||
|
||||
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
|
||||
@@ -26,7 +26,7 @@ You should have received a copy of the GNU General Public License along with thi
|
||||
<#if projects?size > 0>
|
||||
\tab ${license}:\par
|
||||
<#list projects as project>
|
||||
\tab\tab- ${artifactFormat(project)}\par
|
||||
\tab\tab - ${artifactFormat(project)}\par
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
@@ -38,4 +38,4 @@ You should have received a copy of the GNU General Public License along with thi
|
||||
\b Cryptomator dynamically links to third-party libraries under the following license:\b0\par
|
||||
\tab Uncategorized License:\par
|
||||
\tab\tab - WinFsp - Windows File System Proxy, Copyright (C) Bill Zissimopoulos ({{\field{\*\fldinst{HYPERLINK https://github.com/billziss-gh/winfsp }}{\fldrslt{https://github.com/billziss-gh/winfsp\ul0\cf0}}}}\f0\fs16 )\b\par
|
||||
}
|
||||
}
|
||||
|
||||
2
dist/win/bundle/resources/winfsp-download.url
vendored
Normal file
2
dist/win/bundle/resources/winfsp-download.url
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[InternetShortcut]
|
||||
URL=https://github.com/winfsp/winfsp/releases/download/v1.11/winfsp-1.11.22176.msi
|
||||
6
dist/win/contrib/patchWebDAV.bat
vendored
6
dist/win/contrib/patchWebDAV.bat
vendored
@@ -1,3 +1,7 @@
|
||||
@echo off
|
||||
:: Default values for Cryptomator builds
|
||||
SET LOOPBACK_ALIAS="cryptomator-vault"
|
||||
|
||||
cd %~dp0
|
||||
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1
|
||||
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
|
||||
-LoopbackAlias %LOOPBACK_ALIAS%
|
||||
12
dist/win/contrib/patchWebDAV.ps1
vendored
12
dist/win/contrib/patchWebDAV.ps1
vendored
@@ -1,10 +1,16 @@
|
||||
#Requires -RunAsAdministrator
|
||||
Param(
|
||||
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
|
||||
)
|
||||
|
||||
# Adds for address 127.0.0.1 the 'cryptomator-vault' alias to the hosts file
|
||||
# Adds an alias for 127.0.0.1 to the hosts file
|
||||
function Add-AliasToHost {
|
||||
param (
|
||||
[string]$LoopbackAlias
|
||||
)
|
||||
$sysdir = [Environment]::SystemDirectory
|
||||
$hostsFile = "$sysdir\drivers\etc\hosts"
|
||||
$aliasLine = '127.0.0.1 cryptomator-vault'
|
||||
$aliasLine = "127.0.0.1 $LoopbackAlias"
|
||||
|
||||
foreach ($line in Get-Content $hostsFile) {
|
||||
if ($line -eq $aliasLine){
|
||||
@@ -49,7 +55,7 @@ function Edit-ProviderOrder {
|
||||
}
|
||||
|
||||
|
||||
Add-AliasToHost
|
||||
Add-AliasToHost $LoopbackAlias
|
||||
Write-Output 'Ensured alias exists in hosts file'
|
||||
|
||||
Set-WebDAVFileSizeLimit
|
||||
|
||||
27
dist/win/resources/customWizard.wxi
vendored
27
dist/win/resources/customWizard.wxi
vendored
@@ -13,7 +13,6 @@
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
@@ -24,8 +23,9 @@
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
|
||||
<!-- custom end dialog -->
|
||||
<!-- custom end dialogs -->
|
||||
<Publish Dialog="MyExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
|
||||
<Publish Dialog="MyFatalErrorDlg" Control="Finish" Event="EndDialog" Value="Return" Order="998">1</Publish>
|
||||
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed</Publish>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
|
||||
@@ -75,11 +75,34 @@
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<!-- copy pasta from https://github.com/wixtoolset/wix3/blob/develop/src/ext/UIExtension/wixlib/FatalError.wxs with adjustments-->
|
||||
<Dialog Id="MyFatalErrorDlg" Width="370" Height="270" Title="!(loc.FatalError_Title)">
|
||||
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
|
||||
<Publish Event="EndDialog" Value="Exit">1</Publish>
|
||||
</Control>
|
||||
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUICancel)" />
|
||||
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="!(loc.FatalErrorBitmap)" />
|
||||
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||
<Control Id="Title" Type="Text" X="135" Y="20" Width="220" Height="60" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorTitle)" />
|
||||
<Control Id="Description" Type="Text" X="135" Y="70" Width="220" Height="80" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorDescription1) !(loc.FatalErrorDescription2)" />
|
||||
<Control Id="DescriptionReason1" Type="Text" X="135" Y="160" Width="220" Height="20" Transparent="yes" NoPrefix="yes" Hidden="yes" >
|
||||
<Text>Reason:</Text>
|
||||
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
|
||||
</Control>
|
||||
<Control Id="DescriptionReason2" Type="Text" X="135" Y="170" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Hidden="yes" >
|
||||
<Text>Application to update was still running during installation.</Text>
|
||||
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<InstallUISequence>
|
||||
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
|
||||
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
|
||||
</InstallUISequence>
|
||||
<AdminUISequence>
|
||||
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
|
||||
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
|
||||
</AdminUISequence>
|
||||
|
||||
</UI>
|
||||
|
||||
14
dist/win/resources/licenseTemplate.ftl
vendored
14
dist/win/resources/licenseTemplate.ftl
vendored
@@ -5,18 +5,18 @@
|
||||
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
|
||||
</#if>
|
||||
</#function>
|
||||
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
|
||||
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
|
||||
{\colortbl ;\red0\green0\blue255;}
|
||||
\viewkind4\uc1
|
||||
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||
\vieww12000\viewh15840\viewkind0
|
||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||
\par
|
||||
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par
|
||||
\b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
|
||||
\par
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||
\par
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
|
||||
\par
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
|
||||
\par
|
||||
|
||||
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
|
||||
@@ -26,12 +26,12 @@ You should have received a copy of the GNU General Public License along with thi
|
||||
<#if projects?size > 0>
|
||||
\tab ${license}:\par
|
||||
<#list projects as project>
|
||||
\tab\tab- ${artifactFormat(project)}\par
|
||||
\tab\tab - ${artifactFormat(project)}\par
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
\par
|
||||
\b Cryptomator uses other third-party assets under the following licenses:\b0\par
|
||||
\tab SIL OFL 1.1 License:\par
|
||||
\tab\tab - Font Awesome 5.12.0 ({{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
|
||||
\tab\tab - Font Awesome (5.12.0 - {{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
|
||||
}
|
||||
31
dist/win/resources/main.wxs
vendored
31
dist/win/resources/main.wxs
vendored
@@ -86,12 +86,12 @@
|
||||
<!-- Non-Opening ProgID -->
|
||||
<DirectoryRef Id="INSTALLDIR">
|
||||
<Component Win64="yes" Id="nonStartingProgID" >
|
||||
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\Cryptomator-Vault.ico" Name="Cryptomator-Vault.ico"></File>
|
||||
<ProgId Id="Cryptomator.Encrypted.1" Description="Cryptomator Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
|
||||
<Extension Id="c9r" Advertise="no" ContentType="application/vnd.cryptomator.encrypted">
|
||||
<MIME ContentType="application/vnd.cryptomator.encrypted" Default="yes"></MIME>
|
||||
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileC9rC9s)" Name="$(var.IconFileC9rC9s)"></File>
|
||||
<ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
|
||||
<Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
|
||||
<MIME ContentType="$(var.ProgIdContentType)" Default="yes"></MIME>
|
||||
</Extension>
|
||||
<Extension Id="c9s" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"/>
|
||||
<Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||
</ProgId>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
@@ -127,6 +127,20 @@
|
||||
<!-- WebDAV patches -->
|
||||
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
|
||||
|
||||
<!-- Running App detection and exit -->
|
||||
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
||||
<util:CloseApplication
|
||||
Target="$(var.CloseApplicationTarget)"
|
||||
Id="Close$(var.JpAppName)"
|
||||
CloseMessage="no"
|
||||
RebootPrompt="no"
|
||||
PromptToContinue="yes"
|
||||
Description="A running instance of $(var.JpAppName) is found. Please close it to continue."
|
||||
Property="FOUNDRUNNINGAPP"
|
||||
>
|
||||
</util:CloseApplication>
|
||||
<CustomAction Id="FailOnRunningApp" Impersonate="no" ExeCommand="[SystemFolder]\cmd.exe /c "exit 1"" Directory="INSTALLDIR" Execute="immediate" Return="check" />
|
||||
|
||||
<?ifdef JpIcon ?>
|
||||
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
|
||||
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
|
||||
@@ -155,7 +169,12 @@
|
||||
<?ifndef JpAllowDowngrades ?>
|
||||
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
||||
<?endif?>
|
||||
<RemoveExistingProducts Before="CostInitialize"/>
|
||||
|
||||
<!-- Check and fail if Cryptomator is running -->
|
||||
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
||||
<Custom Action="FailOnRunningApp" After="WixCloseApplications" >FOUNDRUNNINGAPP</Custom>
|
||||
|
||||
<RemoveExistingProducts After="InstallValidate"/>
|
||||
|
||||
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
39
dist/win/resources/overrides.wxi
vendored
Normal file
39
dist/win/resources/overrides.wxi
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Stub by design -->
|
||||
|
||||
<!--
|
||||
overrides.wxi is a placeholder to set/alter WiX variables referenced from default
|
||||
main.wxs file.
|
||||
|
||||
Put custom overrides.wxi in resource directory to replace this default file.
|
||||
|
||||
Override default overrides.wxi if configuring of msi installers through jpackage
|
||||
command line is not sufficient.
|
||||
|
||||
WiX variables referenced from default main.wxs that can be altered in custom overrides.wxi:
|
||||
|
||||
- JpProductLanguage
|
||||
Value of `Language` attribute of `Product` WiX element. Default value is 1033.
|
||||
|
||||
- JpInstallerVersion
|
||||
Value of `InstallerVersion` attribute of `Package` WiX element. Default value is 200.
|
||||
|
||||
- JpCompressedMsi
|
||||
Value of `Compressed` attribute of `Package` WiX element. Default value is `yes`.
|
||||
|
||||
- JpAllowDowngrades
|
||||
Should be defined to enable downgrades and undefined to disable downgrades.
|
||||
Default value is `yes`.
|
||||
|
||||
- JpAllowUpgrades
|
||||
Should be defined to enable upgrades and undefined to disable upgrades.
|
||||
Default value is `yes`.
|
||||
-->
|
||||
|
||||
<!-- Non-opening ProgID settings-->
|
||||
<?define IconFileC9rC9s= "Cryptomator-Vault.ico" ?>
|
||||
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
|
||||
|
||||
<!-- Close Application util -->
|
||||
<?define CloseApplicationTarget= "cryptomator.exe" ?>
|
||||
<Include/>
|
||||
7
license/merges
Normal file
7
license/merges
Normal file
@@ -0,0 +1,7 @@
|
||||
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
|
||||
MIT License|The MIT License (MIT)|The MIT License|MIT license
|
||||
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
|
||||
GPLv2|GNU General Public License Version 2
|
||||
GPLv2+CE|CDDL + GPLv2 with classpath exception
|
||||
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
|
||||
Eclipse Public License - Version 2.0|Eclipse Public License - v 2.0
|
||||
120
pom.xml
120
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptomator</artifactId>
|
||||
<version>1.7.0-SNAPSHOT</version>
|
||||
<version>1.6.13</version>
|
||||
<name>Cryptomator Desktop App</name>
|
||||
|
||||
<organization>
|
||||
@@ -27,39 +27,48 @@
|
||||
<nonModularGroupIds>com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.3.1</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.1.0-beta1</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version>
|
||||
<cryptomator.cryptolib.version>2.1.0-beta3</cryptomator.cryptolib.version>
|
||||
<cryptomator.cryptofs.version>2.4.2</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.1.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.1.2</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.1.2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.1.0</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>1.3.4</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>1.2.6</cryptomator.webdav.version>
|
||||
<cryptomator.webdav.version>1.2.8</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<javafx.version>17.0.2</javafx.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<jwt.version>3.18.3</jwt.version>
|
||||
<dagger.version>2.41</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<guava.version>31.0-jre</guava.version>
|
||||
<dagger.version>2.40.3</dagger.version>
|
||||
<gson.version>2.8.9</gson.version>
|
||||
<zxcvbn.version>1.5.2</zxcvbn.version>
|
||||
<slf4j.version>1.7.32</slf4j.version>
|
||||
<logback.version>1.2.9</logback.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
<gson.version>2.9.0</gson.version>
|
||||
<javafx.version>18.0.1</javafx.version>
|
||||
<jwt.version>4.0.0</jwt.version>
|
||||
<nimbus-jose.version>9.23</nimbus-jose.version>
|
||||
<logback.version>1.2.11</logback.version>
|
||||
<slf4j.version>1.7.36</slf4j.version>
|
||||
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.8.1</junit.jupiter.version>
|
||||
<mockito.version>3.12.4</mockito.version>
|
||||
<mockito.version>4.4.0</mockito.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
|
||||
<!-- build plugin dependencies -->
|
||||
<dependency-check.version>7.0.0</dependency-check.version>
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>7.1.0</dependency-check.version>
|
||||
<jacoco.version>0.8.7</jacoco.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Cryptomator Libs -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
<version>${cryptomator.cryptolib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptofs</artifactId>
|
||||
@@ -132,12 +141,22 @@
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<!-- OAuth/JWT -->
|
||||
<dependency>
|
||||
<groupId>io.github.coffeelibs</groupId>
|
||||
<artifactId>tiny-oauth2-client</artifactId>
|
||||
<version>${tinyoauth2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<version>${nimbus-jose.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- EasyBind -->
|
||||
<dependency>
|
||||
@@ -224,6 +243,13 @@
|
||||
<version>1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>${jetbrains.annotations.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -290,6 +316,41 @@
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.0.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>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -347,23 +408,24 @@
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
|
||||
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
|
||||
<includedScopes>compile</includedScopes>
|
||||
<excludedGroups>org\.cryptomator</excludedGroups>
|
||||
<licenseMerges>
|
||||
<licenseMerge>Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0</licenseMerge>
|
||||
<licenseMerge>MIT License|The MIT License (MIT)|The MIT License|MIT license</licenseMerge>
|
||||
<licenseMerge>LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1</licenseMerge>
|
||||
<licenseMerge>GPLv2|GNU General Public License Version 2</licenseMerge>
|
||||
<licenseMerge>GPLv2+CE|CDDL + GPLv2 with classpath exception</licenseMerge>
|
||||
</licenseMerges>
|
||||
<fileTemplate>${project.basedir}/src/license/template.ftl</fileTemplate>
|
||||
<licenseMergesUrl>file:///${project.basedir}/license/merges</licenseMergesUrl>
|
||||
<fileTemplate>${project.basedir}/src/main/resources/license/template.ftl</fileTemplate>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<excludes>
|
||||
<exclude>license/*</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
com.github.serceman--jnr-fuse--0.5.4=MIT License
|
||||
@@ -1,40 +1,50 @@
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
module org.cryptomator.desktop {
|
||||
requires static org.jetbrains.annotations;
|
||||
|
||||
requires org.cryptomator.cryptolib;
|
||||
|
||||
requires org.cryptomator.cryptofs;
|
||||
requires org.cryptomator.frontend.dokany;
|
||||
requires org.cryptomator.frontend.fuse;
|
||||
requires org.cryptomator.frontend.webdav;
|
||||
requires org.cryptomator.integrations.api;
|
||||
// jdk:
|
||||
requires java.desktop;
|
||||
requires java.net.http;
|
||||
requires javafx.base;
|
||||
requires javafx.graphics;
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires com.tobiasdiez.easybind;
|
||||
requires jdk.crypto.ec;
|
||||
// 3rd party:
|
||||
requires com.auth0.jwt;
|
||||
requires com.google.common;
|
||||
requires com.google.gson;
|
||||
requires com.nulabinc.zxcvbn;
|
||||
requires com.tobiasdiez.easybind;
|
||||
requires dagger;
|
||||
requires io.github.coffeelibs.tinyoauth2client;
|
||||
requires org.slf4j;
|
||||
requires org.apache.commons.lang3;
|
||||
requires dagger;
|
||||
requires com.auth0.jwt;
|
||||
|
||||
/* TODO: filename-based modules: */
|
||||
requires static javax.inject; /* ugly dagger/guava crap */
|
||||
requires logback.classic;
|
||||
requires logback.core;
|
||||
requires com.nimbusds.jose.jwt;
|
||||
|
||||
uses AutoStartProvider;
|
||||
uses KeychainAccessProvider;
|
||||
uses TrayIntegrationProvider;
|
||||
uses UiAppearanceProvider;
|
||||
exports org.cryptomator.ui.traymenu to org.cryptomator.integrations.api;
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
|
||||
exports org.cryptomator.ui.keyloading.hub to com.fasterxml.jackson.databind;
|
||||
|
||||
opens org.cryptomator.common.settings to com.google.gson;
|
||||
opens org.cryptomator.ui.keyloading.hub to com.google.gson, javafx.fxml;
|
||||
|
||||
opens org.cryptomator.launcher to javafx.graphics;
|
||||
|
||||
opens org.cryptomator.common to javafx.fxml;
|
||||
opens org.cryptomator.common.vaults to javafx.fxml;
|
||||
|
||||
91
src/main/java/org/cryptomator/common/CatchingExecutors.java
Normal file
91
src/main/java/org/cryptomator/common/CatchingExecutors.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.Task;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//Inspired by: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)
|
||||
public final class CatchingExecutors {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CatchingExecutors.class);
|
||||
|
||||
private CatchingExecutors() { /* NO-OP */ }
|
||||
|
||||
public static class CatchingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
|
||||
|
||||
public CatchingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
|
||||
super(corePoolSize, threadFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable runnable, Throwable throwable) {
|
||||
super.afterExecute(runnable, throwable);
|
||||
afterExecuteInternal(runnable, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CatchingThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public CatchingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable runnable, Throwable throwable) {
|
||||
super.afterExecute(runnable, throwable);
|
||||
afterExecuteInternal(runnable, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void afterExecuteInternal(Runnable runnable, Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
callHandler(Thread.currentThread(), throwable);
|
||||
} else if (runnable instanceof Task<?> t) {
|
||||
afterExecuteTask(t);
|
||||
} else if (runnable instanceof Future<?> f) {
|
||||
afterExecuteFuture(f);
|
||||
}
|
||||
//Errors in this method are delegated to the UncaughtExceptionHandler of the current thread
|
||||
}
|
||||
|
||||
private static void callHandler(Thread thread, Throwable throwable) {
|
||||
Objects.requireNonNullElseGet(thread.getUncaughtExceptionHandler(), CatchingExecutors::fallbackHandler).uncaughtException(thread, throwable);
|
||||
}
|
||||
|
||||
private static Thread.UncaughtExceptionHandler fallbackHandler() {
|
||||
return (thread, throwable) -> LOG.error("FALLBACK: Uncaught exception in " + thread.getName(), throwable);
|
||||
}
|
||||
|
||||
private static void afterExecuteTask(Task<?> task) {
|
||||
var caller = Thread.currentThread();
|
||||
Platform.runLater(() -> {
|
||||
if (task.getOnFailed() == null) {
|
||||
callHandler(caller, task.getException());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void afterExecuteFuture(Future<?> future) {
|
||||
try {
|
||||
future.get();
|
||||
} catch (CancellationException ce) {
|
||||
//Ignore
|
||||
} catch (ExecutionException ee) {
|
||||
callHandler(Thread.currentThread(), ee.getCause());
|
||||
} catch (InterruptedException ie) {
|
||||
//Ignore/Reset
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.keychain.KeychainModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.SettingsProvider;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultComponent;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.common.vaults.VaultListModule;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||
@@ -25,16 +23,13 @@ import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -93,7 +88,7 @@ public abstract class CommonsModule {
|
||||
@Singleton
|
||||
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
|
||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
||||
ScheduledExecutorService executorService = new CatchingExecutors.CatchingScheduledThreadPoolExecutor(NUM_SCHEDULER_THREADS, r -> {
|
||||
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
|
||||
Thread t = new Thread(r);
|
||||
t.setName(name);
|
||||
@@ -110,7 +105,7 @@ public abstract class CommonsModule {
|
||||
@Singleton
|
||||
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
|
||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||
ExecutorService executorService = new CatchingExecutors.CatchingThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
|
||||
Thread t = new Thread(r);
|
||||
t.setName(name);
|
||||
|
||||
@@ -6,6 +6,7 @@ public interface Constants {
|
||||
String MASTERKEY_BACKUP_SUFFIX = ".bkup";
|
||||
String VAULTCONFIG_FILENAME = "vault.cryptomator";
|
||||
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
|
||||
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
||||
byte[] PEPPER = new byte[0];
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,17 @@ public class Environment {
|
||||
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
|
||||
private static final char PATH_LIST_SEP = ':';
|
||||
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||
|
||||
@Inject
|
||||
public Environment() {
|
||||
@@ -32,17 +43,17 @@ public class Environment {
|
||||
LOG.debug("user.language: {}", System.getProperty("user.language"));
|
||||
LOG.debug("user.region: {}", System.getProperty("user.region"));
|
||||
LOG.debug("logback.configurationFile: {}", System.getProperty("logback.configurationFile"));
|
||||
LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath"));
|
||||
LOG.debug("cryptomator.ipcSocketPath: {}", System.getProperty("cryptomator.ipcSocketPath"));
|
||||
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
|
||||
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
|
||||
LOG.debug("cryptomator.pluginDir: {}", System.getProperty("cryptomator.pluginDir"));
|
||||
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
|
||||
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
|
||||
LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
|
||||
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
|
||||
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
|
||||
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
|
||||
LOG.debug("{}: {}", SETTINGS_PATH_PROP_NAME, System.getProperty(SETTINGS_PATH_PROP_NAME));
|
||||
LOG.debug("{}: {}", IPC_SOCKET_PATH_PROP_NAME, System.getProperty(IPC_SOCKET_PATH_PROP_NAME));
|
||||
LOG.debug("{}: {}", KEYCHAIN_PATHS_PROP_NAME, System.getProperty(KEYCHAIN_PATHS_PROP_NAME));
|
||||
LOG.debug("{}: {}", LOG_DIR_PROP_NAME, System.getProperty(LOG_DIR_PROP_NAME));
|
||||
LOG.debug("{}: {}", PLUGIN_DIR_PROP_NAME, System.getProperty(PLUGIN_DIR_PROP_NAME));
|
||||
LOG.debug("{}: {}", MOUNTPOINT_DIR_PROP_NAME, System.getProperty(MOUNTPOINT_DIR_PROP_NAME));
|
||||
LOG.debug("{}: {}", MIN_PW_LENGTH_PROP_NAME, System.getProperty(MIN_PW_LENGTH_PROP_NAME));
|
||||
LOG.debug("{}: {}", APP_VERSION_PROP_NAME, System.getProperty(APP_VERSION_PROP_NAME));
|
||||
LOG.debug("{}: {}", BUILD_NUMBER_PROP_NAME, System.getProperty(BUILD_NUMBER_PROP_NAME));
|
||||
LOG.debug("{}: {}", TRAY_ICON_PROP_NAME, System.getProperty(TRAY_ICON_PROP_NAME));
|
||||
LOG.debug("{}: {}", P12_PATH_PROP_NAME, System.getProperty(P12_PATH_PROP_NAME));
|
||||
}
|
||||
|
||||
public boolean useCustomLogbackConfig() {
|
||||
@@ -50,48 +61,47 @@ public class Environment {
|
||||
}
|
||||
|
||||
public Stream<Path> getSettingsPath() {
|
||||
return getPaths("cryptomator.settingsPath");
|
||||
return getPaths(SETTINGS_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> getP12Path() {
|
||||
return getPaths(P12_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> ipcSocketPath() {
|
||||
return getPaths("cryptomator.ipcSocketPath");
|
||||
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
||||
}
|
||||
|
||||
public Stream<Path> getKeychainPath() {
|
||||
return getPaths("cryptomator.keychainPath");
|
||||
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
||||
}
|
||||
|
||||
public Optional<Path> getLogDir() {
|
||||
return getPath("cryptomator.logDir").map(this::replaceHomeDir);
|
||||
return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
|
||||
}
|
||||
|
||||
public Optional<Path> getPluginDir() {
|
||||
return getPath("cryptomator.pluginDir").map(this::replaceHomeDir);
|
||||
return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
|
||||
}
|
||||
|
||||
public Optional<Path> getMountPointsDir() {
|
||||
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
|
||||
return getPath(MOUNTPOINT_DIR_PROP_NAME).map(this::replaceHomeDir);
|
||||
}
|
||||
|
||||
public Optional<String> getAppVersion() {
|
||||
return Optional.ofNullable(System.getProperty("cryptomator.appVersion"));
|
||||
return Optional.ofNullable(System.getProperty(APP_VERSION_PROP_NAME));
|
||||
}
|
||||
|
||||
public Optional<String> getBuildNumber() {
|
||||
return Optional.ofNullable(System.getProperty("cryptomator.buildNumber"));
|
||||
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
|
||||
}
|
||||
|
||||
public int getMinPwLength() {
|
||||
return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
|
||||
return getInt(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
|
||||
}
|
||||
|
||||
public boolean showTrayIcon() {
|
||||
return Boolean.getBoolean("cryptomator.showTrayIcon");
|
||||
}
|
||||
|
||||
@Deprecated // TODO: remove as soon as custom mount path works properly on Win+Fuse
|
||||
public boolean useExperimentalFuse() {
|
||||
return Boolean.getBoolean("fuse.experimental");
|
||||
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
|
||||
}
|
||||
|
||||
private int getInt(String propertyName, int defaultValue) {
|
||||
|
||||
64
src/main/java/org/cryptomator/common/LocationPreset.java
Normal file
64
src/main/java/org/cryptomator/common/LocationPreset.java
Normal file
@@ -0,0 +1,64 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
108
src/main/java/org/cryptomator/common/Passphrase.java
Normal file
108
src/main/java/org/cryptomator/common/Passphrase.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import javax.security.auth.Destroyable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A destroyable CharSequence.
|
||||
*/
|
||||
public class Passphrase implements Destroyable, CharSequence {
|
||||
|
||||
private final char[] data;
|
||||
private final int offset;
|
||||
private final int length;
|
||||
private boolean destroyed;
|
||||
|
||||
/**
|
||||
* Wraps (doesn't copy) the given data.
|
||||
*
|
||||
* @param data The wrapped data. Any changes to this will be reflected in this passphrase
|
||||
*/
|
||||
public Passphrase(char[] data) {
|
||||
this(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps (doesn't copy) a subarray of the given data.
|
||||
*
|
||||
* @param data The wrapped data. Any changes to this will be reflected in this passphrase
|
||||
* @param offset The subarray offset, i.e. the first character of this passphrase
|
||||
* @param length The subarray length, i.e. the length of this passphrase
|
||||
*/
|
||||
public Passphrase(char[] data, int offset, int length) {
|
||||
if (offset < 0 || length < 0 || offset + length > data.length) {
|
||||
throw new IndexOutOfBoundsException("[%1$d %1$d + %2$d[ not within [0, %3$d[".formatted(offset, length, data.length));
|
||||
}
|
||||
this.data = data;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public static Passphrase copyOf(CharSequence cs) {
|
||||
char[] result = new char[cs.length()];
|
||||
for (int i = 0; i < cs.length(); i++) {
|
||||
result[i] = cs.charAt(i);
|
||||
}
|
||||
return new Passphrase(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Passphrase that = (Passphrase) o;
|
||||
// time-constant comparison
|
||||
int diff = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
diff |= charAt(i) ^ that.charAt(i);
|
||||
}
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// basically Arrays.hashCode, but only for a certain subarray
|
||||
int result = 1;
|
||||
for (int i = 0; i < length; i++) {
|
||||
result = 31 * result + charAt(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(data, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
if (index < 0 || index >= length) {
|
||||
throw new IndexOutOfBoundsException("%d not within [0, %d[".formatted(index, length));
|
||||
}
|
||||
return data[offset + index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Passphrase subSequence(int start, int end) {
|
||||
if (start < 0 || end < 0 || end > length || start > end) {
|
||||
throw new IndexOutOfBoundsException("[%d, %d[ not within [0, %d[".formatted(start, end, length));
|
||||
}
|
||||
return new Passphrase(Arrays.copyOfRange(data, offset + start, offset + end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return destroyed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
Arrays.fill(data, offset, offset + length, '\0');
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class PluginClassLoader extends URLClassLoader {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
|
||||
private static final String NAME = "PluginClassLoader";
|
||||
private static final String JAR_SUFFIX = ".jar";
|
||||
|
||||
@Inject
|
||||
public PluginClassLoader(Environment env) {
|
||||
super(NAME, env.getPluginDir().map(PluginClassLoader::findJars).orElse(new URL[0]), PluginClassLoader.class.getClassLoader());
|
||||
}
|
||||
|
||||
private static URL[] findJars(Path path) {
|
||||
if (!Files.isDirectory(path)) {
|
||||
return new URL[0];
|
||||
} else {
|
||||
try {
|
||||
var visitor = new JarVisitor();
|
||||
Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
|
||||
return visitor.urls.toArray(URL[]::new);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to scan plugin dir " + path, e);
|
||||
return new URL[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class JarVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
private final List<URL> urls = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
if (attrs.isRegularFile() && file.getFileName().toString().toLowerCase().endsWith(JAR_SUFFIX)) {
|
||||
try {
|
||||
urls.add(file.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
LOG.warn("Failed to create URL for jar file {}", file);
|
||||
}
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,12 +43,6 @@ public class KeychainManager implements KeychainAccessProvider {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
||||
getKeychainOrFail().storePassphrase(key, passphrase);
|
||||
setPassphraseStored(key, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
|
||||
@@ -68,14 +62,6 @@ public class KeychainManager implements KeychainAccessProvider {
|
||||
setPassphraseStored(key, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
||||
if (isPassphraseStored(key)) {
|
||||
getKeychainOrFail().changePassphrase(key, passphrase);
|
||||
setPassphraseStored(key, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||
if (isPassphraseStored(key)) {
|
||||
|
||||
@@ -2,42 +2,30 @@ package org.cryptomator.common.keychain;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.common.PluginClassLoader;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
|
||||
@Module
|
||||
public class KeychainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
|
||||
return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
|
||||
static List<KeychainAccessProvider> provideSupportedKeychainAccessProviders() {
|
||||
return KeychainAccessProvider.get().toList();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) {
|
||||
return availableFactories.stream() //
|
||||
.map(ServiceLoader.Provider::get) //
|
||||
.filter(KeychainAccessProvider::isSupported) //
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
|
||||
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
|
||||
return Bindings.createObjectBinding(() -> {
|
||||
var selectedProviderClass = settings.keychainProvider().get();
|
||||
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
|
||||
var fallbackProvider = providers.stream().findAny().orElse(null);
|
||||
var fallbackProvider = providers.stream().findFirst().orElse(null);
|
||||
return selectedProvider.orElse(fallbackProvider);
|
||||
}, settings.keychainProvider());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Optional;
|
||||
|
||||
class CustomMountPointChooser implements MountPointChooser {
|
||||
@@ -29,17 +30,15 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
|
||||
|
||||
private final VaultSettings vaultSettings;
|
||||
private final Environment environment;
|
||||
|
||||
@Inject
|
||||
public CustomMountPointChooser(VaultSettings vaultSettings, Environment environment) {
|
||||
public CustomMountPointChooser(VaultSettings vaultSettings) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Volume caller) {
|
||||
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse();
|
||||
return caller.getImplementationType() != VolumeImpl.WEBDAV;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
104
src/main/java/org/cryptomator/common/settings/DeviceKey.java
Normal file
104
src/main/java/org/cryptomator/common/settings/DeviceKey.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package org.cryptomator.common.settings;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.cryptolib.common.P384KeyPair;
|
||||
import org.cryptomator.cryptolib.common.Pkcs12Exception;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Singleton
|
||||
public class DeviceKey {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DeviceKey.class);
|
||||
private static final String KEYCHAIN_KEY = "cryptomator-device-p12";
|
||||
|
||||
private final KeychainManager keychainManager;
|
||||
private final Environment env;
|
||||
private final SecureRandom csprng;
|
||||
private final Supplier<P384KeyPair> keyPairSupplier;
|
||||
|
||||
@Inject
|
||||
public DeviceKey(KeychainManager keychainManager, Environment env, SecureRandom csprng) {
|
||||
this.keychainManager = keychainManager;
|
||||
this.env = env;
|
||||
this.csprng = csprng;
|
||||
this.keyPairSupplier = Suppliers.memoize(this::loadOrCreate);
|
||||
}
|
||||
|
||||
public P384KeyPair get() throws DeviceKeyRetrievalException {
|
||||
Preconditions.checkState(keychainManager.isSupported());
|
||||
return keyPairSupplier.get();
|
||||
}
|
||||
|
||||
private P384KeyPair loadOrCreate() throws DeviceKeyRetrievalException {
|
||||
char[] passphrase = null;
|
||||
try {
|
||||
passphrase = keychainManager.loadPassphrase(KEYCHAIN_KEY);
|
||||
if (passphrase != null) {
|
||||
return loadExistingKeyPair(passphrase);
|
||||
} else {
|
||||
passphrase = randomPassword();
|
||||
keychainManager.storePassphrase(KEYCHAIN_KEY, CharBuffer.wrap(passphrase));
|
||||
return createAndStoreNewKeyPair(passphrase);
|
||||
}
|
||||
} catch (KeychainAccessException e) {
|
||||
throw new DeviceKeyRetrievalException("Failed to access system keychain", e);
|
||||
} catch (Pkcs12Exception | IOException e) {
|
||||
throw new DeviceKeyRetrievalException("Failed to access .p12 file", e);
|
||||
} finally {
|
||||
if (passphrase != null) {
|
||||
Arrays.fill(passphrase, '\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private P384KeyPair loadExistingKeyPair(char[] passphrase) throws IOException {
|
||||
var p12File = env.getP12Path() //
|
||||
.filter(Files::isRegularFile) //
|
||||
.findFirst() //
|
||||
.orElseThrow(() -> new DeviceKeyRetrievalException("Missing .p12 file"));
|
||||
LOG.debug("Loading existing device key from {}", p12File);
|
||||
return P384KeyPair.load(p12File, passphrase);
|
||||
}
|
||||
|
||||
private P384KeyPair createAndStoreNewKeyPair(char[] passphrase) throws IOException {
|
||||
var p12File = env.getP12Path() //
|
||||
.findFirst() //
|
||||
.orElseThrow(() -> new DeviceKeyRetrievalException("No path for .p12 file configured"));
|
||||
var keyPair = P384KeyPair.generate();
|
||||
LOG.debug("Store new device key to {}", p12File);
|
||||
keyPair.store(p12File, passphrase);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
private char[] randomPassword() {
|
||||
// this is a fast & easy attempt to create a random string:
|
||||
var uuid = new UUID(csprng.nextLong(), csprng.nextLong());
|
||||
return uuid.toString().toCharArray();
|
||||
}
|
||||
|
||||
public static class DeviceKeyRetrievalException extends RuntimeException {
|
||||
private DeviceKeyRetrievalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
private DeviceKeyRetrievalException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,11 +32,12 @@ public class Settings {
|
||||
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
|
||||
public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
|
||||
public static final boolean DEFAULT_START_HIDDEN = false;
|
||||
public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
|
||||
public static final int DEFAULT_PORT = 42427;
|
||||
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
|
||||
public static final boolean DEFAULT_DEBUG_MODE = false;
|
||||
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
|
||||
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = VolumeImpl.FUSE;
|
||||
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
||||
@Deprecated // to be changed to "whatever is available" eventually
|
||||
public static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
|
||||
@@ -44,12 +45,14 @@ public class Settings {
|
||||
public static final String DEFAULT_LICENSE_KEY = "";
|
||||
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
|
||||
public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
|
||||
public static final String DEFAULT_LANGUAGE = null;
|
||||
|
||||
|
||||
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
|
||||
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
|
||||
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
|
||||
private final BooleanProperty autoCloseVaults = new SimpleBooleanProperty(DEFAULT_AUTO_CLOSE_VAULTS);
|
||||
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
|
||||
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
|
||||
private final ObjectProperty<WebDavUrlScheme> preferredGvfsScheme = new SimpleObjectProperty<>(DEFAULT_GVFS_SCHEME);
|
||||
@@ -66,6 +69,7 @@ public class Settings {
|
||||
private final IntegerProperty windowWidth = new SimpleIntegerProperty();
|
||||
private final IntegerProperty windowHeight = new SimpleIntegerProperty();
|
||||
private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
|
||||
private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
|
||||
|
||||
|
||||
private Consumer<Settings> saveCmd;
|
||||
@@ -80,6 +84,7 @@ public class Settings {
|
||||
askedForUpdateCheck.addListener(this::somethingChanged);
|
||||
checkForUpdates.addListener(this::somethingChanged);
|
||||
startHidden.addListener(this::somethingChanged);
|
||||
autoCloseVaults.addListener(this::somethingChanged);
|
||||
port.addListener(this::somethingChanged);
|
||||
numTrayNotifications.addListener(this::somethingChanged);
|
||||
preferredGvfsScheme.addListener(this::somethingChanged);
|
||||
@@ -96,6 +101,7 @@ public class Settings {
|
||||
windowWidth.addListener(this::somethingChanged);
|
||||
windowHeight.addListener(this::somethingChanged);
|
||||
displayConfiguration.addListener(this::somethingChanged);
|
||||
language.addListener(this::somethingChanged);
|
||||
}
|
||||
|
||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||
@@ -130,6 +136,10 @@ public class Settings {
|
||||
return startHidden;
|
||||
}
|
||||
|
||||
public BooleanProperty autoCloseVaults() {
|
||||
return autoCloseVaults;
|
||||
}
|
||||
|
||||
public IntegerProperty port() {
|
||||
return port;
|
||||
}
|
||||
@@ -191,4 +201,8 @@ public class Settings {
|
||||
public ObjectProperty<String> displayConfigurationProperty() {
|
||||
return displayConfiguration;
|
||||
}
|
||||
|
||||
public StringProperty languageProperty() {
|
||||
return language;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
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("port").value(value.port().get());
|
||||
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
||||
out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get().name());
|
||||
@@ -57,6 +58,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
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.endObject();
|
||||
}
|
||||
@@ -81,6 +83,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
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 "port" -> settings.port().set(in.nextInt());
|
||||
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
|
||||
case "preferredGvfsScheme" -> settings.preferredGvfsScheme().set(parseWebDavUrlSchemePrefix(in.nextString()));
|
||||
@@ -97,6 +100,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
|
||||
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
|
||||
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
|
||||
case "language" -> settings.languageProperty().set(in.nextString());
|
||||
|
||||
default -> {
|
||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||
|
||||
@@ -49,14 +49,12 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
|
||||
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
||||
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
|
||||
private final SettingsJsonAdapter settingsJsonAdapter;
|
||||
private final Environment env;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Gson gson;
|
||||
|
||||
@Inject
|
||||
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
|
||||
this.settingsJsonAdapter = settingsJsonAdapter;
|
||||
this.env = env;
|
||||
this.scheduler = scheduler;
|
||||
this.gson = new GsonBuilder() //
|
||||
@@ -118,7 +116,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
try {
|
||||
Files.createDirectories(settingsPath.getParent());
|
||||
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
|
||||
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE_NEW); //
|
||||
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
|
||||
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
||||
gson.toJson(settings, writer);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package org.cryptomator.common.settings;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
public enum UiTheme {
|
||||
LIGHT("preferences.general.theme.light"), //
|
||||
DARK("preferences.general.theme.dark"), //
|
||||
AUTOMATIC("preferences.general.theme.automatic");
|
||||
LIGHT("preferences.interface.theme.light"), //
|
||||
DARK("preferences.interface.theme.dark"), //
|
||||
AUTOMATIC("preferences.interface.theme.automatic");
|
||||
|
||||
public static UiTheme[] applicableValues() {
|
||||
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
|
||||
|
||||
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface DefaultMountFlags {
|
||||
@interface DefaultMountFlags {
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.vaults.Volume.VolumeException;
|
||||
@@ -125,6 +126,7 @@ public class Vault {
|
||||
.withKeyLoader(keyLoader) //
|
||||
.withFlags(flags) //
|
||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
|
||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||
.build();
|
||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||
}
|
||||
|
||||
@@ -19,22 +19,13 @@ public interface VaultComponent {
|
||||
|
||||
Vault vault();
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
@BindsInstance
|
||||
Builder vaultSettings(VaultSettings vaultSettings);
|
||||
VaultComponent create(@BindsInstance VaultSettings vaultSettings, //
|
||||
@BindsInstance VaultConfigCache configCache, //
|
||||
@BindsInstance VaultState.Value vaultState, //
|
||||
@BindsInstance @Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
@BindsInstance
|
||||
Builder vaultConfigCache(VaultConfigCache configCache);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialVaultState(VaultState.Value vaultState);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
VaultComponent build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -38,15 +38,15 @@ public class VaultListManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
||||
|
||||
private final AutoLocker autoLocker;
|
||||
private final VaultComponent.Builder vaultComponentBuilder;
|
||||
private final VaultComponent.Factory vaultComponentFactory;
|
||||
private final ObservableList<Vault> vaultList;
|
||||
private final String defaultVaultName;
|
||||
|
||||
@Inject
|
||||
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
|
||||
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Factory vaultComponentFactory, ResourceBundle resourceBundle, Settings settings) {
|
||||
this.vaultList = vaultList;
|
||||
this.autoLocker = autoLocker;
|
||||
this.vaultComponentBuilder = vaultComponentBuilder;
|
||||
this.vaultComponentFactory = vaultComponentFactory;
|
||||
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
||||
|
||||
addAll(settings.getDirectories());
|
||||
@@ -93,21 +93,17 @@ public class VaultListManager {
|
||||
}
|
||||
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
|
||||
var wrapper = new VaultConfigCache(vaultSettings);
|
||||
try {
|
||||
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
|
||||
VaultConfigCache wrapper = new VaultConfigCache(vaultSettings);
|
||||
compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config
|
||||
var vaultState = determineVaultState(vaultSettings.path().get());
|
||||
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||
wrapper.reloadConfig();
|
||||
}
|
||||
compBuilder.initialVaultState(vaultState);
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
|
||||
compBuilder.initialVaultState(ERROR);
|
||||
compBuilder.initialErrorCause(e);
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
|
||||
}
|
||||
return compBuilder.build().vault();
|
||||
}
|
||||
|
||||
public static VaultState.Value redetermineVaultState(Vault vault) {
|
||||
|
||||
@@ -83,7 +83,7 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
||||
if (success) {
|
||||
fireValueChangedEvent();
|
||||
} else {
|
||||
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
|
||||
LOG.debug("Failed transiting into state {}: Expected state was not {}.", fromState, toState);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
13
src/main/java/org/cryptomator/launcher/AppLaunchEvent.java
Normal file
13
src/main/java/org/cryptomator/launcher/AppLaunchEvent.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection<Path> pathsToOpen) {
|
||||
|
||||
public enum EventType {
|
||||
REVEAL_APP,
|
||||
OPEN_FILE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,47 +13,60 @@ import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.ipc.IpcCommunicator;
|
||||
import org.cryptomator.logging.DebugMode;
|
||||
import org.cryptomator.logging.LoggerConfiguration;
|
||||
import org.cryptomator.ui.launcher.UiLauncher;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Singleton
|
||||
public class Cryptomator {
|
||||
|
||||
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||
// DaggerCryptomatorComponent gets generated by Dagger.
|
||||
// Run Maven and include target/generated-sources/annotations in your IDE.
|
||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
|
||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
|
||||
private final LoggerConfiguration logConfig;
|
||||
private final DebugMode debugMode;
|
||||
private final SupportedLanguages supportedLanguages;
|
||||
private final Environment env;
|
||||
private final Lazy<IpcMessageHandler> ipcMessageHandler;
|
||||
private final CountDownLatch shutdownLatch;
|
||||
private final ShutdownHook shutdownHook;
|
||||
private final Lazy<UiLauncher> uiLauncher;
|
||||
|
||||
@Inject
|
||||
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
|
||||
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
|
||||
this.logConfig = logConfig;
|
||||
this.debugMode = debugMode;
|
||||
this.supportedLanguages = supportedLanguages;
|
||||
this.env = env;
|
||||
this.ipcMessageHandler = ipcMessageHandler;
|
||||
this.shutdownLatch = shutdownLatch;
|
||||
this.shutdownHook = shutdownHook;
|
||||
this.uiLauncher = uiLauncher;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var printVersion = Optional.ofNullable(args) //
|
||||
.stream() //Streams either one element (the args-array) or zero elements
|
||||
.flatMap(Arrays::stream) //
|
||||
.anyMatch(arg -> "-v".equals(arg) || "--version".equals(arg));
|
||||
|
||||
if (printVersion) {
|
||||
var appVer = System.getProperty("cryptomator.appVersion", "SNAPSHOT");
|
||||
var buildNumber = System.getProperty("cryptomator.buildNumber", "SNAPSHOT");
|
||||
|
||||
//Reduce noise for parsers by using System.out directly
|
||||
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
||||
LOG.info("Exit {}", exitCode);
|
||||
System.exit(exitCode); // end remaining non-daemon threads.
|
||||
@@ -67,8 +80,10 @@ public class Cryptomator {
|
||||
*/
|
||||
private int run(String[] args) {
|
||||
logConfig.init();
|
||||
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||
debugMode.initialize();
|
||||
supportedLanguages.applyPreferred();
|
||||
|
||||
/*
|
||||
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
||||
@@ -79,7 +94,7 @@ public class Cryptomator {
|
||||
communicator.sendHandleLaunchargs(List.of(args));
|
||||
communicator.sendRevealRunningApp();
|
||||
LOG.info("Found running application instance. Shutting down...");
|
||||
return 2;
|
||||
return 0;
|
||||
} else {
|
||||
shutdownHook.runOnShutdown(communicator::closeUnchecked);
|
||||
var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build());
|
||||
@@ -96,21 +111,38 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the JavaFX application and waits until shutdown is requested.
|
||||
* Launches the JavaFX application, blocking the main thread until shuts down.
|
||||
*
|
||||
* @return Nonzero exit code in case of an error.
|
||||
* @implNote This method blocks until {@link #shutdownLatch} reached zero.
|
||||
*/
|
||||
private int runGuiApplication() {
|
||||
try {
|
||||
uiLauncher.get().launch();
|
||||
shutdownLatch.await();
|
||||
Application.launch(MainApp.class);
|
||||
LOG.info("UI shut down");
|
||||
return 0;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Terminating due to error", e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainApp extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
LOG.info("JavaFX runtime started after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
|
||||
.fxApplication(this) //
|
||||
.primaryStage(primaryStage) //
|
||||
.build();
|
||||
component.application().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.info("JavaFX application stopped.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
import org.cryptomator.common.CommonsModule;
|
||||
import org.cryptomator.logging.LoggerModule;
|
||||
import org.cryptomator.ui.launcher.UiLauncherModule;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class})
|
||||
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
|
||||
public interface CryptomatorComponent {
|
||||
|
||||
Cryptomator application();
|
||||
|
||||
FxApplicationComponent.Builder fxAppComponentBuilder();
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
CryptomatorComponent create(@BindsInstance @Named("startupTime") long startupTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,20 +2,50 @@ package org.cryptomator.launcher;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
@Module
|
||||
@Module(subcomponents = {FxApplicationComponent.class})
|
||||
class CryptomatorModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("shutdownLatch")
|
||||
static CountDownLatch provideShutdownLatch() {
|
||||
return new CountDownLatch(1);
|
||||
static ResourceBundle provideLocalization() {
|
||||
return ResourceBundle.getBundle("i18n.strings");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("launchEventQueue")
|
||||
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
||||
return new ArrayBlockingQueue<>(10);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||
return UiAppearanceProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<AutoStartProvider> provideAutostartProvider() {
|
||||
return AutoStartProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
|
||||
return TrayIntegrationProvider.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.ui.launcher.AppLaunchEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
class FileOpenRequestHandler {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.ipc.IpcMessageListener;
|
||||
import org.cryptomator.ui.launcher.AppLaunchEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Singleton
|
||||
public class SupportedLanguages {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
|
||||
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
|
||||
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", //
|
||||
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", //
|
||||
"sr-Latn", "sv", "sw", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW");
|
||||
|
||||
@Nullable
|
||||
private final String preferredLanguage;
|
||||
|
||||
@Inject
|
||||
public SupportedLanguages(Settings settings) {
|
||||
this.preferredLanguage = settings.languageProperty().get();
|
||||
}
|
||||
|
||||
public void applyPreferred() {
|
||||
if (preferredLanguage == null) {
|
||||
LOG.debug("Using system locale");
|
||||
return;
|
||||
}
|
||||
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
|
||||
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
|
||||
Locale.setDefault(preferredLocale);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ 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.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
|
||||
|
||||
import javax.inject.Named;
|
||||
@@ -43,12 +43,12 @@ public abstract class AddVaultModule {
|
||||
@Provides
|
||||
@AddVaultWizardWindow
|
||||
@AddVaultWizardScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.initOwner(primaryStage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,25 +2,24 @@ package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class AddVaultSuccessController implements FxController {
|
||||
|
||||
private final FxApplication fxApplication;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Stage window;
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
|
||||
@Inject
|
||||
AddVaultSuccessController(FxApplication fxApplication, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
|
||||
this.fxApplication = fxApplication;
|
||||
AddVaultSuccessController(FxApplicationWindows appWindows, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
|
||||
this.appWindows = appWindows;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
}
|
||||
@@ -28,7 +27,7 @@ public class AddVaultSuccessController implements FxController {
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
fxApplication.startUnlockWorkflow(vault.get(), Optional.of(window));
|
||||
appWindows.startUnlockWorkflow(vault.get(), window);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -2,12 +2,14 @@ package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -20,12 +22,11 @@ import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class ChooseExistingVaultController implements FxController {
|
||||
|
||||
@@ -34,30 +35,32 @@ public class ChooseExistingVaultController implements FxController {
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> welcomeScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final ObjectProperty<Vault> vault;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Settings settings;
|
||||
|
||||
private Image screenshot;
|
||||
|
||||
@Inject
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, Settings settings) {
|
||||
this.window = window;
|
||||
this.welcomeScene = welcomeScene;
|
||||
this.successScene = successScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vault = vault;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac.png").toString());
|
||||
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac"+(UiTheme.LIGHT == settings.theme().get()? "":"-dark")+".png").toString());
|
||||
} else {
|
||||
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString());
|
||||
}
|
||||
@@ -72,7 +75,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
public void chooseFileAndNext() {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Vault", "*.cryptomator"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
|
||||
File masterkeyFile = fileChooser.showOpenDialog(window);
|
||||
if (masterkeyFile != null) {
|
||||
vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent());
|
||||
@@ -82,7 +85,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
window.setScene(successScene.get());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to open existing vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> chooseNameScene;
|
||||
private final Lazy<Scene> choosePasswordScene;
|
||||
private final LocationPresets locationPresets;
|
||||
private final ObservedLocationPresets locationPresets;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final StringProperty vaultName;
|
||||
private final ResourceBundle resourceBundle;
|
||||
@@ -71,7 +71,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
public FontAwesome5IconView badLocation;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, LocationPresets 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, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.chooseNameScene = chooseNameScene;
|
||||
this.choosePasswordScene = choosePasswordScene;
|
||||
@@ -197,7 +197,7 @@ public class CreateNewVaultLocationController implements FxController {
|
||||
return validVaultPath.get();
|
||||
}
|
||||
|
||||
public LocationPresets getLocationPresets() {
|
||||
public ObservedLocationPresets getObservedLocationPresets() {
|
||||
return locationPresets;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import javax.inject.Named;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -33,8 +32,6 @@ public class CreateNewVaultNameController implements FxController {
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final StringProperty vaultName;
|
||||
private final BooleanBinding validVaultName;
|
||||
private final BooleanBinding invalidVaultName;
|
||||
private final StringBinding warningText;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||
@@ -43,9 +40,7 @@ public class CreateNewVaultNameController implements FxController {
|
||||
this.chooseLocationScene = chooseLocationScene;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vaultName = vaultName;
|
||||
this.validVaultName = Bindings.createBooleanBinding(this::isValidVaultName, vaultName);
|
||||
this.invalidVaultName = validVaultName.not();
|
||||
this.warningText = Bindings.when(vaultName.isNotEmpty().and(invalidVaultName)).then(resourceBundle.getString("addvaultwizard.new.invalidName")).otherwise((String) null);
|
||||
this.validVaultName = Bindings.createBooleanBinding(this::isValidVaultNameInternal, vaultName);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -54,7 +49,7 @@ public class CreateNewVaultNameController implements FxController {
|
||||
vaultName.addListener(this::vaultNameChanged);
|
||||
}
|
||||
|
||||
public boolean isValidVaultName() {
|
||||
private boolean isValidVaultNameInternal() {
|
||||
return vaultName.get() != null && VALID_NAME_PATTERN.matcher(vaultName.get().trim()).matches();
|
||||
}
|
||||
|
||||
@@ -79,28 +74,12 @@ public class CreateNewVaultNameController implements FxController {
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public BooleanBinding invalidVaultNameProperty() {
|
||||
return invalidVaultName;
|
||||
public BooleanBinding validVaultNameProperty() {
|
||||
return validVaultName;
|
||||
}
|
||||
|
||||
public boolean isInvalidVaultName() {
|
||||
return invalidVaultName.get();
|
||||
}
|
||||
|
||||
public StringBinding warningTextProperty() {
|
||||
return warningText;
|
||||
}
|
||||
|
||||
public String getWarningText() {
|
||||
return warningText.get();
|
||||
}
|
||||
|
||||
public BooleanBinding showWarningProperty() {
|
||||
return warningText.isNotEmpty();
|
||||
}
|
||||
|
||||
public boolean isShowWarning() {
|
||||
return showWarningProperty().get();
|
||||
public boolean isValidVaultName() {
|
||||
return validVaultName.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.slf4j.Logger;
|
||||
@@ -60,7 +60,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
private final Lazy<Scene> chooseLocationScene;
|
||||
private final Lazy<Scene> recoveryKeyScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final ExecutorService executor;
|
||||
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||
private final StringProperty vaultNameProperty;
|
||||
@@ -83,12 +83,12 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
public NewPasswordController newPasswordSceneController;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.chooseLocationScene = chooseLocationScene;
|
||||
this.recoveryKeyScene = recoveryKeyScene;
|
||||
this.successScene = successScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.executor = executor;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
this.vaultNameProperty = vaultName;
|
||||
@@ -121,16 +121,6 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void next() {
|
||||
Path pathToVault = vaultPathProperty.get();
|
||||
|
||||
try {
|
||||
Files.createDirectory(pathToVault);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to create vault directory.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
return;
|
||||
}
|
||||
|
||||
if (showRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
|
||||
showRecoveryKeyScene();
|
||||
} else if (skipRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
|
||||
@@ -144,15 +134,15 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Path pathToVault = vaultPathProperty.get();
|
||||
processing.set(true);
|
||||
Tasks.create(() -> {
|
||||
initializeVault(pathToVault);
|
||||
createVault(pathToVault);
|
||||
return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters());
|
||||
}).onSuccess(recoveryKey -> {
|
||||
initializationSucceeded(pathToVault);
|
||||
creationSucceeded(pathToVault);
|
||||
recoveryKeyProperty.set(recoveryKey);
|
||||
window.setScene(recoveryKeyScene.get());
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Failed to initialize vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
LOG.error("Failed to create vault.", e);
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}).andFinally(() -> {
|
||||
processing.set(false);
|
||||
}).runOnce(executor);
|
||||
@@ -162,19 +152,22 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Path pathToVault = vaultPathProperty.get();
|
||||
processing.set(true);
|
||||
Tasks.create(() -> {
|
||||
initializeVault(pathToVault);
|
||||
createVault(pathToVault);
|
||||
}).onSuccess(() -> {
|
||||
initializationSucceeded(pathToVault);
|
||||
creationSucceeded(pathToVault);
|
||||
window.setScene(successScene.get());
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Failed to initialize vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
LOG.error("Failed to create vault.", e);
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}).andFinally(() -> {
|
||||
processing.set(false);
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void initializeVault(Path path) throws IOException {
|
||||
private void createVault(Path path) throws IOException {
|
||||
// 0. create directory
|
||||
Files.createDirectory(path);
|
||||
|
||||
// 1. write masterkey:
|
||||
Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
|
||||
try (Masterkey masterkey = Masterkey.generate(csprng)) {
|
||||
@@ -193,7 +186,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
throw new IOException("Failed initialize vault.", e);
|
||||
throw new IOException("Vault initialization failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +199,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
LOG.info("Created vault at {}", path);
|
||||
}
|
||||
|
||||
private void initializationSucceeded(Path pathToVault) {
|
||||
private void creationSucceeded(Path pathToVault) {
|
||||
try {
|
||||
Vault newVault = vaultListManager.add(pathToVault);
|
||||
vaultProperty.set(newVault);
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
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.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class LocationPresets {
|
||||
|
||||
private static final String USER_HOME = System.getProperty("user.home");
|
||||
private static final String[] ICLOUDDRIVE_LOCATIONS = {"~/Library/Mobile Documents/iCloud~com~setolabs~Cryptomator/Documents", "~/iCloudDrive/iCloud~com~setolabs~Cryptomator"};
|
||||
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
|
||||
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive/My Drive", "~/Google Drive"};
|
||||
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
|
||||
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
|
||||
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
|
||||
public class ObservedLocationPresets {
|
||||
|
||||
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
|
||||
private final ReadOnlyObjectProperty<Path> dropboxLocation;
|
||||
@@ -33,13 +25,13 @@ public class LocationPresets {
|
||||
private final BooleanBinding foundPcloud;
|
||||
|
||||
@Inject
|
||||
public LocationPresets() {
|
||||
this.iclouddriveLocation = new SimpleObjectProperty<>(existingWritablePath(ICLOUDDRIVE_LOCATIONS));
|
||||
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
|
||||
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
|
||||
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS));
|
||||
this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS));
|
||||
this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS));
|
||||
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();
|
||||
@@ -48,24 +40,6 @@ public class LocationPresets {
|
||||
this.foundPcloud = pcloudLocation.isNotNull();
|
||||
}
|
||||
|
||||
private static Path existingWritablePath(String... candidates) {
|
||||
for (String candidate : candidates) {
|
||||
Path path = Paths.get(resolveHomePath(candidate));
|
||||
if (Files.isDirectory(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String resolveHomePath(String path) {
|
||||
if (path.startsWith("~/")) {
|
||||
return USER_HOME + path.substring(1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/* Observables */
|
||||
|
||||
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
|
||||
@@ -8,10 +8,10 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
@@ -38,9 +37,8 @@ public class ChangePasswordController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final KeychainManager keychain;
|
||||
private final SecureRandom csprng;
|
||||
private final MasterkeyFileAccess masterkeyFileAccess;
|
||||
|
||||
public NiceSecurePasswordField oldPasswordField;
|
||||
@@ -49,12 +47,11 @@ public class ChangePasswordController implements FxController {
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, FxApplicationWindows appWindows, KeychainManager keychain, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.keychain = keychain;
|
||||
this.csprng = csprng;
|
||||
this.masterkeyFileAccess = masterkeyFileAccess;
|
||||
}
|
||||
|
||||
@@ -95,7 +92,7 @@ public class ChangePasswordController implements FxController {
|
||||
oldPasswordField.requestFocus();
|
||||
} catch (IOException | CryptoException e) {
|
||||
LOG.error("Password change failed. Unable to perform operation.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.Nullable;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@@ -16,34 +15,17 @@ public interface ErrorComponent {
|
||||
@FxmlScene(FxmlFile.ERROR)
|
||||
Scene scene();
|
||||
|
||||
default void showErrorScene() {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
show();
|
||||
} else {
|
||||
Platform.runLater(this::show);
|
||||
}
|
||||
}
|
||||
|
||||
private void show() {
|
||||
default Stage show() {
|
||||
Stage stage = window();
|
||||
stage.setScene(scene());
|
||||
stage.show();
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder cause(Throwable cause);
|
||||
|
||||
@BindsInstance
|
||||
Builder window(Stage window);
|
||||
|
||||
@BindsInstance
|
||||
Builder returnToScene(@Nullable Scene previousScene);
|
||||
|
||||
ErrorComponent build();
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
ErrorComponent create(@BindsInstance Throwable cause, @BindsInstance Stage window, @BindsInstance @Nullable Scene previousScene);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@ public enum FxmlFile {
|
||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||
HEALTH_START("/fxml/health_start.fxml"), //
|
||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
|
||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"),
|
||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||
@@ -23,9 +29,11 @@ public enum FxmlFile {
|
||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||
PREFERENCES("/fxml/preferences.fxml"), //
|
||||
QUIT("/fxml/quit.fxml"), //
|
||||
QUIT_FORCED("/fxml/quit_forced.fxml"), //
|
||||
RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
|
||||
RECOVERYKEY_RECOVER("/fxml/recoverykey_recover.fxml"), //
|
||||
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
|
||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
||||
@@ -42,7 +50,7 @@ public enum FxmlFile {
|
||||
this.ressourcePathString = ressourcePathString;
|
||||
}
|
||||
|
||||
String getRessourcePathString() {
|
||||
public String getRessourcePathString() {
|
||||
return ressourcePathString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public class FxmlLoaderFactory {
|
||||
this.resourceBundle = resourceBundle;
|
||||
}
|
||||
|
||||
public static <T extends FxController> FxmlLoaderFactory forController(T controller, Function<Parent, Scene> sceneFactory, ResourceBundle resourceBundle) {
|
||||
return new FxmlLoaderFactory(Map.of(controller.getClass(), () -> controller), sceneFactory, resourceBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new FXMLLoader instance
|
||||
*/
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class StageFactory {
|
||||
|
||||
private final Consumer<Stage> initializer;
|
||||
|
||||
public StageFactory(Consumer<Stage> initializer) {
|
||||
@Inject
|
||||
public StageFactory(StageInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
public Stage create() {
|
||||
return create(StageStyle.DECORATED);
|
||||
}
|
||||
|
||||
public Stage create(StageStyle stageStyle) {
|
||||
Stage stage = new Stage(stageStyle);
|
||||
Stage stage = new Stage(StageStyle.DECORATED);
|
||||
initializer.accept(stage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Performs common setup for all stages
|
||||
*/
|
||||
@FxApplicationScoped
|
||||
public class StageInitializer implements Consumer<Stage> {
|
||||
|
||||
private final List<Image> windowIcons;
|
||||
|
||||
@Inject
|
||||
public StageInitializer() {
|
||||
this.windowIcons = SystemUtils.IS_OS_MAC ? List.of() : List.of( //
|
||||
new Image(StageInitializer.class.getResource("/img/window_icon_32.png").toString()), //
|
||||
new Image(StageInitializer.class.getResource("/img/window_icon_512.png").toString()) //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Stage stage) {
|
||||
stage.getIcons().setAll(windowIcons);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class UserInteractionLock<E extends Enum<E>> {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition condition = lock.newCondition();
|
||||
private final BooleanProperty awaitingInteraction = new SimpleBooleanProperty();
|
||||
private final AtomicBoolean interacted = new AtomicBoolean();
|
||||
private final AtomicReference<E> state;
|
||||
|
||||
public UserInteractionLock(E initialValue) {
|
||||
this.state = new AtomicReference<>(initialValue);
|
||||
}
|
||||
|
||||
public synchronized void reset(E value) {
|
||||
state.set(value);
|
||||
interacted.set(false);
|
||||
}
|
||||
|
||||
public void interacted(E result) {
|
||||
assert Platform.isFxApplicationThread();
|
||||
lock.lock();
|
||||
try {
|
||||
state.set(result);
|
||||
interacted.set(true);
|
||||
awaitingInteraction.set(false);
|
||||
condition.signal();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public E awaitInteraction() throws InterruptedException {
|
||||
assert !Platform.isFxApplicationThread();
|
||||
lock.lock();
|
||||
try {
|
||||
Platform.runLater(() -> awaitingInteraction.set(true));
|
||||
while (!interacted.get()) {
|
||||
condition.await();
|
||||
}
|
||||
return state.get();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyBooleanProperty awaitingInteraction() {
|
||||
return awaitingInteraction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@@ -53,7 +54,9 @@ public class VaultService {
|
||||
*
|
||||
* @param vault The vault to lock
|
||||
* @param forced Whether to attempt a forced lock
|
||||
* @deprecated use {@link org.cryptomator.ui.fxapp.FxApplicationWindows#startLockWorkflow(Vault, Stage)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void lock(Vault vault, boolean forced) {
|
||||
executorService.execute(createLockTask(vault, forced));
|
||||
}
|
||||
@@ -83,14 +86,15 @@ public class VaultService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates but doesn't start a lock-all task.
|
||||
* Creates a lock-all task.
|
||||
* This task itself is _not started_, but its subtasks locking each vault will be already executed.
|
||||
*
|
||||
* @param vaults The list of vaults to be locked
|
||||
* @param forced Whether to attempt a forced lock
|
||||
* @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask
|
||||
*/
|
||||
public Task<Collection<Vault>> createLockAllTask(Collection<Vault> vaults, boolean forced) {
|
||||
List<Task<Vault>> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList());
|
||||
List<Task<Vault>> lockTasks = vaults.stream().<Task<Vault>>map(v -> new LockVaultTask(v, forced)).toList();
|
||||
lockTasks.forEach(executorService::execute);
|
||||
Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks);
|
||||
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import org.cryptomator.common.Passphrase;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.StringProperty;
|
||||
@@ -82,7 +84,7 @@ public class NiceSecurePasswordField extends StackPane {
|
||||
return passwordField.textProperty();
|
||||
}
|
||||
|
||||
public CharSequence getCharacters() {
|
||||
public Passphrase getCharacters() {
|
||||
return passwordField.getCharacters();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.cryptomator.ui.controls;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.cryptomator.common.Passphrase;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.NamedArg;
|
||||
@@ -28,7 +29,6 @@ import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import java.nio.CharBuffer;
|
||||
import java.text.Normalizer;
|
||||
import java.text.Normalizer.Form;
|
||||
import java.util.Arrays;
|
||||
@@ -203,8 +203,8 @@ public class SecurePasswordField extends TextField {
|
||||
* @see #wipe()
|
||||
*/
|
||||
@Override
|
||||
public CharSequence getCharacters() {
|
||||
return CharBuffer.wrap(content, 0, length);
|
||||
public Passphrase getCharacters() {
|
||||
return new Passphrase(content, 0, length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,9 +43,11 @@ public class ForgetPasswordController implements FxController {
|
||||
LOG.debug("Forgot password for vault {}.", vault.getDisplayName());
|
||||
confirmedResult.setValue(true);
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to remove entry from system keychain.", e);
|
||||
LOG.error("Failed to delete passphrase from system keychain.", e);
|
||||
confirmedResult.setValue(false);
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Keychain not supported. Doing nothing.");
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.launcher.AppLaunchEvent;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.application.Platform;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@@ -17,22 +17,25 @@ import java.util.concurrent.ExecutorService;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
|
||||
|
||||
@Singleton
|
||||
// TODO: use message bus
|
||||
@FxApplicationScoped
|
||||
class AppLaunchEventHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class);
|
||||
|
||||
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||
private final ExecutorService executorService;
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final VaultService vaultService;
|
||||
|
||||
@Inject
|
||||
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationStarter fxApplicationStarter, VaultListManager vaultListManager) {
|
||||
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
|
||||
this.launchEventQueue = launchEventQueue;
|
||||
this.executorService = executorService;
|
||||
this.fxApplicationStarter = fxApplicationStarter;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.vaultService = vaultService;
|
||||
}
|
||||
|
||||
public void startHandlingLaunchEvents() {
|
||||
@@ -52,14 +55,12 @@ class AppLaunchEventHandler {
|
||||
}
|
||||
|
||||
private void handleLaunchEvent(AppLaunchEvent event) {
|
||||
switch (event.getType()) {
|
||||
case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow);
|
||||
case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> {
|
||||
Platform.runLater(() -> {
|
||||
event.getPathsToOpen().forEach(this::addOrRevealVault);
|
||||
});
|
||||
switch (event.type()) {
|
||||
case REVEAL_APP -> appWindows.showMainWindow();
|
||||
case OPEN_FILE -> Platform.runLater(() -> {
|
||||
event.pathsToOpen().forEach(this::addOrRevealVault);
|
||||
});
|
||||
default -> LOG.warn("Unsupported event type: {}", event.getType());
|
||||
default -> LOG.warn("Unsupported event type: {}", event.type());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ class AppLaunchEventHandler {
|
||||
}
|
||||
|
||||
if (v.isUnlocked()) {
|
||||
fxApplicationStarter.get().thenAccept(app -> app.getVaultService().reveal(v));
|
||||
vaultService.reveal(v);
|
||||
}
|
||||
LOG.debug("Added vault {}", potentialVaultPath);
|
||||
} catch (IOException e) {
|
||||
30
src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
Normal file
30
src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class AutoUnlocker {
|
||||
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
@Inject
|
||||
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows) {
|
||||
this.vaults = vaults;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
vaults.stream().filter(Vault::isLocked) //
|
||||
.filter(v -> v.getVaultSettings().unlockAfterStartup().get()) //
|
||||
.<CompletionStage<Void>>reduce(CompletableFuture.completedFuture(null), //
|
||||
(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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
|
||||
record ExitingQuitResponse(QuitResponse delegate) implements QuitResponse {
|
||||
|
||||
@Override
|
||||
public void performQuit() {
|
||||
Platform.exit();
|
||||
// TODO wait a moment for javafx to terminate?
|
||||
delegate.performQuit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
delegate.cancelQuit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,213 +1,74 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Lazy;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.Theme;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplication extends Application {
|
||||
public class FxApplication {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||
|
||||
private final long startupTime;
|
||||
private final Settings settings;
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final Lazy<QuitComponent> quitWindow;
|
||||
private final Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider;
|
||||
private final Provider<LockComponent.Builder> lockWorkflowBuilderProvider;
|
||||
private final ErrorComponent.Builder errorWindowBuilder;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Optional<UiAppearanceProvider> appearanceProvider;
|
||||
private final VaultService vaultService;
|
||||
private final LicenseHolder licenseHolder;
|
||||
private final ObservableList<Window> visibleWindows;
|
||||
private final BooleanBinding hasVisibleWindows;
|
||||
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
|
||||
private final AppLaunchEventHandler launchEventHandler;
|
||||
private final Lazy<TrayMenuComponent> trayMenu;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final FxApplicationStyle applicationStyle;
|
||||
private final FxApplicationTerminator applicationTerminator;
|
||||
private final AutoUnlocker autoUnlocker;
|
||||
|
||||
@Inject
|
||||
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
|
||||
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||
this.startupTime = startupTime;
|
||||
this.settings = settings;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
|
||||
this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
|
||||
this.quitWindow = quitWindow;
|
||||
this.errorWindowBuilder = errorWindowBuilder;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.appearanceProvider = appearanceProvider;
|
||||
this.vaultService = vaultService;
|
||||
this.licenseHolder = licenseHolder;
|
||||
this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
|
||||
this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
|
||||
this.launchEventHandler = launchEventHandler;
|
||||
this.trayMenu = trayMenu;
|
||||
this.appWindows = appWindows;
|
||||
this.applicationStyle = applicationStyle;
|
||||
this.applicationTerminator = applicationTerminator;
|
||||
this.autoUnlocker = autoUnlocker;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
LOG.trace("FxApplication.start()");
|
||||
Platform.setImplicitExit(false);
|
||||
applicationStyle.initialize();
|
||||
appWindows.initialize();
|
||||
applicationTerminator.initialize();
|
||||
|
||||
hasVisibleWindows.addListener(this::hasVisibleStagesChanged);
|
||||
|
||||
settings.theme().addListener(this::appThemeChanged);
|
||||
loadSelectedStyleSheet(settings.theme().get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) {
|
||||
throw new UnsupportedOperationException("Use start() instead.");
|
||||
}
|
||||
|
||||
private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
|
||||
LOG.debug("has visible stages: {}", newValue);
|
||||
if (newValue) {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
|
||||
// init system tray
|
||||
final boolean hasTrayIcon;
|
||||
if (settings.showTrayIcon().get() && trayMenu.get().isSupported()) {
|
||||
trayMenu.get().initializeTrayIcon();
|
||||
Platform.setImplicitExit(false); // don't quit when closing all windows
|
||||
hasTrayIcon = true;
|
||||
} else {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
|
||||
hasTrayIcon = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void showPreferencesWindow(SelectedPreferencesTab selectedTab) {
|
||||
Platform.runLater(() -> {
|
||||
preferencesWindow.get().showPreferencesWindow(selectedTab);
|
||||
LOG.debug("Showing Preferences");
|
||||
});
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showMainWindow() {
|
||||
CompletableFuture<Stage> future = new CompletableFuture<>();
|
||||
Platform.runLater(() -> {
|
||||
var win = mainWindow.get().showMainWindow();
|
||||
LOG.debug("Showing MainWindow");
|
||||
future.complete(win);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
|
||||
unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
|
||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
|
||||
// show main window
|
||||
appWindows.showMainWindow().thenAccept(stage -> {
|
||||
if (settings.startHidden().get()) {
|
||||
if (hasTrayIcon) {
|
||||
stage.hide();
|
||||
} else {
|
||||
stage.setIconified(true);
|
||||
}
|
||||
}
|
||||
LOG.debug("Main window initialized after {}ms", System.currentTimeMillis() - startupTime);
|
||||
}).exceptionally(error -> {
|
||||
LOG.error("Failed to show main window", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
|
||||
lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showQuitWindow(QuitResponse response) {
|
||||
Platform.runLater(() -> {
|
||||
quitWindow.get().showQuitWindow(response);
|
||||
LOG.debug("Showing QuitWindow");
|
||||
});
|
||||
}
|
||||
|
||||
public VaultService getVaultService() {
|
||||
return vaultService;
|
||||
}
|
||||
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
||||
try {
|
||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to disable automatic theme switching.");
|
||||
}
|
||||
}
|
||||
loadSelectedStyleSheet(newValue);
|
||||
}
|
||||
|
||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
case AUTOMATIC -> {
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
try {
|
||||
appearanceProvider.addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to enable automatic theme switching.");
|
||||
}
|
||||
});
|
||||
applySystemTheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme theme) {
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applySystemTheme() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
||||
} else {
|
||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
||||
applyLightTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLightTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
appearanceProvider.adjustToTheme(Theme.LIGHT);
|
||||
});
|
||||
}
|
||||
|
||||
private void applyDarkTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
appearanceProvider.adjustToTheme(Theme.DARK);
|
||||
});
|
||||
launchEventHandler.startHandlingLaunchEvents();
|
||||
autoUnlocker.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@FxApplicationScoped
|
||||
@Subcomponent(modules = FxApplicationModule.class)
|
||||
public interface FxApplicationComponent {
|
||||
@@ -16,6 +20,12 @@ public interface FxApplicationComponent {
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder fxApplication(Application application);
|
||||
|
||||
@BindsInstance
|
||||
Builder primaryStage(@PrimaryStage Stage primaryStage);
|
||||
|
||||
FxApplicationComponent build();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
@@ -15,68 +14,48 @@ import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {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 {
|
||||
|
||||
@Provides
|
||||
@Named("windowIcons")
|
||||
@FxApplicationScoped
|
||||
static List<Image> provideWindowIcons() {
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return List.of( //
|
||||
createImageFromResource("/img/window_icon_32.png"), //
|
||||
createImageFromResource("/img/window_icon_512.png") //
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to load embedded resource.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static StageFactory provideStageFactory(@Named("windowIcons") List<Image> windowIcons) {
|
||||
return new StageFactory(stage -> {
|
||||
stage.getIcons().addAll(windowIcons);
|
||||
});
|
||||
}
|
||||
|
||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
|
||||
return new Image(in);
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract Application bindApplication(FxApplication application);
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java
Normal file
108
src/main/java/org/cryptomator/ui/fxapp/FxApplicationStyle.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.integrations.uiappearance.Theme;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.util.Optional;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationStyle {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStyle.class);
|
||||
|
||||
private final Settings settings;
|
||||
private final Optional<UiAppearanceProvider> appearanceProvider;
|
||||
private final LicenseHolder licenseHolder;
|
||||
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
|
||||
|
||||
@Inject
|
||||
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder){
|
||||
this.settings = settings;
|
||||
this.appearanceProvider = appearanceProvider;
|
||||
this.licenseHolder = licenseHolder;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
settings.theme().addListener(this::appThemeChanged);
|
||||
loadSelectedStyleSheet(settings.theme().get());
|
||||
}
|
||||
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
||||
try {
|
||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to disable automatic theme switching.");
|
||||
}
|
||||
}
|
||||
loadSelectedStyleSheet(newValue);
|
||||
}
|
||||
|
||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
case AUTOMATIC -> {
|
||||
appearanceProvider.ifPresent(provider -> {
|
||||
try {
|
||||
provider.addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to enable automatic theme switching.");
|
||||
}
|
||||
});
|
||||
applySystemTheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme theme) {
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applySystemTheme() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
||||
} else {
|
||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
||||
applyLightTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLightTheme() {
|
||||
var stylesheet = Optional //
|
||||
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
|
||||
.orElse(getClass().getResource("/css/light_theme.css"));
|
||||
if (stylesheet == null) {
|
||||
LOG.warn("Failed to load light_theme stylesheet");
|
||||
} else {
|
||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDarkTheme() {
|
||||
var stylesheet = Optional //
|
||||
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
|
||||
.orElse(getClass().getResource("/css/dark_theme.css"));
|
||||
if (stylesheet == null) {
|
||||
LOG.warn("Failed to load dark_theme stylesheet");
|
||||
} else {
|
||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.awt.desktop.QuitStrategy;
|
||||
import java.util.EnumSet;
|
||||
import java.util.EventObject;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationTerminator {
|
||||
|
||||
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
|
||||
private static final Set<VaultState.Value> STATES_PREVENT_TERMINATION = EnumSet.of(PROCESSING);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
|
||||
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final ShutdownHook shutdownHook;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final AtomicBoolean allowQuitWithoutPrompt = new AtomicBoolean();
|
||||
private final AtomicBoolean preventQuitWithGracefulLock = new AtomicBoolean();
|
||||
private final Settings settings;
|
||||
private final VaultService vaultService;
|
||||
|
||||
@Inject
|
||||
public FxApplicationTerminator(ObservableList<Vault> vaults, ShutdownHook shutdownHook, FxApplicationWindows appWindows, Settings settings, VaultService vaultService) {
|
||||
this.vaults = vaults;
|
||||
this.shutdownHook = shutdownHook;
|
||||
this.appWindows = appWindows;
|
||||
this.settings = settings;
|
||||
this.vaultService = vaultService;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
|
||||
// register quit handler
|
||||
if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
|
||||
desktop.setQuitHandler(this::handleQuitRequest);
|
||||
}
|
||||
|
||||
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
|
||||
if (desktop.isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
|
||||
desktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
|
||||
}
|
||||
|
||||
// allow sudden termination?
|
||||
vaultListChanged(vaults);
|
||||
vaults.addListener(this::vaultListChanged);
|
||||
|
||||
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully terminates the application.
|
||||
*/
|
||||
public void terminate() {
|
||||
handleQuitRequest(null, new NoopQuitResponse());
|
||||
}
|
||||
|
||||
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
||||
boolean allowSuddenTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
|
||||
boolean stateChanged = allowQuitWithoutPrompt.compareAndSet(!allowSuddenTermination, allowSuddenTermination);
|
||||
|
||||
boolean preventGracefulTermination = vaults.stream().map(Vault::getState).anyMatch(STATES_PREVENT_TERMINATION::contains);
|
||||
preventQuitWithGracefulLock.set(preventGracefulTermination);
|
||||
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
if (stateChanged && desktop.isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
|
||||
if (allowSuddenTermination) {
|
||||
LOG.debug("Enabling sudden termination");
|
||||
desktop.enableSuddenTermination();
|
||||
} else {
|
||||
LOG.debug("Disabling sudden termination");
|
||||
desktop.disableSuddenTermination();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the app to quit. If confirmed, the JavaFX application will exit before giving a {@code response}.
|
||||
*
|
||||
* @param e ignored
|
||||
* @param response a quit response that will be {@link ExitingQuitResponse decorated in order to exit the JavaFX application}.
|
||||
*/
|
||||
private void handleQuitRequest(@SuppressWarnings("unused") @Nullable EventObject e, QuitResponse response) {
|
||||
var exitingResponse = new ExitingQuitResponse(response);
|
||||
|
||||
if (allowQuitWithoutPrompt.get()) {
|
||||
exitingResponse.performQuit();
|
||||
} else if (settings.autoCloseVaults().get() && !preventQuitWithGracefulLock.get()) {
|
||||
var lockAllTask = vaultService.createLockAllTask(vaults.filtered(Vault::isUnlocked), false);
|
||||
lockAllTask.setOnSucceeded(event -> {
|
||||
LOG.info("Locked remaining vaults was succesful.");
|
||||
exitingResponse.performQuit();
|
||||
});
|
||||
lockAllTask.setOnFailed(event -> {
|
||||
LOG.warn("Unable to lock all vaults.");
|
||||
appWindows.showQuitWindow(exitingResponse, true);
|
||||
});
|
||||
lockAllTask.run();
|
||||
} else {
|
||||
appWindows.showQuitWindow(exitingResponse, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceUnmountRemainingVaults() {
|
||||
for (Vault vault : vaults) {
|
||||
if (vault.isUnlocked()) {
|
||||
try {
|
||||
vault.lock(true);
|
||||
} catch (Volume.VolumeException e) {
|
||||
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
||||
} catch (LockNotCompletedException e) {
|
||||
LOG.error("Failed to lock vault " + vault.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy QuitResponse that ignores the response.
|
||||
* <p>
|
||||
* To be used with {@link #handleQuitRequest(EventObject, QuitResponse)} if the invoking method is not interested in the response.
|
||||
*/
|
||||
private static class NoopQuitResponse implements QuitResponse {
|
||||
|
||||
@Override
|
||||
public void performQuit() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
155
src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
Normal file
155
src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.desktop.AppReopenedListener;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationWindows {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationWindows.class);
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final QuitComponent.Builder quitWindowBuilder;
|
||||
private final UnlockComponent.Factory unlockWorkflowFactory;
|
||||
private final LockComponent.Factory lockWorkflowFactory;
|
||||
private final ErrorComponent.Factory errorWindowFactory;
|
||||
private final ExecutorService executor;
|
||||
private final FilteredList<Window> visibleWindows;
|
||||
|
||||
@Inject
|
||||
public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional<TrayIntegrationProvider> trayIntegration, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, QuitComponent.Builder quitWindowBuilder, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.quitWindowBuilder = quitWindowBuilder;
|
||||
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||
this.errorWindowFactory = errorWindowFactory;
|
||||
this.executor = executor;
|
||||
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
|
||||
// register preferences shortcut
|
||||
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
|
||||
desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||
}
|
||||
|
||||
// register preferences shortcut
|
||||
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
|
||||
desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
|
||||
}
|
||||
|
||||
// register app reopen listener
|
||||
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
|
||||
desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
|
||||
}
|
||||
|
||||
// observe visible windows
|
||||
if (trayIntegration.isPresent()) {
|
||||
visibleWindows.addListener(this::visibleWindowsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void visibleWindowsChanged(ListChangeListener.Change<? extends Window> change) {
|
||||
int visibleWindows = change.getList().size();
|
||||
LOG.debug("visible windows: {}", visibleWindows);
|
||||
if (visibleWindows > 0) {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
|
||||
} else {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showMainWindow() {
|
||||
return CompletableFuture.supplyAsync(mainWindow.get()::showMainWindow, Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showPreferencesWindow(SelectedPreferencesTab selectedTab) {
|
||||
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public void showQuitWindow(QuitResponse response, boolean forced) {
|
||||
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
|
||||
}
|
||||
|
||||
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
|
||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
||||
}, Platform::runLater) //
|
||||
.thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
|
||||
.exceptionally(e -> {
|
||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public CompletionStage<Void> startLockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING), "Vault not unlocked.");
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
return lockWorkflowFactory.create(vault, owner).lockWorkflow();
|
||||
}, Platform::runLater) //
|
||||
.thenCompose(lockWorkflow -> CompletableFuture.runAsync(lockWorkflow, executor)) //
|
||||
.exceptionally(e -> {
|
||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the generic error scene in the given window.
|
||||
*
|
||||
* @param cause The exception to show
|
||||
* @param window What window to display the scene in
|
||||
* @param previousScene To what scene to return to when pressing "back". Back button will be hidden, if <code>null</code>
|
||||
* @return A
|
||||
*/
|
||||
public CompletionStage<Stage> showErrorWindow(Throwable cause, Stage window, @Nullable Scene previousScene) {
|
||||
return CompletableFuture.supplyAsync(() -> errorWindowFactory.create(cause, window, previousScene).show(), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
private void reportErrors(@Nullable Stage stage, @Nullable Throwable error) {
|
||||
if (error != null) {
|
||||
LOG.error("Failed to display stage", error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/java/org/cryptomator/ui/fxapp/PrimaryStage.java
Normal file
14
src/main/java/org/cryptomator/ui/fxapp/PrimaryStage.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
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)
|
||||
public @interface PrimaryStage {
|
||||
|
||||
}
|
||||
@@ -43,7 +43,9 @@ public abstract class UpdateCheckerModule {
|
||||
@FxApplicationScoped
|
||||
static Optional<HttpClient> provideHttpClient() {
|
||||
try {
|
||||
return Optional.of(HttpClient.newHttpClient());
|
||||
return Optional.of(HttpClient.newBuilder() //
|
||||
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
|
||||
.build());
|
||||
} catch (UncheckedIOException e) {
|
||||
LOG.error("HttpClient for update check cannot be created.", e);
|
||||
return Optional.empty();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user