mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-23 13:11:28 +00:00
Compare commits
275 Commits
1.7.1
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bad933c85 | ||
|
|
ca3a11de90 | ||
|
|
91ece74f57 | ||
|
|
b9f77cb1d6 | ||
|
|
b6d09016cd | ||
|
|
0a07103a4f | ||
|
|
96253f636a | ||
|
|
bd932b1067 | ||
|
|
ade277daf4 | ||
|
|
14776fc571 | ||
|
|
c18f489e9d | ||
|
|
bfc1e25335 | ||
|
|
4c2e6088a9 | ||
|
|
f1d4bd4cd4 | ||
|
|
2a0e0738be | ||
|
|
1fec9781bf | ||
|
|
886c69f298 | ||
|
|
58c7774e0d | ||
|
|
df31b214f5 | ||
|
|
25474ce014 | ||
|
|
5f62f038d5 | ||
|
|
eebba45909 | ||
|
|
982e88d1c5 | ||
|
|
632d39bc9f | ||
|
|
f25afb720f | ||
|
|
bc361ea592 | ||
|
|
2202c91564 | ||
|
|
f817bc5378 | ||
|
|
c30c90d2c3 | ||
|
|
09b638eafa | ||
|
|
91de0c1813 | ||
|
|
a3c953d437 | ||
|
|
743da019f9 | ||
|
|
2c506fcc97 | ||
|
|
dd71df8382 | ||
|
|
2c984ad405 | ||
|
|
171b0e61ad | ||
|
|
e5047c242c | ||
|
|
131145e8b8 | ||
|
|
11a0136cb9 | ||
|
|
e92b80bdf4 | ||
|
|
9d81b2312f | ||
|
|
511dbe355a | ||
|
|
cca6d475b3 | ||
|
|
88c6246ceb | ||
|
|
7bf2de6339 | ||
|
|
894d0528df | ||
|
|
168b9ef096 | ||
|
|
6c8005e1b2 | ||
|
|
ed8457ff36 | ||
|
|
a50bc8e05c | ||
|
|
e382a3bab9 | ||
|
|
9cd50e8ae4 | ||
|
|
1a6836dac6 | ||
|
|
067814d5db | ||
|
|
29d63a0a83 | ||
|
|
8c3ede0d2a | ||
|
|
ee82965ef5 | ||
|
|
ab048508a8 | ||
|
|
47ae271126 | ||
|
|
ef22928e01 | ||
|
|
287584429b | ||
|
|
e23c0c23c9 | ||
|
|
e9bb3195dd | ||
|
|
b1ff94bdd6 | ||
|
|
79eea4814e | ||
|
|
cee0486d71 | ||
|
|
12e990a8b3 | ||
|
|
24fc2888ef | ||
|
|
9b0c940195 | ||
|
|
ce596698e3 | ||
|
|
9383abbdbd | ||
|
|
a31d3185dc | ||
|
|
3098628db1 | ||
|
|
3c53968dd2 | ||
|
|
6c18e3a929 | ||
|
|
99862869bc | ||
|
|
c0368f2a50 | ||
|
|
c6c292b505 | ||
|
|
ebc60c4fb3 | ||
|
|
5f2497de90 | ||
|
|
b2a184bdf0 | ||
|
|
01da51e6e7 | ||
|
|
8417618615 | ||
|
|
386bdd3490 | ||
|
|
fe3abcaaa8 | ||
|
|
56ba12fe56 | ||
|
|
5e52f715ce | ||
|
|
2c2606d6ad | ||
|
|
b3d8df0da0 | ||
|
|
ebea8ef7e5 | ||
|
|
ec645a4bb9 | ||
|
|
2c0474ec46 | ||
|
|
4d09728880 | ||
|
|
173b1e8386 | ||
|
|
9b18a179c2 | ||
|
|
12b38ad8c8 | ||
|
|
4065e15aa1 | ||
|
|
6fda206b9e | ||
|
|
638a6d1195 | ||
|
|
27bc62e602 | ||
|
|
2e7af0affa | ||
|
|
19c41a3cca | ||
|
|
3bf4473f9d | ||
|
|
f92c436339 | ||
|
|
f054a1af03 | ||
|
|
c46eeb0401 | ||
|
|
d680c5812d | ||
|
|
3120751d3e | ||
|
|
3c623b7ed1 | ||
|
|
150dd3e542 | ||
|
|
8f3f23939c | ||
|
|
5d796efe30 | ||
|
|
702ae72063 | ||
|
|
b296dc775c | ||
|
|
e5e0d4076a | ||
|
|
da0eeb2e45 | ||
|
|
ff49094f35 | ||
|
|
be1c5da54e | ||
|
|
40abf582c5 | ||
|
|
a8377be691 | ||
|
|
0e7a27b2e1 | ||
|
|
d82d11feb7 | ||
|
|
81c12f50fe | ||
|
|
e2eac0e398 | ||
|
|
9d573c497e | ||
|
|
81087a9568 | ||
|
|
2806525397 | ||
|
|
0c0060262a | ||
|
|
9af4ffe83b | ||
|
|
c6f963793d | ||
|
|
8c34fc76c5 | ||
|
|
785cf7a9a6 | ||
|
|
c63837c4ce | ||
|
|
b1a3ef9023 | ||
|
|
32436f779f | ||
|
|
ccc6f605ba | ||
|
|
f338d2447b | ||
|
|
179240b325 | ||
|
|
32a65bddce | ||
|
|
710cdf800d | ||
|
|
1d6edb8373 | ||
|
|
a3d30612ec | ||
|
|
6acda9b13c | ||
|
|
28cb812dab | ||
|
|
68ea4af0ad | ||
|
|
0af0a9e440 | ||
|
|
0989c735c0 | ||
|
|
a3492b9ea3 | ||
|
|
e345e6415f | ||
|
|
5b6d09308b | ||
|
|
49bda58993 | ||
|
|
32d7189a12 | ||
|
|
1253b7db2b | ||
|
|
067a7ad3ee | ||
|
|
a9ec76a344 | ||
|
|
085f762a35 | ||
|
|
7dd1c3576f | ||
|
|
d23bd2865a | ||
|
|
4429d57b5e | ||
|
|
2ff71ed7b0 | ||
|
|
82de8b6994 | ||
|
|
d4cba2fd6e | ||
|
|
ff80f634d2 | ||
|
|
b10523ea6c | ||
|
|
6386dd3d50 | ||
|
|
a3f05db189 | ||
|
|
151ef6c7b2 | ||
|
|
72fd38baf1 | ||
|
|
532ffb1202 | ||
|
|
2a704d5eb4 | ||
|
|
e8f8466d9a | ||
|
|
9297562c99 | ||
|
|
7d62fc78de | ||
|
|
ba627d0d60 | ||
|
|
8e7e7de358 | ||
|
|
10c60d7492 | ||
|
|
aa03bd119a | ||
|
|
325ffda9af | ||
|
|
d1270ceeb2 | ||
|
|
901a290dd9 | ||
|
|
35b9dadfc2 | ||
|
|
5f57678edc | ||
|
|
30e1922bc9 | ||
|
|
2e0908ab15 | ||
|
|
689ce5b985 | ||
|
|
a71a23aa31 | ||
|
|
864454e6fc | ||
|
|
94c3381723 | ||
|
|
d9f945e70a | ||
|
|
dc9b39202f | ||
|
|
2a01aba3cf | ||
|
|
4305fd3285 | ||
|
|
a24cd1ba7f | ||
|
|
2ba0d963ec | ||
|
|
cd0c6fbd33 | ||
|
|
f4374a2606 | ||
|
|
a1d5b8a4e2 | ||
|
|
34e430aff6 | ||
|
|
c79766cdf6 | ||
|
|
bf76bad626 | ||
|
|
c3f654b454 | ||
|
|
d1d990d47c | ||
|
|
6052c0589e | ||
|
|
db2560fccf | ||
|
|
3a50c32e50 | ||
|
|
65eca31d26 | ||
|
|
3462e0b540 | ||
|
|
697529136e | ||
|
|
50d31bdc18 | ||
|
|
84caf96d3f | ||
|
|
a1a5fd3609 | ||
|
|
6c11cc8f1d | ||
|
|
2b391a6ee3 | ||
|
|
b7fc03213d | ||
|
|
dfe17569e1 | ||
|
|
827f9ad141 | ||
|
|
c8a6d0339e | ||
|
|
b5bbd21f25 | ||
|
|
771468c8c6 | ||
|
|
ea2a48771f | ||
|
|
0e10da25b3 | ||
|
|
943a3e9cfd | ||
|
|
c988fb50a7 | ||
|
|
219ee0da9a | ||
|
|
5665e92839 | ||
|
|
04ff188624 | ||
|
|
ec7d6eafec | ||
|
|
28bb2ff9b1 | ||
|
|
a92ebfdc7b | ||
|
|
f1e97fa64b | ||
|
|
b9d5cf04c2 | ||
|
|
75cd3e44d8 | ||
|
|
3cf1b829b8 | ||
|
|
6e4e9cd261 | ||
|
|
e15dd7565f | ||
|
|
77bc60fe5b | ||
|
|
9f633a1ecb | ||
|
|
fcf59d12a8 | ||
|
|
6721075831 | ||
|
|
4bb0026415 | ||
|
|
bebae14744 | ||
|
|
997315eaf5 | ||
|
|
16d677c40f | ||
|
|
42a1913c17 | ||
|
|
c3f6655e48 | ||
|
|
fa1b0f2de8 | ||
|
|
385574a618 | ||
|
|
a67477bf3b | ||
|
|
03a362e9b4 | ||
|
|
2223bc5e78 | ||
|
|
fdc0d2d6b5 | ||
|
|
58ed48b097 | ||
|
|
7a0d255bd3 | ||
|
|
228fa099cb | ||
|
|
a60ff20f15 | ||
|
|
2328a5e3a8 | ||
|
|
e7e181b1a5 | ||
|
|
f3e7c08b43 | ||
|
|
2a41afcfa9 | ||
|
|
5bf38a328c | ||
|
|
f983b29034 | ||
|
|
4bc217e489 | ||
|
|
b24f52a4ec | ||
|
|
91e7fa3de3 | ||
|
|
1365efab3f | ||
|
|
852963b785 | ||
|
|
c47d4eaf02 | ||
|
|
4fc07c27b3 | ||
|
|
ec1d25bf65 | ||
|
|
7aa554498b | ||
|
|
85ac3c244d | ||
|
|
5db5346c88 | ||
|
|
8f4bf144c3 | ||
|
|
268c66a108 |
136
.github/SECURITY.md
vendored
136
.github/SECURITY.md
vendored
@@ -2,123 +2,25 @@
|
|||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
For reporting security-related vulnerabilities or exploits that [haven't been reported yet](https://github.com/cryptomator/cryptomator/labels/type%3Asecurity-issue), contact us at: security@cryptomator.org
|
We take security seriously at Cryptomator. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
<details>
|
To report a security vulnerability, please use the [GitHub Security Advisory feature](https://github.com/cryptomator/cryptomator/security/advisories). This feature allows you to privately discuss, fix, and publish information about security vulnerabilities.
|
||||||
<summary>PGP Key</summary>
|
|
||||||
|
|
||||||
```
|
If you prefer to report the vulnerability via email, please send an email to security@cryptomator.org.
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Comment: GPGTools - https://gpgtools.org
|
|
||||||
|
|
||||||
mQINBFbgeicBEADM9AcU6DTgM5KZnBaJc6x9DBLr+TCMHntTt7YM9GLTlO2Z43Jt
|
PGP key fingerprint: `3647 9903 B23A E0A5 9359 9A3E 23B5 DBEF 94D4 D81D` ([public key](https://gist.github.com/cryptobot/864300b6b44ae2d2a15abedfe14bd040))
|
||||||
oYoyqdRWAY28veqpLEFgRvvVD3fdBj/KUOxF1cr2JsErwXqbjwaLq0o/0KIXz7UK
|
|
||||||
a6pQSemZKfpOtJrfacofOTwvG6AuG9uakBYNMyxuojyOkoh3xsYS1KZ7TwPgCdET
|
## Expectations
|
||||||
t8/zva41Pa5kh5+GeSZJdCuygG6ynPBJEpmK5V7Qizvics5fziXecF+QaFZijafv
|
|
||||||
YahfxokvF9pXCQTmV4m57NQma9uK0w83U9nJCPjEd+x3wK0Hxrc1ojy8ZFTA1YND
|
When reporting a vulnerability, please provide us with a detailed report that includes:
|
||||||
AQg/MTABgHbQQkXDQhjS/TloOObqtbMBqNSbcSXpaR4teaCWKBl1MSq00nJLj8db
|
|
||||||
vPJGqfg7UbXhlALggp029/kskYlR5SmbxWquLbl0Xre3fDHuHEiWcJL6MS3454Wt
|
- A description of the vulnerability
|
||||||
Mno13/4UhOlRFh5g0pLmPz7seOTJjDqc9abn/RXOLq0+3qX0gC0bDm5aCE5dQ2MV
|
- Steps to reproduce the vulnerability
|
||||||
FMbrrlw/dZESNLZvtB3gOsramSry1R3HVZ0QJ2vMaF2cxewebqcYbuecUNj6bxpv
|
- Possible impact of the vulnerability
|
||||||
5LEhEmqz6dG1meLLWDsvQLPEUWEIJnfpBiDSm342yxJq4pXnVF+aqAQsCL3FpmvZ
|
- Any additional information that may be helpful
|
||||||
2j0FgFOs7iXOcFUJIiR0xUmWPk1NWYcUowqmRW8pMM9nFUzFF99iggPznwARAQAB
|
|
||||||
tC1DcnlwdG9tYXRvciBTdXBwb3J0IDxzdXBwb3J0QGNyeXB0b21hdG9yLm9yZz6J
|
We ask that you do not publicly disclose the vulnerability until we have had a chance to address it.
|
||||||
AkAEEwEKACoCGwMFCQcrKAAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAleu2cQC
|
|
||||||
GQEACgkQI7Xb75TU2B3+7A/7BKRWdo5/moCCEbBzYQ7vRMLFdwmjFFlSZ7aGC0fP
|
## Thank You
|
||||||
YHdeUwxPbO0cATwmNpGMma7rBn1FDg3Vto6/wottGxm+XIRwlyY84CD1VZAihZ/e
|
|
||||||
WvjOO28/7VgRy6PGKzlhpDSoT8GwFOgO69e7bEff1Zj562RZe7nXc4tDivILMB++
|
We appreciate your help in keeping Cryptomator secure. Thank you for your contributions to the security of our project.
|
||||||
KgmmSgtddygmNQCS3RD3KssGo+l+cSjsg09F5WAJ6nQe8Jq2hICq+o/P6UXPI5lX
|
|
||||||
bhvWYDn4/8sRHsIlGpQYYDDe0fz7IQKuSLAHpF5upNDxj6dYb05F8PPVrk6MW6nL
|
|
||||||
/kf1fZ27DlLN5/NFvhhBRuwxxoAFqPS7Iel3z7L0JkRUYmGLVB5m9Cqiw6FK8JRv
|
|
||||||
OtvakdDoKb5lVAoN5NeBfNBSqEcXVF/EdfTfIyyo7hZRA6xFMEVbmYbzt0sj0djV
|
|
||||||
ZOey2TOFrTCpkHfUUDgKvk5sn+F3u8mmPIbqquEzlFJSFjcyiYYDv22rg1In+zKV
|
|
||||||
Xmw4BFZRDS6IVSQRGlskRGJBixCaGyDYxHXXT2cg4Rk9uiCX11+0E9qlAsg6xPe6
|
|
||||||
rnaYDT8dU0AFyVpDpshflXH3kVQSpiqZS3jkAk1/54ODO8pE80Zrnd5m5AMuNcmX
|
|
||||||
+9MkZKE+h0882UskDs1dyt26GU2hoy4lAeRUaut7zIK/WO6nnuLaTvGWT95RDz+q
|
|
||||||
kD2JAiIEEwEKAAwFAleu2iYFgweGH4AACgkQZnuGbqgkCgnmCA//U22uhyEC/Tp3
|
|
||||||
Cbt5lctQmqbgMbjRBaHQyW52tPFMaq8vXMbo/5TTtVC6xsp2PJT84cxAd8KX8hWq
|
|
||||||
cPtF4wWCJGng/AzyxQ5dWfGvA/ll32ygjtJN3P/AvA9KlhG+6XYmS8cPkBkJBi6B
|
|
||||||
2yCdZT1cXc/TPAFzjgAwz7K9g3awG0OeOc/CXymH0DD/snkiwKQoucStolYywZGc
|
|
||||||
GszjMQgeT4zOc1wtEz24uL3dMNDlDcQMAh56YvK2oB0iMYmAFyX/IS+f2bM9paXi
|
|
||||||
HX+mg/z53iwgf5ZXbslNDbMTJ5GNksjEGjCFfDHAdNdgT+lcW4l2U7q4PYUaN4LA
|
|
||||||
DE9j2OlOlQ9qjucOgoCStirnTP7XHd4p31lgdz8+THOQowB5Ji95OkiNQAFCfxBt
|
|
||||||
mcA/bWnJZQDm7L8RVzHovBpAaK6vUjxEvR+DXdESSzyZwkpsZwGZcyqGRT26R1/L
|
|
||||||
JE5WvjKufNc5v3Cat320MjyrLZwVGRgvEpDMoCw3nTWl9AtOj5vgaakEWr7AnqET
|
|
||||||
xk7UFbYmdTlQqkWuLKubz9Rx/FbrBmvd6vwTHy1Dfl6QyMWNCClatgN00Hxped/6
|
|
||||||
CErg+R/RXd8apGxnOuWDqoujPn5LOHzgJolp1Ox16nTiZe2G+LbDr3hqRFi1wW6w
|
|
||||||
ioMB4KpkdA03uyxJSWmDEMiR1l3Oxom0KUNyeXB0b21hdG9yIFByZXNzIDxwcmVz
|
|
||||||
c0BjcnlwdG9tYXRvci5vcmc+iQI9BBMBCgAnBQJXrtnDAhsDBQkHKygABQsJCAcD
|
|
||||||
BRUKCQgLBRYCAwEAAh4BAheAAAoJECO12++U1NgdQYMQAKCIzNJF8rURQcFLSv3J
|
|
||||||
sPBjRy2HCzCWm21MuhU+bsaZx7U9M9dgEjzLfxN9s19VsBH3WKLgok2FgiYSGka3
|
|
||||||
6Oy/P8VFLFmHs7dS9i2fro2eF7i4zj/ZD/9t0jM4ZIgLpbzr5sTBld292nsfXGob
|
|
||||||
xOJeOx3oWYyR2FO9VQxXjC3JvJyZkFgoy0tauS4Mvii4cF56wJGcxDTbe1s7UaRC
|
|
||||||
a/fh4zgISZSBE3rYhCawkN4mqMDM5RDjrdtjKUPWk345HcjjQ4Wos8xw4YbGbNr9
|
|
||||||
Pc7m2URYJJ0jFM4tnoRF6cmA3bT9tm8pcOFg+K/ycVrltVEy+A8Wj8UGjyP1uI1t
|
|
||||||
EqWHN3LZpIGfW0w9AGrw7OUI9czXcukfngj/DsOU3WMBDIM8pW9+zBpr75yIS6lz
|
|
||||||
C0IqksLXSqX0b/Rby4O+wb6UZ1ZFkaim2GGtAZV+nGXtdnEXSNFiP7ykzjZ02m/1
|
|
||||||
7CKyj3VmdAgT56zEIypFSfxm9gOWsJPmfhSyuE8bFyoitgNxpheZk6xZy4upVMPR
|
|
||||||
WK3hutScU0yDv2HVCiA3o3Ggy42nmz9HpGF6W2DmBx4bhMaVs6I2VFyKdQzmJD/3
|
|
||||||
FCWjwz8PiEgVGHGPnD+WdPFLhrc/44gF4h/VuLjkubtULGuTVvgjeTIJ5LR1Gmwc
|
|
||||||
YOk6eD7MAJPzJVj5/PYFtIbKiQIiBBMBCgAMBQJXrtonBYMHhh+AAAoJEGZ7hm6o
|
|
||||||
JAoJBh4P/1w88YMTKUHpFTfJEwH2hK36BZN96Bf/k+vP7n1Xxp3NheInJblHFOt/
|
|
||||||
ccsup6am+APrk8gGtlIVmtVc3nO8WMsWxfJxGDecyRsNbessnODv/llyg3tzVU/H
|
|
||||||
tLk7gLiK0TcIsOLfeNXGTxRRSKWjVFsNfuixNCzzHa7tFq6ddVn9VRZ8fqJB2p21
|
|
||||||
OogWSDqUo9q9Wfb4RkYHguDx+8Jzoo/MxR1TSt8gUO2xDvEbqgeQiMCLF8R0lO3Y
|
|
||||||
zz0FrpyOsFU1CxVp+wo55bWv1UdwgQKQt4o0m5/zDJ2RAtscXpd4YcTE+XxKeK+4
|
|
||||||
qhihhkhLGpKsxzK5m9/qwMbodHwoBCBzfalkUR9xOq9yQIeEoC8XYL62NqB3BCSU
|
|
||||||
KfWFIHxUkE9WH5zHWaV+bhrlNgk7nz3xBfPf1P2mNIc1VUHoNqOZOmWwz2VaKLSW
|
|
||||||
f3GIqx9wGythFbLdXmUoC3W//DDYgQnvImvkncMqQ5nRHPf8uHcLQK5WZyIxpgWT
|
|
||||||
eKon5G/cj0BTptcBhapMwSIyfaC5FV7so0/CkOA6R9Fyq2VpGoHy7XPhFS+6ieLi
|
|
||||||
KUWhCvbuf2deWbSaJ0peMdzy1p72UXwrsEM0M3Fz+Jd8zvCaFzf5Fx27+pAAdlfg
|
|
||||||
4bT3/2gSf7S+cU3+DnYOH0NeRt2Z2mjEKg9OwttTO/oDboQHdZlrtDRDcnlwdG9t
|
|
||||||
YXRvciBTZWN1cml0eS1UZWFtIDxzZWN1cml0eUBjcnlwdG9tYXRvci5vcmc+iQI9
|
|
||||||
BBMBCgAnBQJXrtnWAhsDBQkHKygABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJ
|
|
||||||
ECO12++U1NgddzoQAI78+Nvm6VvNuptXJjEmrpHRyKCnHF9wH5kxvF8WZCgpOkJ4
|
|
||||||
vONmyS+9ZlepnT83MpGm/MzdIMCnDJmDmqmA5ISBRcD7k9Gjzz5rPKwE2zDyo0M0
|
|
||||||
wF1L2UEUqAlcvE0e4twZcP2DGoNqdSf6IaWsXhQMb1a/rTMsoGZLuTB8kCbv6Ntl
|
|
||||||
ULahcRToTB2shsbZjzE896P6X5hDCfGWl0Jhcbf53pnXX1dOsEw3et9AGru1IUMs
|
|
||||||
UGM+wpgTwagRj+XB/WY1x9IznKtiHTq83Fvt+3bkg0+NIcV3GDqXDIUtqIwy8gDd
|
|
||||||
4KgBU+LkyxXFDa4OxLc53n6b+Iy1nDosM+SiqSzdCCgEs/dY1tQBn/7P1GT18dEe
|
|
||||||
tFgeH/c6wLvEpDIc9urAsYXf8H+1uy7glWpWTq8DE0yhCr4adjCqlIsVHQQO4UUW
|
|
||||||
NfqMGEFpJ+3HjSSwnvDGY78lLQh5d4vqWV435aNaMqZg0gJIA0FtiP1fRtmT73BG
|
|
||||||
N/tBNiBxretFR4B+x/TWqPd5iJV7/MAn/pa1WSOcaxzJrVUsjXdgLQCqcHWd4/w1
|
|
||||||
f4DU9cJjl3sxZlMdAlg8Q1bF+pmjQQ4WKZkqMtwpoUilfVXmL42ay1LBCgW68/uJ
|
|
||||||
OTyGfp8ntUsbbm5raGsny3TLqnacyG9hxcPGNTzD1+MrbUvfsc7+4U0dCZTuiQIi
|
|
||||||
BBMBCgAMBQJXrtonBYMHhh+AAAoJEGZ7hm6oJAoJ1DQP/R+1drZiZQU45ChMbfTb
|
|
||||||
XQjJRsUOGZp3PTWtx4KrVFvE8ea0PF+DZX5gLJYIU+iZmPXRpzFu6dKPbcZ7RfRt
|
|
||||||
5RRH102zDZzijt2CQd7YLO8wxUFoWX9X7DGgxXEcNjl9kFVmnyHgiTwTzuZ0Zy4y
|
|
||||||
PvoiwrhcZmXEYbOeV40gLFie6wuzz5IIcs01e30xIs+1/1gwmgI5UnG3jveUgmcj
|
|
||||||
f/lvg3POKiwrY5Uzw1FSruJx21X06wTpDcfOACID4L7aY9eg2B/qL2Xj8nuhejqG
|
|
||||||
+1AVTMk2o6pxkvevHmxYQfEpuWGCw0RCBn9ObWwz6Zn5J9pjGbMrM+b1/M2Ouv3N
|
|
||||||
cpoGgCSahKNsRMKO7RMrBG0jtLcasPSgZFYPJSZAAb+YhxKUbpPHzDIwTEjgM7CL
|
|
||||||
gKSyRTKyp5IoFK53bpXL/ZIjkAhMvyDult6+BL6vI0+h3BBA9I0FF2Qhe139xLv/
|
|
||||||
DS7aDiYAE9vGMGoeCBfxJPwUsDU3hrGe/wgL7fR6nmN7R2QffisBHKHsklORy9t3
|
|
||||||
w3YFRd5sBAxv+EOcdkgXEmqKOfVQ8KU9adQcxPDGMAK/esjVwxUxsaf2PF5noxxW
|
|
||||||
3zL2ureUO/mMoH5Cwr0BuM3HFb82t1JJd4IXlLEyNvDMFMwD2d7h37bGK7Y5hEsl
|
|
||||||
zL7Dm+wQRY8sxg4QOZHbJjQXuQINBFbgeicBEADnkxGSEL1zwACaiVqADKC6/pgO
|
|
||||||
MMWjxoENBT6r8Vnp1D5hfNDkEi9iXUpCEO6nzywBf3/4c4Yk1wBOBZ7YWyWXMf4v
|
|
||||||
2g1evxELO5z1UlAwna6HSl7G0omIBqzz1Er5IS7C9WEZM8ZggwcuswCrbxfz4+fN
|
|
||||||
t7cCL5QyOvuxez+vrn+VIgLQzKm+LV4Wc+OFbHIys+0saQUhItKO0/CsXGc8R314
|
|
||||||
jdN5UsZk/MUdPPAs+6OCr8d3PpJvR6IST76TtN8aDjSS9T6em7dwdGFEwCGww3Jc
|
|
||||||
xrAkvvUmSlscz+rnvHA5DYQGK6NXLenB40sVQVfch1r1VqwvlzA0u7OovjwM8+7u
|
|
||||||
+DaBQ0YejbdnC7yfeE91LmZkG6jRKfvTJkv18tjNsgZsVmM13xzP67fCFIB9M+lN
|
|
||||||
t9zEldGKHVwm+06FHIWJsBDRgrquNb9xd1vgHHeIbJvKf+LqZhVrbKVEneG34Km+
|
|
||||||
ndtb+mvcGc0fOoMU9lYrFaxAWl8oU9BchC9IyjcPZB445R+AhfTuoHSUViSCo6IO
|
|
||||||
TG0hQsJuNoKmDAU8l5sTsiFXuXBOo1wK8gTkRnhZHduZrZIjJXvT7efz1knLQ6eG
|
|
||||||
prZHf4CtbgHyAe2XZabetWtCsFcPbOjC7ezNK57UvVH98h2GkckxOM00BESMCTee
|
|
||||||
kYy7uG0v0rrajzHY1wARAQABiQIlBBgBCgAPBQJW4HonAhsMBQkHKygAAAoJECO1
|
|
||||||
2++U1NgdyAsQAKZUVA6pY225BASkeNiW31L7K4VeRYpAdFkiRex2zQFtj9Vovfi1
|
|
||||||
JeTs0fRm35dUsQraf1bkhsjEdPVZ3gD324/baauFO04KX+soyQvK/tUq8KO+5ALt
|
|
||||||
Ul5aAljuSwxfJWFpApv+Mbf7gOjm+77jirs7pgG/gCow/mkRlmKTwAmn2DXjkckC
|
|
||||||
2EH0mqmh5pdoNWKO7WeTFFbUmESsPcnB2FwTpEjHFvgHll+rmKpXZTgFYN4dDhhm
|
|
||||||
HsL/SCf/Nw+YIsuvErQ9TJVdJDLG8ZYatruk7dZZMPtFxvxM1Q36gDIpPEOKPkvm
|
|
||||||
dMXg6jHaIdYIaoMpzXFaXsQMdRuMtzbcA+CdwXVY55qGLtfmM/QuEiIJdDeeh7iB
|
|
||||||
+VAMyEFOOpi8IFhixaeMoZAmrKDqOkzPcMJVklLYq8N+b9p5JszYNwZEbpyWCACM
|
|
||||||
6K+iJzlWzW/OPZttGLJBgYuSYIJIuG80Cx5m5m1e5RAgQ1iT8nbfrS+gYttwP48J
|
|
||||||
V7SXQg7QugxG9l1vlK4VjnXiOFulJ7V0e/VyUBpJp3qHcCxFq3RnxVwlIqKZh+jm
|
|
||||||
Q1bk0H0Xodd27nQITfDP5ullByGW2Jrjs6SsXeR3jl9+t0XQfInU1L9d/wSOkMjL
|
|
||||||
9IMUt06lV4vB/WP2xioqLZiZ4eAi0E+lWkFxjZsgNs2xbOAYRThMB8a5
|
|
||||||
=W1Ri
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|||||||
13
.github/no-response.yml
vendored
13
.github/no-response.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
# Configuration for probot-no-response - https://github.com/probot/no-response
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue is closed for lack of response
|
|
||||||
daysUntilClose: 14
|
|
||||||
# Label requiring a response
|
|
||||||
responseRequiredLabel: state:awaiting-response
|
|
||||||
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
|
|
||||||
closeComment: >
|
|
||||||
This issue has been automatically closed because there has been no response
|
|
||||||
to our request for more information from the original author. With only the
|
|
||||||
information that is currently in the issue, we don't have enough information
|
|
||||||
to take action. Please reach out if you have or find the answers we need so
|
|
||||||
that we can investigate further.
|
|
||||||
29
.github/release.yml
vendored
Normal file
29
.github/release.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# .github/release.yml
|
||||||
|
# see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
authors:
|
||||||
|
- cryptobot
|
||||||
|
- dependabot
|
||||||
|
- github-actions
|
||||||
|
categories:
|
||||||
|
- title: What's New 🎉
|
||||||
|
labels:
|
||||||
|
- type:feature-request
|
||||||
|
- type:enhancement
|
||||||
|
- title: Bugfixes 🐛
|
||||||
|
labels:
|
||||||
|
- type:security-issue
|
||||||
|
- type:bug
|
||||||
|
- type:minor-bug
|
||||||
|
- title: Other Changes 📎
|
||||||
|
labels:
|
||||||
|
- "*"
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- type:feature-request
|
||||||
|
- type:enhancement
|
||||||
|
- type:security-issue
|
||||||
|
- type:bug
|
||||||
|
- type:minor-bug
|
||||||
24
.github/stale.yml
vendored
24
.github/stale.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
|
||||||
daysUntilStale: 365
|
|
||||||
# Number of days of inactivity before a stale issue is closed
|
|
||||||
daysUntilClose: 90
|
|
||||||
# Issues with these labels will never be considered stale
|
|
||||||
exemptLabels:
|
|
||||||
- type:security-issue # never close automatically
|
|
||||||
- type:feature-request # never close automatically
|
|
||||||
- type:enhancement # never close automatically
|
|
||||||
- type:upstream-bug # never close automatically
|
|
||||||
- state:awaiting-response # handled by different bot
|
|
||||||
- state:blocked
|
|
||||||
- state:confirmed
|
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
|
||||||
exemptMilestones: true
|
|
||||||
# Label to use when marking an issue as stale
|
|
||||||
staleLabel: state:stale
|
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions.
|
|
||||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
|
||||||
closeComment: false
|
|
||||||
15
.github/workflows/appimage.yml
vendored
15
.github/workflows/appimage.yml
vendored
@@ -10,7 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -85,12 +85,13 @@ jobs:
|
|||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=false"
|
--java-options "-Dcryptomator.showTrayIcon=false"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
||||||
|
|||||||
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -53,4 +53,9 @@ jobs:
|
|||||||
body: |-
|
body: |-
|
||||||
:construction: Work in Progress
|
:construction: Work in Progress
|
||||||
|
|
||||||
|
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
|
||||||
|
|
||||||
|
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
<!-- Don't forget to include the 💾 SHA-256 checksums of release artifacts: -->
|
||||||
23
.github/workflows/debian.yml
vendored
23
.github/workflows/debian.yml
vendored
@@ -3,9 +3,6 @@ name: Build Debian Package
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
ref:
|
|
||||||
description: 'GitHub Ref (e.g. refs/tags/1.6.16)'
|
|
||||||
required: true
|
|
||||||
semver:
|
semver:
|
||||||
description: 'SemVer String (e.g. 1.7.0-beta1)'
|
description: 'SemVer String (e.g. 1.7.0-beta1)'
|
||||||
required: true
|
required: true
|
||||||
@@ -19,9 +16,9 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-aarch64_bin-jmods.zip'
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-aarch64_bin-jmods.zip'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -29,9 +26,6 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
|
||||||
ref: ${{ inputs.ref }}
|
|
||||||
fetch-depth: 0
|
|
||||||
- id: versions
|
- id: versions
|
||||||
name: Get version information
|
name: Get version information
|
||||||
run: |
|
run: |
|
||||||
@@ -45,7 +39,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0
|
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
@@ -141,15 +135,10 @@ jobs:
|
|||||||
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
|
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
|
||||||
|
|
||||||
# If ref is a tag, also upload to GitHub Releases:
|
# If ref is a tag, also upload to GitHub Releases:
|
||||||
- name: Determine tag name
|
|
||||||
if: startsWith(inputs.ref, 'refs/tags/')
|
|
||||||
run: |
|
|
||||||
REF=${{ inputs.ref }}
|
|
||||||
echo "TAG_NAME=${REF##*/}" >> $GITHUB_ENV
|
|
||||||
- name: Publish Debian package on GitHub Releases
|
- name: Publish Debian package on GitHub Releases
|
||||||
if: startsWith(inputs.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
artifacts=$(ls | grep cryptomator*.deb)
|
artifacts=$(ls | grep cryptomator*.deb)
|
||||||
gh release upload ${{ env.TAG_NAME }} $artifacts
|
gh release upload ${{ github.ref_name }} $artifacts
|
||||||
2
.github/workflows/get-version.yml
vendored
2
.github/workflows/get-version.yml
vendored
@@ -22,7 +22,7 @@ on:
|
|||||||
value: ${{ jobs.determine-version.outputs.type }}
|
value: ${{ jobs.determine-version.outputs.type }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_CACHE: 'maven'
|
JAVA_CACHE: 'maven'
|
||||||
|
|
||||||
|
|||||||
15
.github/workflows/mac-dmg.yml
vendored
15
.github/workflows/mac-dmg.yml
vendored
@@ -10,7 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -95,16 +95,17 @@ jobs:
|
|||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=true"
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--mac-package-identifier org.cryptomator
|
--mac-package-identifier org.cryptomator
|
||||||
|
|||||||
22
.github/workflows/no-response.yml
vendored
Normal file
22
.github/workflows/no-response.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
|
||||||
|
|
||||||
|
name: 'Close awaiting response issues'
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '00 09 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
no-response:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v8
|
||||||
|
with:
|
||||||
|
days-before-stale: 14
|
||||||
|
days-before-close: 0
|
||||||
|
days-before-pr-close: -1
|
||||||
|
stale-issue-label: 'state:stale'
|
||||||
|
close-issue-message: "This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further."
|
||||||
|
only-labels: 'state:awaiting-response'
|
||||||
2
.github/workflows/post-publish.yml
vendored
2
.github/workflows/post-publish.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Download source tarball
|
- name: Download source tarball
|
||||||
run: |
|
run: |
|
||||||
curl -L -H "Accept: application/vnd.github+json" ${{ github.event.release.tarball_url }} --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
|
curl -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
|
||||||
- name: Sign source tarball with key 615D449FE6E6A235
|
- name: Sign source tarball with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
|
|||||||
2
.github/workflows/pullrequest.yml
vendored
2
.github/workflows/pullrequest.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
2
.github/workflows/release-check.yml
vendored
2
.github/workflows/release-check.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
- 'hotfix/**'
|
- 'hotfix/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
24
.github/workflows/stale.yml
vendored
Normal file
24
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
|
||||||
|
|
||||||
|
name: 'Close stale issues'
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '00 09 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v8
|
||||||
|
with:
|
||||||
|
days-before-stale: 365
|
||||||
|
days-before-close: 90
|
||||||
|
exempt-issue-labels: 'type:security-issue,type:feature-request,type:enhancement,type:upstream-bug,state:awaiting-response,state:blocked,state:confirmed'
|
||||||
|
exempt-all-milestones: true
|
||||||
|
stale-issue-label: 'state:stale'
|
||||||
|
stale-pr-label: 'state:stale'
|
||||||
|
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
|
||||||
|
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
|
||||||
72
.github/workflows/win-exe.yml
vendored
72
.github/workflows/win-exe.yml
vendored
@@ -8,11 +8,17 @@ on:
|
|||||||
version:
|
version:
|
||||||
description: 'Version'
|
description: 'Version'
|
||||||
required: false
|
required: false
|
||||||
|
isDebug:
|
||||||
|
description: 'Build debug version with console output'
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_VERSION: 19
|
JAVA_VERSION: 20
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_CACHE: 'maven'
|
JAVA_CACHE: 'maven'
|
||||||
|
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip'
|
||||||
|
JFX_JMODS_HASH: 'D00767334C43B8832B5CF10267D34CA8F563D187C4655B73EB6020DD79C054B5'
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -30,6 +36,7 @@ jobs:
|
|||||||
needs: [get-version]
|
needs: [get-version]
|
||||||
env:
|
env:
|
||||||
LOOPBACK_ALIAS: 'cryptomator-vault'
|
LOOPBACK_ALIAS: 'cryptomator-vault'
|
||||||
|
WIN_CONSOLE_FLAG: ''
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -37,17 +44,31 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
java-package: 'jdk+fx'
|
java-package: 'jdk'
|
||||||
cache: ${{ env.JAVA_CACHE }}
|
cache: ${{ env.JAVA_CACHE }}
|
||||||
- name: Ensure major jfx version in pom equals in jdk
|
- name: Download and extract JavaFX jmods from Gluon
|
||||||
shell: pwsh
|
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||||
run: |
|
run: |
|
||||||
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
|
curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
|
||||||
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
|
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
|
||||||
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
|
throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}.";
|
||||||
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
|
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
|
||||||
|
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
|
||||||
|
shell: pwsh
|
||||||
|
- name: Ensure major jfx version in pom and in jmods is the same
|
||||||
|
run: |
|
||||||
|
JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1)
|
||||||
|
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
|
||||||
|
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
|
||||||
|
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
|
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
|
||||||
|
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
@@ -61,13 +82,16 @@ jobs:
|
|||||||
${JAVA_HOME}/bin/jlink
|
${JAVA_HOME}/bin/jlink
|
||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods"
|
--module-path "jfxjmods;${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress=1
|
--compress=1
|
||||||
|
- name: Change win-console flag if debug is active
|
||||||
|
if: ${{ inputs.isDebug }}
|
||||||
|
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -88,19 +112,22 @@ jobs:
|
|||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
|
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
|
--java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
||||||
|
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--icon dist/win/resources/Cryptomator.ico
|
--icon dist/win/resources/Cryptomator.ico
|
||||||
|
${WIN_CONSOLE_FLAG}
|
||||||
- name: Patch Application Directory
|
- name: Patch Application Directory
|
||||||
run: |
|
run: |
|
||||||
cp dist/win/contrib/* appdir/Cryptomator
|
cp dist/win/contrib/* appdir/Cryptomator
|
||||||
@@ -203,15 +230,6 @@ jobs:
|
|||||||
*.msi
|
*.msi
|
||||||
*.asc
|
*.asc
|
||||||
|
|
||||||
call-winget-flow:
|
|
||||||
needs: [get-version, build-msi]
|
|
||||||
if: github.event.action == 'published' && needs.get-version.outputs.versionType == 'stable'
|
|
||||||
uses: ./.github/workflows/winget.yml
|
|
||||||
with:
|
|
||||||
releaseTag: ${{ github.event.release.tag_name }}
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
|
|
||||||
build-exe:
|
build-exe:
|
||||||
name: Build .exe installer
|
name: Build .exe installer
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|||||||
49
.github/workflows/winget.yml
vendored
49
.github/workflows/winget.yml
vendored
@@ -1,49 +0,0 @@
|
|||||||
name: Release to Winget
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
releaseTag:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
releaseTag:
|
|
||||||
description: 'Release tag name'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-winget:
|
|
||||||
name: Publish on winget repo
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Get download url for release assets
|
|
||||||
id: get-release-assets
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const query =`query($tag:String!) {
|
|
||||||
repository(owner:"cryptomator", name:"cryptomator"){
|
|
||||||
release(tagName: $tag) {
|
|
||||||
releaseAssets(first:20) {
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
downloadUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
const variables = {
|
|
||||||
tag: "${{ inputs.releaseTag }}"
|
|
||||||
}
|
|
||||||
return await github.graphql(query, variables)
|
|
||||||
- name: Submit package to Windows Package Manager Community Repository
|
|
||||||
id: submit-winget
|
|
||||||
run: |
|
|
||||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
|
||||||
$releaseAssets = (ConvertFrom-Json '${{ steps.get-release-assets.outputs.result }}').repository.release.releaseAssets.nodes
|
|
||||||
$installerUrl = $releaseAssets | Where-Object -Property name -match '^Cryptomator-.*\.msi$' | Select -ExpandProperty downloadUrl -First 1
|
|
||||||
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v "${{ inputs.releaseTag }}" -u "$installerUrl" -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
|
||||||
shell: pwsh
|
|
||||||
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@@ -53,9 +53,10 @@
|
|||||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||||
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||||
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||||
|
<option name="IF_BRACE_FORCE" value="3" />
|
||||||
<option name="ENUM_CONSTANTS_WRAP" value="2" />
|
<option name="ENUM_CONSTANTS_WRAP" value="2" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="USE_TAB_CHARACTER" value="true" />
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
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">
|
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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 --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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 --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</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">
|
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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 --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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 --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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.mountPointsDir="~/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
|
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<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.mountPointsDir="~/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
|
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td>
|
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td>
|
||||||
<td><a href="https://proxy-hub.com/"><img src="https://cryptomator.org/img/sponsors/proxyhub.svg" alt="Proxy-Hub" height="80"></a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
13
dist/linux/appimage/build.sh
vendored
13
dist/linux/appimage/build.sh
vendored
@@ -50,12 +50,13 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=false" \
|
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||||
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
||||||
|
|||||||
@@ -66,6 +66,13 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2023-06-07" version="1.9.1"/>
|
||||||
|
<release date="2023-05-30" version="1.9.0"/>
|
||||||
|
<release date="2023-04-25" version="1.8.0"/>
|
||||||
|
<release date="2023-04-07" version="1.7.5"/>
|
||||||
|
<release date="2023-04-05" version="1.7.4"/>
|
||||||
|
<release date="2023-03-15" version="1.7.3"/>
|
||||||
|
<release date="2023-03-07" version="1.7.2"/>
|
||||||
<release date="2023-03-03" version="1.7.1"/>
|
<release date="2023-03-03" version="1.7.1"/>
|
||||||
<release date="2023-03-01" version="1.7.0"/>
|
<release date="2023-03-01" version="1.7.0"/>
|
||||||
<release date="2022-12-14" version="1.6.17"/>
|
<release date="2022-12-14" version="1.6.17"/>
|
||||||
|
|||||||
2
dist/linux/debian/control
vendored
2
dist/linux/debian/control
vendored
@@ -2,7 +2,7 @@ Source: cryptomator
|
|||||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-19, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
|
|||||||
15
dist/linux/debian/rules
vendored
15
dist/linux/debian/rules
vendored
@@ -4,7 +4,7 @@
|
|||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
|
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs
|
||||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||||
@@ -48,12 +48,13 @@ override_dh_auto_build:
|
|||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=false" \
|
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
||||||
|
|||||||
13
dist/mac/dmg/build.sh
vendored
13
dist/mac/dmg/build.sh
vendored
@@ -71,16 +71,17 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
||||||
--java-options "-Dsun.java2d.metal=true" \
|
--java-options "-Dsun.java2d.metal=true" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
|
||||||
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/${APP_NAME}/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
|
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"~/${APP_NAME}\"" \
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||||
|
|||||||
43
dist/win/build.ps1
vendored
43
dist/win/build.ps1
vendored
@@ -12,6 +12,9 @@ Param(
|
|||||||
[bool] $clean
|
[bool] $clean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
|
||||||
|
|
||||||
# check preconditions
|
# check preconditions
|
||||||
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
|
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
|
||||||
{
|
{
|
||||||
@@ -26,7 +29,7 @@ if ((Get-Command "mvn" -ErrorAction SilentlyContinue) -eq $null)
|
|||||||
|
|
||||||
$buildDir = Split-Path -Parent $PSCommandPath
|
$buildDir = Split-Path -Parent $PSCommandPath
|
||||||
$version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout)
|
$version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout)
|
||||||
$semVerNo = $version -replace '(\d\.\d\.\d).*','$1'
|
$semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1'
|
||||||
$revisionNo = $(git rev-list --count HEAD)
|
$revisionNo = $(git rev-list --count HEAD)
|
||||||
|
|
||||||
Write-Output "`$version=$version"
|
Write-Output "`$version=$version"
|
||||||
@@ -47,11 +50,28 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
|||||||
Remove-Item -Path $runtimeImagePath -Force -Recurse
|
Remove-Item -Path $runtimeImagePath -Force -Recurse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## download jfx jmods
|
||||||
|
$jfxJmodsChecksum = 'd00767334c43b8832b5cf10267d34ca8f563d187c4655b73eb6020dd79c054b5'
|
||||||
|
$jfxJmodsZip = '.\resources\jfxJmods.zip'
|
||||||
|
if( !(Test-Path -Path $jfxJmodsZip) ) {
|
||||||
|
$jmodsUrl = "https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip"
|
||||||
|
Write-Output "Downloading ${jmodsUrl}..."
|
||||||
|
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
|
||||||
|
}
|
||||||
|
|
||||||
|
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
|
||||||
|
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
|
||||||
|
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
Expand-Archive -Force -Path $jfxJmodsZip -DestinationPath ".\resources\"
|
||||||
|
|
||||||
|
|
||||||
& "$Env:JAVA_HOME\bin\jlink" `
|
& "$Env:JAVA_HOME\bin\jlink" `
|
||||||
--verbose `
|
--verbose `
|
||||||
--output runtime `
|
--output runtime `
|
||||||
--module-path "$Env:JAVA_HOME/jmods" `
|
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods-20.0.1" `
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
||||||
--strip-native-commands `
|
--strip-native-commands `
|
||||||
--no-header-files `
|
--no-header-files `
|
||||||
--no-man-pages `
|
--no-man-pages `
|
||||||
@@ -82,15 +102,16 @@ if ($clean -and (Test-Path -Path $appPath)) {
|
|||||||
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
|
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
|
||||||
--app-version "$semVerNo.$revisionNo" `
|
--app-version "$semVerNo.$revisionNo" `
|
||||||
--java-options "-Dfile.encoding=`"utf-8`"" `
|
--java-options "-Dfile.encoding=`"utf-8`"" `
|
||||||
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
|
--java-options "-Djava.net.useSystemProxies=true" `
|
||||||
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
|
--java-options "-Dcryptomator.logDir=`"@{localappdata}/$AppName`"" `
|
||||||
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
|
--java-options "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`"" `
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
|
--java-options "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`"" `
|
||||||
--java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
|
--java-options "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`"" `
|
||||||
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
|
--java-options "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`"" `
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`"" `
|
||||||
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
|
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"~/AppData/Roaming/$AppName/keychain.json`"" `
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$AppName/keychain.json;@{userhome}/AppData/Roaming/$AppName/keychain.json`"" `
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true" `
|
--java-options "-Dcryptomator.showTrayIcon=true" `
|
||||||
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
|
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
|
||||||
--resource-dir resources `
|
--resource-dir resources `
|
||||||
@@ -151,8 +172,6 @@ $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
|||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
||||||
|
|
||||||
# download Winfsp
|
# 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= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||||
Write-Output "Downloading ${winfspMsiUrl}..."
|
Write-Output "Downloading ${winfspMsiUrl}..."
|
||||||
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
||||||
|
|||||||
2
dist/win/bundle/resources/winFspMetaData.wxi
vendored
2
dist/win/bundle/resources/winFspMetaData.wxi
vendored
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
|
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
|
||||||
<?define BundledWinFspVersion="v1.12.22309" ?>
|
<?define BundledWinFspVersion="v1.12.22339" ?>
|
||||||
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
|
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
|
||||||
</Include>
|
</Include>
|
||||||
12
dist/win/resources/main.wxs
vendored
12
dist/win/resources/main.wxs
vendored
@@ -70,6 +70,9 @@
|
|||||||
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
|
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
|
||||||
|
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||||
|
|
||||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||||
<!-- Block installation if innosetup entry of Cryptomator is found -->
|
<!-- Block installation if innosetup entry of Cryptomator is found -->
|
||||||
<Property Id="OLDEXEINSTALLER">
|
<Property Id="OLDEXEINSTALLER">
|
||||||
@@ -172,11 +175,12 @@
|
|||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<?ifndef JpAllowUpgrades ?>
|
<?ifndef JpAllowUpgrades ?>
|
||||||
<Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
|
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
|
||||||
<?endif?>
|
<?endif?>
|
||||||
<?ifndef JpAllowDowngrades ?>
|
<?ifndef JpAllowDowngrades ?>
|
||||||
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
|
|
||||||
<!-- Check and fail if Cryptomator is running -->
|
<!-- Check and fail if Cryptomator is running -->
|
||||||
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
||||||
@@ -188,6 +192,10 @@
|
|||||||
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<InstallUISequence>
|
||||||
|
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
|
</InstallUISequence>
|
||||||
|
|
||||||
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
||||||
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
||||||
</Product>
|
</Product>
|
||||||
|
|||||||
92
pom.xml
92
pom.xml
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptomator</artifactId>
|
<artifactId>cryptomator</artifactId>
|
||||||
<version>1.7.1</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -17,60 +17,58 @@
|
|||||||
<email>sebastian.stenzel@gmail.com</email>
|
<email>sebastian.stenzel@gmail.com</email>
|
||||||
<timezone>+1</timezone>
|
<timezone>+1</timezone>
|
||||||
</developer>
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<name>Armin Schrenk</name>
|
||||||
|
<email>armin.schrenk+dev@mailbox.org</email>
|
||||||
|
<timezone>+1</timezone>
|
||||||
|
</developer>
|
||||||
</developers>
|
</developers>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.jdk.version>19</project.jdk.version>
|
<project.jdk.version>20</project.jdk.version>
|
||||||
|
|
||||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||||
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
|
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
|
||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptolib.version>2.1.1</cryptomator.cryptolib.version>
|
<cryptomator.cryptofs.version>2.6.6</cryptomator.cryptofs.version>
|
||||||
<cryptomator.cryptofs.version>2.6.1</cryptomator.cryptofs.version>
|
|
||||||
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.2.1</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>2.0.2</cryptomator.fuse.version>
|
<cryptomator.fuse.version>3.0.0</cryptomator.fuse.version>
|
||||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||||
<cryptomator.webdav.version>2.0.1</cryptomator.webdav.version>
|
<cryptomator.webdav.version>2.0.3</cryptomator.webdav.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
<dagger.version>2.45</dagger.version>
|
<dagger.version>2.45</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<guava.version>31.1-jre</guava.version>
|
<guava.version>32.0.1-jre</guava.version>
|
||||||
<gson.version>2.10.1</gson.version>
|
<jackson.version>2.15.2</jackson.version>
|
||||||
<javafx.version>19.0.2.1</javafx.version>
|
<javafx.version>20.0.1</javafx.version>
|
||||||
<jwt.version>4.3.0</jwt.version>
|
<jwt.version>4.4.0</jwt.version>
|
||||||
<nimbus-jose.version>9.31</nimbus-jose.version>
|
<nimbus-jose.version>9.31</nimbus-jose.version>
|
||||||
<logback.version>1.4.5</logback.version>
|
<logback.version>1.4.7</logback.version>
|
||||||
<slf4j.version>2.0.6</slf4j.version>
|
<slf4j.version>2.0.7</slf4j.version>
|
||||||
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.7.0</zxcvbn.version>
|
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.9.2</junit.jupiter.version>
|
<junit.jupiter.version>5.9.3</junit.jupiter.version>
|
||||||
<mockito.version>5.1.1</mockito.version>
|
<mockito.version>5.3.1</mockito.version>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>2.2</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
||||||
<dependency-check.version>8.1.0</dependency-check.version>
|
<dependency-check.version>8.1.2</dependency-check.version>
|
||||||
<jacoco.version>0.8.8</jacoco.version>
|
<jacoco.version>0.8.9</jacoco.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Cryptomator Libs -->
|
<!-- Cryptomator Libs -->
|
||||||
<dependency>
|
|
||||||
<!-- needed due to https://github.com/cryptomator/cryptolib/issues/34 -->
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>cryptolib</artifactId>
|
|
||||||
<version>${cryptomator.cryptolib.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptofs</artifactId>
|
<artifactId>cryptofs</artifactId>
|
||||||
@@ -159,6 +157,11 @@
|
|||||||
<artifactId>nimbus-jose-jwt</artifactId>
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
<version>${nimbus-jose.version}</version>
|
<version>${nimbus-jose.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- EasyBind -->
|
<!-- EasyBind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -208,11 +211,6 @@
|
|||||||
<artifactId>dagger</artifactId>
|
<artifactId>dagger</artifactId>
|
||||||
<version>${dagger.version}</version>
|
<version>${dagger.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<version>${gson.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -319,41 +317,6 @@
|
|||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>compile-light-theme</id>
|
|
||||||
<phase>compile</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>java</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
|
|
||||||
<arguments>
|
|
||||||
<arg>${project.basedir}/src/main/resources/css/light_theme.css</arg>
|
|
||||||
<arg>${project.build.outputDirectory}/css/light_theme.bss</arg>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>compile-dark-theme</id>
|
|
||||||
<phase>compile</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>java</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
|
|
||||||
<arguments>
|
|
||||||
<arg>${project.basedir}/src/main/resources/css/dark_theme.css</arg>
|
|
||||||
<arg>${project.build.outputDirectory}/css/dark_theme.bss</arg>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
@@ -369,6 +332,9 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<argLine>--enable-preview</argLine>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
import ch.qos.logback.classic.spi.Configurator;
|
import ch.qos.logback.classic.spi.Configurator;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.GoogleDriveLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.LeitzcloudLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||||
@@ -25,7 +38,7 @@ open module org.cryptomator.desktop {
|
|||||||
requires ch.qos.logback.core;
|
requires ch.qos.logback.core;
|
||||||
requires com.auth0.jwt;
|
requires com.auth0.jwt;
|
||||||
requires com.google.common;
|
requires com.google.common;
|
||||||
requires com.google.gson;
|
requires com.fasterxml.jackson.databind;
|
||||||
requires com.nimbusds.jose.jwt;
|
requires com.nimbusds.jose.jwt;
|
||||||
requires com.nulabinc.zxcvbn;
|
requires com.nulabinc.zxcvbn;
|
||||||
requires com.tobiasdiez.easybind;
|
requires com.tobiasdiez.easybind;
|
||||||
@@ -37,6 +50,16 @@ open module org.cryptomator.desktop {
|
|||||||
/* TODO: filename-based modules: */
|
/* TODO: filename-based modules: */
|
||||||
requires static javax.inject; /* ugly dagger/guava crap */
|
requires static javax.inject; /* ugly dagger/guava crap */
|
||||||
|
|
||||||
|
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
|
||||||
provides TrayMenuController with AwtTrayMenuController;
|
provides TrayMenuController with AwtTrayMenuController;
|
||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
|
provides LocationPresetsProvider with //
|
||||||
|
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
||||||
|
GoogleDriveLocationPresetsProvider, //
|
||||||
|
ICloudWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
|
||||||
|
LeitzcloudLocationPresetsProvider, //
|
||||||
|
MegaLocationPresetsProvider, //
|
||||||
|
OneDriveWindowsLocationPresetsProvider, OneDriveMacLocationPresetsProvider, OneDriveLinuxLocationPresetsProvider, //
|
||||||
|
PCloudLocationPresetsProvider;
|
||||||
}
|
}
|
||||||
@@ -139,9 +139,9 @@ public abstract class CommonsModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
||||||
return settings.port().map(port -> {
|
return settings.port.map(port -> {
|
||||||
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
||||||
return InetSocketAddress.createUnresolved(host, settings.port().intValue());
|
return InetSocketAddress.createUnresolved(host, settings.port.intValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
public interface Constants {
|
public interface Constants {
|
||||||
|
|
||||||
String MASTERKEY_FILENAME = "masterkey.cryptomator";
|
String MASTERKEY_FILENAME = "masterkey.cryptomator";
|
||||||
@@ -7,6 +11,7 @@ public interface Constants {
|
|||||||
String VAULTCONFIG_FILENAME = "vault.cryptomator";
|
String VAULTCONFIG_FILENAME = "vault.cryptomator";
|
||||||
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
|
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
|
||||||
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
||||||
|
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
|
||||||
byte[] PEPPER = new byte[0];
|
byte[] PEPPER = new byte[0];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import java.util.stream.StreamSupport;
|
|||||||
public class Environment {
|
public class Environment {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
||||||
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 int DEFAULT_MIN_PW_LENGTH = 8;
|
||||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
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 IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||||
@@ -80,7 +78,7 @@ public class Environment {
|
|||||||
return getPaths(P12_PATH_PROP_NAME);
|
return getPaths(P12_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<Path> ipcSocketPath() {
|
public Stream<Path> getIpcSocketPath() {
|
||||||
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +87,7 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Path> getLogDir() {
|
public Optional<Path> getLogDir() {
|
||||||
return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
|
return getPath(LOG_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getLoopbackAlias() {
|
public Optional<String> getLoopbackAlias() {
|
||||||
@@ -97,11 +95,11 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Path> getPluginDir() {
|
public Optional<Path> getPluginDir() {
|
||||||
return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
|
return getPath(PLUGIN_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Path> getMountPointsDir() {
|
public Optional<Path> getMountPointsDir() {
|
||||||
return getPath(MOUNTPOINT_DIR_PROP_NAME).map(this::replaceHomeDir);
|
return getPath(MOUNTPOINT_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,22 +129,9 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// visible for testing
|
// visible for testing
|
||||||
public Path getHomeDir() {
|
Stream<Path> getPaths(String propertyName) {
|
||||||
return getPath("user.home").orElseThrow();
|
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
|
||||||
}
|
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
public Stream<Path> getPaths(String propertyName) {
|
|
||||||
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
|
|
||||||
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path replaceHomeDir(Path path) {
|
|
||||||
if (path.startsWith(RELATIVE_HOME_DIR)) {
|
|
||||||
return getHomeDir().resolve(RELATIVE_HOME_DIR.relativize(path));
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<String> getRawList(String propertyName, char separator) {
|
private Stream<String> getRawList(String propertyName, char separator) {
|
||||||
|
|||||||
@@ -32,18 +32,15 @@ public class ErrorCode {
|
|||||||
this.rootCauseSpecificFrames = rootCauseSpecificFrames;
|
this.rootCauseSpecificFrames = rootCauseSpecificFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// visible for testing
|
public String methodCode() {
|
||||||
String methodCode() {
|
|
||||||
return format(traceCode(rootCause, LATEST_FRAME));
|
return format(traceCode(rootCause, LATEST_FRAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
// visible for testing
|
public String rootCauseCode() {
|
||||||
String rootCauseCode() {
|
|
||||||
return format(traceCode(rootCause, rootCauseSpecificFrames));
|
return format(traceCode(rootCause, rootCauseSpecificFrames));
|
||||||
}
|
}
|
||||||
|
|
||||||
// visible for testing
|
public String throwableCode() {
|
||||||
String throwableCode() {
|
|
||||||
return format(traceCode(throwable, ALL_FRAMES));
|
return format(traceCode(throwable, ALL_FRAMES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class LicenseHolder {
|
|||||||
this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject);
|
this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject);
|
||||||
this.validLicenseProperty = validJwtClaims.isNotNull();
|
this.validLicenseProperty = validJwtClaims.isNotNull();
|
||||||
|
|
||||||
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
|
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey.get());
|
||||||
validJwtClaims.set(claims.orElse(null));
|
validJwtClaims.set(claims.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ public class LicenseHolder {
|
|||||||
Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
|
Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
|
||||||
validJwtClaims.set(claims.orElse(null));
|
validJwtClaims.set(claims.orElse(null));
|
||||||
if (claims.isPresent()) {
|
if (claims.isPresent()) {
|
||||||
settings.licenseKey().set(licenseKey);
|
settings.licenseKey.set(licenseKey);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.cryptomator.common;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum of common cloud providers and their default local storage location path.
|
|
||||||
*/
|
|
||||||
public enum LocationPreset {
|
|
||||||
|
|
||||||
DROPBOX("Dropbox", "~/Dropbox"),
|
|
||||||
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
|
|
||||||
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
|
|
||||||
MEGA("MEGA", "~/MEGA"),
|
|
||||||
ONEDRIVE("OneDrive", "~/OneDrive"),
|
|
||||||
PCLOUD("pCloud", "~/pCloudDrive"),
|
|
||||||
|
|
||||||
LOCAL("local");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final List<Path> candidates;
|
|
||||||
|
|
||||||
LocationPreset(String name, String... candidates) {
|
|
||||||
this.name = name;
|
|
||||||
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks for this LocationPreset if any of the associated paths exist.
|
|
||||||
*
|
|
||||||
* @return the first existing path or null, if none exists.
|
|
||||||
*/
|
|
||||||
public Path existingPath() {
|
|
||||||
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getDisplayName();
|
|
||||||
}
|
|
||||||
|
|
||||||
//this contruct is needed, since static members are initialized after every enum member is initialized
|
|
||||||
//TODO: refactor this to normal class and use this also in different parts of the project
|
|
||||||
private static class UserHome {
|
|
||||||
|
|
||||||
private static final String USER_HOME = System.getProperty("user.home");
|
|
||||||
|
|
||||||
private static String resolve(String path) {
|
|
||||||
if (path.startsWith("~/")) {
|
|
||||||
return UserHome.USER_HOME + path.substring(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2,7 +2,9 @@ package org.cryptomator.common;
|
|||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class ObservableUtil {
|
public class ObservableUtil {
|
||||||
|
|
||||||
@@ -15,4 +17,14 @@ public class ObservableUtil {
|
|||||||
}
|
}
|
||||||
}, observable);
|
}, observable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, U> ObservableValue<U> mapWithDefault(ObservableValue<T> observable, Function<? super T, ? extends U> mapper, Supplier<U> defaultValue) {
|
||||||
|
return Bindings.createObjectBinding(() -> {
|
||||||
|
if (observable.getValue() == null) {
|
||||||
|
return defaultValue.get();
|
||||||
|
} else {
|
||||||
|
return mapper.apply(observable.getValue());
|
||||||
|
}
|
||||||
|
}, observable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
171
src/main/java/org/cryptomator/common/PropertiesDecorator.java
Normal file
171
src/main/java/org/cryptomator/common/PropertiesDecorator.java
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.InvalidPropertiesFormatException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
class PropertiesDecorator extends Properties {
|
||||||
|
|
||||||
|
protected final Properties delegate;
|
||||||
|
|
||||||
|
PropertiesDecorator(Properties delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProperty(String key) {return delegate.getProperty(key);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProperty(String key, String defaultValue) {return delegate.getProperty(key, defaultValue);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object setProperty(String key, String value) {
|
||||||
|
return delegate.setProperty(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void load(Reader reader) throws IOException {delegate.load(reader);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void load(InputStream inStream) throws IOException {delegate.load(inStream);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(Writer writer, String comments) throws IOException {delegate.store(writer, comments);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(OutputStream out, @Nullable String comments) throws IOException {delegate.store(out, comments);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException {delegate.loadFromXML(in);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeToXML(OutputStream os, String comment) throws IOException {delegate.storeToXML(os, comment);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeToXML(OutputStream os, String comment, String encoding) throws IOException {delegate.storeToXML(os, comment, encoding);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeToXML(OutputStream os, String comment, Charset charset) throws IOException {delegate.storeToXML(os, comment, charset);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<?> propertyNames() {return delegate.propertyNames();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> stringPropertyNames() {return delegate.stringPropertyNames();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void list(PrintStream out) {delegate.list(out);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void list(PrintWriter out) {delegate.list(out);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {return delegate.size();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {return delegate.isEmpty();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<Object> keys() {return delegate.keys();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<Object> elements() {return delegate.elements();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object value) {return delegate.contains(value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {return delegate.containsValue(value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {return delegate.containsKey(key);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object key) {return delegate.get(key);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object put(Object key, Object value) {return delegate.put(key, value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object remove(Object key) {return delegate.remove(key);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void putAll(Map<?, ?> t) {delegate.putAll(t);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void clear() {delegate.clear();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toString() {return delegate.toString();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Object> keySet() {return delegate.keySet();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Object> values() {return delegate.values();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Map.Entry<Object, Object>> entrySet() {return delegate.entrySet();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean equals(Object o) {return delegate.equals(o);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int hashCode() {return delegate.hashCode();}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getOrDefault(Object key, Object defaultValue) {return delegate.getOrDefault(key, defaultValue);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {delegate.forEach(action);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {delegate.replaceAll(function);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object putIfAbsent(Object key, Object value) {return delegate.putIfAbsent(key, value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean remove(Object key, Object value) {return delegate.remove(key, value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean replace(Object key, Object oldValue, Object newValue) {return delegate.replace(key, oldValue, newValue);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object replace(Object key, Object value) {return delegate.replace(key, value);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) {return delegate.computeIfAbsent(key, mappingFunction);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.computeIfPresent(key, remappingFunction);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.compute(key, remappingFunction);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.merge(key, value, remappingFunction);}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Object clone() {
|
||||||
|
var delegateClone = (Properties) delegate.clone();
|
||||||
|
return new PropertiesDecorator(delegateClone);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class SubstitutingProperties extends PropertiesDecorator {
|
||||||
|
|
||||||
|
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
|
||||||
|
|
||||||
|
private final Map<String, String> env;
|
||||||
|
|
||||||
|
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment) {
|
||||||
|
super(props);
|
||||||
|
this.env = systemEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProperty(String key) {
|
||||||
|
var value = delegate.getProperty(key);
|
||||||
|
if (key.startsWith("cryptomator.") && value != null) {
|
||||||
|
return process(value);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProperty(String key, String defaultValue) {
|
||||||
|
var result = getProperty(key);
|
||||||
|
return result != null ? result : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
String process(String value) {
|
||||||
|
return TEMPLATE.matcher(value).replaceAll(match -> //
|
||||||
|
switch (match.group(1)) {
|
||||||
|
case "appdir" -> resolveFrom("APPDIR", Source.ENV);
|
||||||
|
case "appdata" -> resolveFrom("APPDATA", Source.ENV);
|
||||||
|
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
|
||||||
|
case "userhome" -> resolveFrom("user.home", Source.PROPS);
|
||||||
|
default -> {
|
||||||
|
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value);
|
||||||
|
yield match.group();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveFrom(String key, Source src) {
|
||||||
|
var val = switch (src) {
|
||||||
|
case ENV -> env.get(key);
|
||||||
|
case PROPS -> delegate.getProperty(key);
|
||||||
|
};
|
||||||
|
if (val == null) {
|
||||||
|
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return val.replace("\\", "\\\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Source {
|
||||||
|
ENV,
|
||||||
|
PROPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,14 +23,14 @@ public class KeychainModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
|
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
|
||||||
return Bindings.createObjectBinding(() -> {
|
return Bindings.createObjectBinding(() -> {
|
||||||
if (!settings.useKeychain().get()) {
|
if (!settings.useKeychain.get()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var selectedProviderClass = settings.keychainProvider().get();
|
var selectedProviderClass = settings.keychainProvider.get();
|
||||||
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
|
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
|
||||||
var fallbackProvider = providers.stream().findFirst().orElse(null);
|
var fallbackProvider = providers.stream().findFirst().orElse(null);
|
||||||
return selectedProvider.orElse(fallbackProvider);
|
return selectedProvider.orElse(fallbackProvider);
|
||||||
}, settings.keychainProvider(), settings.useKeychain());
|
}, settings.keychainProvider, settings.useKeychain);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
|
||||||
|
|
||||||
|
@OperatingSystem(LINUX)
|
||||||
|
public final class DropboxLinuxLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path USER_HOME = LocationPresetsProvider.resolveLocation("~/.").toAbsolutePath();
|
||||||
|
private static final Predicate<String> PATTERN = Pattern.compile("Dropbox \\(.+\\)").asMatchPredicate();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
try (var dirStream = Files.list(USER_HOME)) {
|
||||||
|
var presets = dirStream.filter(p -> Files.isDirectory(p) && PATTERN.test(p.getFileName().toString())) //
|
||||||
|
.map(p -> new LocationPreset(p.getFileName().toString(), p)) //
|
||||||
|
.toList();
|
||||||
|
return presets.stream(); //workaround to ensure that the directory stream is always closed
|
||||||
|
} catch (IOException | UncheckedIOException e) { //UncheckedIOException thrown by the stream of Files.list()
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox");
|
||||||
|
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
|
||||||
|
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION) || Files.isDirectory(FALLBACK_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
if(Files.isDirectory(LOCATION)) {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||||
|
} else if(Files.isDirectory(FALLBACK_LOCATION)) {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", FALLBACK_LOCATION));
|
||||||
|
} else {
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
|
||||||
|
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("Dropbox", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class GoogleDriveLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final List<Path> LOCATIONS = Arrays.asList( //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/GoogleDrive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/Google Drive") //
|
||||||
|
);
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return LOCATIONS.stream().anyMatch(Files::isDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return LOCATIONS.stream() //
|
||||||
|
.filter(Files::isDirectory) //
|
||||||
|
.map(location -> new LocationPreset("Google Drive", location)) //
|
||||||
|
.findFirst() //
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class LeitzcloudLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/leitzcloud");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("leitzcloud", LOCATION));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public record LocationPreset(String name, Path path) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.IntegrationsLoader;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public interface LocationPresetsProvider {
|
||||||
|
|
||||||
|
Logger LOG = LoggerFactory.getLogger(LocationPresetsProvider.class);
|
||||||
|
String USER_HOME = System.getProperty("user.home");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streams account-separated location presets found by this provider
|
||||||
|
* @return Stream of LocationPresets
|
||||||
|
*/
|
||||||
|
Stream<LocationPreset> getLocations();
|
||||||
|
|
||||||
|
static Path resolveLocation(String p) {
|
||||||
|
if (p.startsWith("~/")) {
|
||||||
|
return Path.of(USER_HOME, p.substring(2));
|
||||||
|
} else {
|
||||||
|
return Path.of(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//copied from org.cryptomator.integrations.common.IntegrationsLoader
|
||||||
|
//TODO: delete, once migrated to integrations-api
|
||||||
|
static <T> Stream<T> loadAll(Class<T> clazz) {
|
||||||
|
return ServiceLoader.load(clazz)
|
||||||
|
.stream()
|
||||||
|
.filter(LocationPresetsProvider::isSupportedOperatingSystem)
|
||||||
|
.filter(LocationPresetsProvider::passesStaticAvailabilityCheck)
|
||||||
|
.map(ServiceLoader.Provider::get)
|
||||||
|
.peek(impl -> logServiceIsAvailable(clazz, impl.getClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isSupportedOperatingSystem(ServiceLoader.Provider<?> provider) {
|
||||||
|
var annotations = provider.type().getAnnotationsByType(OperatingSystem.class);
|
||||||
|
return annotations.length == 0 || Arrays.stream(annotations).anyMatch(OperatingSystem.Value::isCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean passesStaticAvailabilityCheck(ServiceLoader.Provider<?> provider) {
|
||||||
|
return passesStaticAvailabilityCheck(provider.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean passesStaticAvailabilityCheck(Class<?> type) {
|
||||||
|
return passesAvailabilityCheck(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logServiceIsAvailable(Class<?> apiType, Class<?> implType) {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("{}: Implementation is available: {}", apiType.getSimpleName(), implType.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean passesAvailabilityCheck(Class<? extends T> type, @Nullable T instance) {
|
||||||
|
if (!type.isAnnotationPresent(CheckAvailability.class)) {
|
||||||
|
return true; // if type is not annotated, skip tests
|
||||||
|
}
|
||||||
|
if (!type.getModule().isExported(type.getPackageName(), IntegrationsLoader.class.getModule())) {
|
||||||
|
LOG.error("Can't run @CheckAvailability tests for class {}. Make sure to export {} to {}!", type.getName(), type.getPackageName(), IntegrationsLoader.class.getPackageName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Arrays.stream(type.getMethods())
|
||||||
|
.filter(m -> isAvailabilityCheck(m, instance == null))
|
||||||
|
.allMatch(m -> passesAvailabilityCheck(m, instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean passesAvailabilityCheck(Method m, @Nullable Object instance) {
|
||||||
|
assert Boolean.TYPE.equals(m.getReturnType());
|
||||||
|
try {
|
||||||
|
return (boolean) m.invoke(instance);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
LOG.warn("Failed to invoke @CheckAvailability test {}#{}", m.getDeclaringClass(), m.getName(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAvailabilityCheck(Method m, boolean isStatic) {
|
||||||
|
return m.isAnnotationPresent(CheckAvailability.class)
|
||||||
|
&& Boolean.TYPE.equals(m.getReturnType())
|
||||||
|
&& m.getParameterCount() == 0
|
||||||
|
&& Modifier.isStatic(m.getModifiers()) == isStatic;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class MegaLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("MEGA", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
|
||||||
|
|
||||||
|
@OperatingSystem(LINUX)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class OneDriveLinuxLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
|
||||||
|
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return Files.isDirectory(LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return Stream.of(new LocationPreset("OneDrive", LOCATION));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
|
||||||
|
private static final Path PARENT_LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
var newLocations = getNewLocations().toList();
|
||||||
|
if (newLocations.size() >= 1) {
|
||||||
|
return newLocations.stream();
|
||||||
|
} else {
|
||||||
|
return getOldLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<LocationPreset> getNewLocations() {
|
||||||
|
try (var dirStream = Files.newDirectoryStream(PARENT_LOCATION, "OneDrive*")) {
|
||||||
|
return StreamSupport.stream(dirStream.spliterator(), false) //
|
||||||
|
.filter(Files::isDirectory) //
|
||||||
|
.map(p -> new LocationPreset(String.join(" - ", p.getFileName().toString().split("-")), p));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<LocationPreset> getOldLocation() {
|
||||||
|
return Stream.of(new LocationPreset("OneDrive", FALLBACK_LOCATION)).filter(preset -> Files.isDirectory(preset.path()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.jetbrains.annotations.Blocking;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(OneDriveWindowsLocationPresetsProvider.class);
|
||||||
|
private static final String REGSTR_TOKEN = "REG_SZ";
|
||||||
|
private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
try {
|
||||||
|
var accountRegKeys = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList();
|
||||||
|
var cloudLocations = new ArrayList<LocationPreset>();
|
||||||
|
for (var accountRegKey : accountRegKeys) {
|
||||||
|
var path = queryRegistry(accountRegKey, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||||
|
.map(Path::of) //
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
var name = "OneDrive"; //we assume personal oneDrive account by default
|
||||||
|
if (!accountRegKey.endsWith("Personal")) {
|
||||||
|
name = queryRegistry(accountRegKey, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
|
||||||
|
.map("OneDrive - "::concat) //
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
cloudLocations.add(new LocationPreset(name, path));
|
||||||
|
}
|
||||||
|
return cloudLocations.stream();
|
||||||
|
} catch (IOException | CommandFailedException | TimeoutException e) {
|
||||||
|
LOG.error("Unable to determine OneDrive location", e);
|
||||||
|
return Stream.of();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
LOG.error("Determination of OneDrive location interrupted", e);
|
||||||
|
return Stream.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<String> queryRegistry(String keyname, List<String> moreArgs, Predicate<String> outputFilter) throws InterruptedException, CommandFailedException, TimeoutException, IOException {
|
||||||
|
var args = new ArrayList<String>();
|
||||||
|
args.add("reg");
|
||||||
|
args.add("query");
|
||||||
|
args.add(keyname);
|
||||||
|
args.addAll(moreArgs);
|
||||||
|
ProcessBuilder command = new ProcessBuilder(args);
|
||||||
|
Process p = command.start();
|
||||||
|
waitForSuccess(p, 3, "`reg query`");
|
||||||
|
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}.
|
||||||
|
*
|
||||||
|
* @param process The process to wait for
|
||||||
|
* @param timeoutSeconds How long to wait (in seconds)
|
||||||
|
* @param cmdDescription A short description of the process used to generate log and exception messages
|
||||||
|
* @throws TimeoutException Thrown when the process doesn't finish in time
|
||||||
|
* @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish
|
||||||
|
* @throws CommandFailedException Thrown when the process exit code is non-zero
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
private static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException {
|
||||||
|
boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
if (!exited) {
|
||||||
|
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
|
||||||
|
}
|
||||||
|
if (process.exitValue() != 0) {
|
||||||
|
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
|
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
|
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CommandFailedException extends Exception {
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
String stdout;
|
||||||
|
String stderr;
|
||||||
|
|
||||||
|
private CommandFailedException(String cmdDescription, int exitCode, String stdout, String stderr) {
|
||||||
|
super(cmdDescription + " returned with non-zero exit code " + exitCode);
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
this.stdout = stdout;
|
||||||
|
this.stderr = stderr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
|
@OperatingSystem(WINDOWS)
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class PCloudLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final List<Path> LOCATIONS = Arrays.asList( //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/pCloudDrive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/pCloud Drive") //
|
||||||
|
);
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return LOCATIONS.stream().anyMatch(Files::isDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
return LOCATIONS.stream() //
|
||||||
|
.filter(Files::isDirectory) //
|
||||||
|
.map(location -> new LocationPreset("pCloud", location)) //
|
||||||
|
.findFirst() //
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,50 +2,71 @@ package org.cryptomator.common.mount;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.common.ObservableUtil;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.integrations.mount.Mount;
|
||||||
import org.cryptomator.integrations.mount.MountService;
|
import org.cryptomator.integrations.mount.MountService;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class MountModule {
|
public class MountModule {
|
||||||
|
|
||||||
|
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
|
||||||
|
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static List<MountService> provideSupportedMountServices() {
|
static List<MountService> provideSupportedMountServices() {
|
||||||
return MountService.get().toList();
|
return MountService.get().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
//currently not used, because macFUSE and FUSE-T cannot be used in the same JVM
|
|
||||||
/*
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls) {
|
@Named("FUPFMS")
|
||||||
|
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
|
||||||
|
return new AtomicReference<>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
|
||||||
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
||||||
return ObservableUtil.mapWithDefault(settings.mountService(), //
|
|
||||||
|
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
|
||||||
desiredServiceImpl -> { //
|
desiredServiceImpl -> { //
|
||||||
var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
|
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
|
||||||
return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
|
var targetedService = serviceFromSettings.orElse(fallbackProvider);
|
||||||
|
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
|
||||||
}, //
|
}, //
|
||||||
new ActualMountService(fallbackProvider, true));
|
() -> { //
|
||||||
}
|
return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
|
||||||
*/
|
});
|
||||||
|
return observableMountService;
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
static ActualMountService provideActualMountService(Settings settings, List<MountService> serviceImpls) {
|
|
||||||
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
|
||||||
var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(settings.mountService().getValue())).findFirst(); //
|
|
||||||
return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
//see https://github.com/cryptomator/cryptomator/issues/2786
|
||||||
@Singleton
|
private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) {
|
||||||
static ObservableValue<ActualMountService> provideMountService(ActualMountService service) {
|
//set the first used problematic fuse service if applicable
|
||||||
return new SimpleObjectProperty<>(service);
|
var targetIsProblematicFuse = isProblematicFuseService(targetedService);
|
||||||
|
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
|
||||||
|
firstUsedProblematicFuseMountService.set(targetedService);
|
||||||
|
}
|
||||||
|
|
||||||
|
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
|
||||||
|
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
|
||||||
|
return new ActualMountService(formerSelectedMountService.get(), false);
|
||||||
|
} else {
|
||||||
|
formerSelectedMountService.set(targetedService);
|
||||||
|
return new ActualMountService(targetedService, isDesired);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isProblematicFuseService(MountService service) {
|
||||||
|
return problematicFuseMountServices.contains(service.getClass().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
|
public class MountPointInUseException extends IllegalMountPointException {
|
||||||
|
|
||||||
|
public MountPointInUseException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ package org.cryptomator.common.mount;
|
|||||||
|
|
||||||
public class MountPointPreparationException extends RuntimeException {
|
public class MountPointPreparationException extends RuntimeException {
|
||||||
|
|
||||||
|
public MountPointPreparationException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
public MountPointPreparationException(Throwable cause) {
|
public MountPointPreparationException(Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,19 @@ public final class MountWithinParentUtil {
|
|||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
||||||
}
|
}
|
||||||
|
int attempts = 0;
|
||||||
|
while (!Files.notExists(mountPoint)) {
|
||||||
|
if (attempts >= 10) {
|
||||||
|
throw new MountPointPreparationException("Path " + mountPoint + " could not be cleared");
|
||||||
|
}
|
||||||
|
Thread.sleep(1000);
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new MountPointPreparationException(e);
|
throw new MountPointPreparationException(e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new MountPointPreparationException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,19 +54,19 @@ public class Mounter {
|
|||||||
switch (capability) {
|
switch (capability) {
|
||||||
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
||||||
case LOOPBACK_PORT ->
|
case LOOPBACK_PORT ->
|
||||||
builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
|
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
|
||||||
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
||||||
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
|
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
|
||||||
case MOUNT_FLAGS -> {
|
case MOUNT_FLAGS -> {
|
||||||
var mountFlags = vaultSettings.mountFlags().get();
|
var mountFlags = vaultSettings.mountFlags.get();
|
||||||
if (mountFlags == null || mountFlags.isBlank()) {
|
if (mountFlags == null || mountFlags.isBlank()) {
|
||||||
builder.setMountFlags(service.getDefaultMountFlags());
|
builder.setMountFlags(service.getDefaultMountFlags());
|
||||||
} else {
|
} else {
|
||||||
builder.setMountFlags(mountFlags);
|
builder.setMountFlags(mountFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId());
|
case VOLUME_ID -> builder.setVolumeId(vaultSettings.id);
|
||||||
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
|
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public class Mounter {
|
|||||||
|
|
||||||
private Runnable prepareMountPoint() throws IOException {
|
private Runnable prepareMountPoint() throws IOException {
|
||||||
Runnable cleanup = () -> {};
|
Runnable cleanup = () -> {};
|
||||||
var userChosenMountPoint = vaultSettings.getMountPoint();
|
var userChosenMountPoint = vaultSettings.mountPoint.get();
|
||||||
var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
|
var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
|
||||||
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
|
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
|
||||||
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
|
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
|
||||||
@@ -91,13 +91,17 @@ public class Mounter {
|
|||||||
Files.createDirectories(defaultMountPointBase);
|
Files.createDirectories(defaultMountPointBase);
|
||||||
builder.setMountpoint(defaultMountPointBase);
|
builder.setMountpoint(defaultMountPointBase);
|
||||||
} else if (canMountToDir) {
|
} else if (canMountToDir) {
|
||||||
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
|
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName.get());
|
||||||
Files.createDirectories(mountPoint);
|
Files.createDirectories(mountPoint);
|
||||||
builder.setMountpoint(mountPoint);
|
builder.setMountpoint(mountPoint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
|
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
|
||||||
if (!mpIsDriveLetter && canMountToParent && !canMountToDir) {
|
if (mpIsDriveLetter) {
|
||||||
|
if (driveLetters.getOccupied().contains(userChosenMountPoint)) {
|
||||||
|
throw new MountPointInUseException(userChosenMountPoint.toString());
|
||||||
|
}
|
||||||
|
} else if (canMountToParent && !canMountToDir) {
|
||||||
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
|
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
|
||||||
cleanup = () -> {
|
cleanup = () -> {
|
||||||
MountWithinParentUtil.cleanup(userChosenMountPoint);
|
MountWithinParentUtil.cleanup(userChosenMountPoint);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ package org.cryptomator.common.settings;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
@@ -27,59 +29,88 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
|
|
||||||
public static final int MIN_PORT = 1024;
|
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
|
||||||
public static final int MAX_PORT = 65535;
|
|
||||||
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
|
static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
|
||||||
public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
|
static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
|
||||||
public static final boolean DEFAULT_START_HIDDEN = false;
|
static final boolean DEFAULT_START_HIDDEN = false;
|
||||||
public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
|
static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
|
||||||
public static final boolean DEFAULT_USE_KEYCHAIN = true;
|
static final boolean DEFAULT_USE_KEYCHAIN = true;
|
||||||
public static final int DEFAULT_PORT = 42427;
|
static final int DEFAULT_PORT = 42427;
|
||||||
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||||
public static final boolean DEFAULT_DEBUG_MODE = false;
|
static final boolean DEFAULT_DEBUG_MODE = false;
|
||||||
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
||||||
@Deprecated // to be changed to "whatever is available" eventually
|
@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";
|
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";
|
||||||
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
|
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
|
||||||
public static final String DEFAULT_LICENSE_KEY = "";
|
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
|
||||||
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
|
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01";
|
||||||
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 BooleanProperty useKeychain = new SimpleBooleanProperty(DEFAULT_USE_KEYCHAIN);
|
|
||||||
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
|
|
||||||
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
|
|
||||||
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
|
|
||||||
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
|
|
||||||
private final ObjectProperty<String> keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
|
|
||||||
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
|
|
||||||
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
|
|
||||||
private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
|
|
||||||
private final BooleanProperty showTrayIcon;
|
|
||||||
private final IntegerProperty windowXPosition = new SimpleIntegerProperty();
|
|
||||||
private final IntegerProperty windowYPosition = new SimpleIntegerProperty();
|
|
||||||
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 final StringProperty mountService = new SimpleStringProperty();
|
|
||||||
|
|
||||||
|
public final ObservableList<VaultSettings> directories;
|
||||||
|
public final BooleanProperty askedForUpdateCheck;
|
||||||
|
public final BooleanProperty checkForUpdates;
|
||||||
|
public final BooleanProperty startHidden;
|
||||||
|
public final BooleanProperty autoCloseVaults;
|
||||||
|
public final BooleanProperty useKeychain;
|
||||||
|
public final IntegerProperty port;
|
||||||
|
public final IntegerProperty numTrayNotifications;
|
||||||
|
public final BooleanProperty debugMode;
|
||||||
|
public final ObjectProperty<UiTheme> theme;
|
||||||
|
public final StringProperty keychainProvider;
|
||||||
|
public final ObjectProperty<NodeOrientation> userInterfaceOrientation;
|
||||||
|
public final StringProperty licenseKey;
|
||||||
|
public final BooleanProperty showMinimizeButton;
|
||||||
|
public final BooleanProperty showTrayIcon;
|
||||||
|
public final IntegerProperty windowXPosition;
|
||||||
|
public final IntegerProperty windowYPosition;
|
||||||
|
public final IntegerProperty windowWidth;
|
||||||
|
public final IntegerProperty windowHeight;
|
||||||
|
public final StringProperty displayConfiguration;
|
||||||
|
public final StringProperty language;
|
||||||
|
public final StringProperty mountService;
|
||||||
|
public final StringProperty lastUpdateCheck;
|
||||||
|
|
||||||
private Consumer<Settings> saveCmd;
|
private Consumer<Settings> saveCmd;
|
||||||
|
|
||||||
|
public static Settings create(Environment env) {
|
||||||
|
var defaults = new SettingsJson();
|
||||||
|
defaults.showTrayIcon = env.showTrayIcon();
|
||||||
|
return new Settings(defaults);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package-private constructor; use {@link SettingsProvider}.
|
* Recreate settings from json
|
||||||
|
*
|
||||||
|
* @param json The parsed settings.json
|
||||||
*/
|
*/
|
||||||
Settings(Environment env) {
|
Settings(SettingsJson json) {
|
||||||
this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
|
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||||
|
this.askedForUpdateCheck = new SimpleBooleanProperty(this, "askedForUpdateCheck", json.askedForUpdateCheck);
|
||||||
|
this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
|
||||||
|
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
|
||||||
|
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
|
||||||
|
this.useKeychain = new SimpleBooleanProperty(this, "useKeychain", json.useKeychain);
|
||||||
|
this.port = new SimpleIntegerProperty(this, "webDavPort", json.port);
|
||||||
|
this.numTrayNotifications = new SimpleIntegerProperty(this, "numTrayNotifications", json.numTrayNotifications);
|
||||||
|
this.debugMode = new SimpleBooleanProperty(this, "debugMode", json.debugMode);
|
||||||
|
this.theme = new SimpleObjectProperty<>(this, "theme", json.theme);
|
||||||
|
this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider);
|
||||||
|
this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT));
|
||||||
|
this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey);
|
||||||
|
this.showMinimizeButton = new SimpleBooleanProperty(this, "showMinimizeButton", json.showMinimizeButton);
|
||||||
|
this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon);
|
||||||
|
this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition);
|
||||||
|
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
||||||
|
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
||||||
|
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
||||||
|
this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
|
||||||
|
this.language = new SimpleStringProperty(this, "language", json.language);
|
||||||
|
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||||
|
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
|
||||||
|
|
||||||
|
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||||
|
|
||||||
|
migrateLegacySettings(json);
|
||||||
|
|
||||||
directories.addListener(this::somethingChanged);
|
directories.addListener(this::somethingChanged);
|
||||||
askedForUpdateCheck.addListener(this::somethingChanged);
|
askedForUpdateCheck.addListener(this::somethingChanged);
|
||||||
@@ -103,8 +134,76 @@ public class Settings {
|
|||||||
displayConfiguration.addListener(this::somethingChanged);
|
displayConfiguration.addListener(this::somethingChanged);
|
||||||
language.addListener(this::somethingChanged);
|
language.addListener(this::somethingChanged);
|
||||||
mountService.addListener(this::somethingChanged);
|
mountService.addListener(this::somethingChanged);
|
||||||
|
lastUpdateCheck.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private void migrateLegacySettings(SettingsJson json) {
|
||||||
|
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
|
||||||
|
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
|
||||||
|
this.mountService.set(switch (json.preferredVolumeImpl) {
|
||||||
|
case "Dokany" -> "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
|
||||||
|
case "FUSE" -> {
|
||||||
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
|
yield "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
|
||||||
|
} else if (SystemUtils.IS_OS_MAC) {
|
||||||
|
yield "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
|
||||||
|
} else {
|
||||||
|
yield "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
|
yield "org.cryptomator.frontend.webdav.mount.WindowsMounter";
|
||||||
|
} else if (SystemUtils.IS_OS_MAC) {
|
||||||
|
yield "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
|
||||||
|
} else {
|
||||||
|
yield "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsJson serialized() {
|
||||||
|
var json = new SettingsJson();
|
||||||
|
json.directories = directories.stream().map(VaultSettings::serialized).toList();
|
||||||
|
json.askedForUpdateCheck = askedForUpdateCheck.get();
|
||||||
|
json.checkForUpdatesEnabled = checkForUpdates.get();
|
||||||
|
json.startHidden = startHidden.get();
|
||||||
|
json.autoCloseVaults = autoCloseVaults.get();
|
||||||
|
json.useKeychain = useKeychain.get();
|
||||||
|
json.port = port.get();
|
||||||
|
json.numTrayNotifications = numTrayNotifications.get();
|
||||||
|
json.debugMode = debugMode.get();
|
||||||
|
json.theme = theme.get();
|
||||||
|
json.keychainProvider = keychainProvider.get();
|
||||||
|
json.uiOrientation = userInterfaceOrientation.get().name();
|
||||||
|
json.licenseKey = licenseKey.get();
|
||||||
|
json.showMinimizeButton = showMinimizeButton.get();
|
||||||
|
json.showTrayIcon = showTrayIcon.get();
|
||||||
|
json.windowXPosition = windowXPosition.get();
|
||||||
|
json.windowYPosition = windowYPosition.get();
|
||||||
|
json.windowWidth = windowWidth.get();
|
||||||
|
json.windowHeight = windowHeight.get();
|
||||||
|
json.displayConfiguration = displayConfiguration.get();
|
||||||
|
json.language = language.get();
|
||||||
|
json.mountService = mountService.get();
|
||||||
|
json.lastUpdateCheck = lastUpdateCheck.get();
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E extends Enum<E>> E parseEnum(String value, Class<E> clazz, E defaultValue) {
|
||||||
|
try {
|
||||||
|
return Enum.valueOf(clazz, value.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.warn("No value {}.{}. Defaulting to {}.", clazz.getSimpleName(), value, defaultValue);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO rename to setChangeListener
|
||||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||||
this.saveCmd = saveCmd;
|
this.saveCmd = saveCmd;
|
||||||
}
|
}
|
||||||
@@ -119,90 +218,4 @@ public class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
|
||||||
|
|
||||||
public ObservableList<VaultSettings> getDirectories() {
|
|
||||||
return directories;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty askedForUpdateCheck() {
|
|
||||||
return askedForUpdateCheck;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty checkForUpdates() {
|
|
||||||
return checkForUpdates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty startHidden() {
|
|
||||||
return startHidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty autoCloseVaults() {
|
|
||||||
return autoCloseVaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty useKeychain() { return useKeychain; }
|
|
||||||
|
|
||||||
public IntegerProperty port() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty numTrayNotifications() {
|
|
||||||
return numTrayNotifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty debugMode() {
|
|
||||||
return debugMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty mountService() {
|
|
||||||
return mountService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<UiTheme> theme() {
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<String> keychainProvider() {return keychainProvider;}
|
|
||||||
|
|
||||||
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
|
|
||||||
return userInterfaceOrientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty licenseKey() {
|
|
||||||
return licenseKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty showMinimizeButton() {
|
|
||||||
return showMinimizeButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty showTrayIcon() {
|
|
||||||
return showTrayIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty windowXPositionProperty() {
|
|
||||||
return windowXPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty windowYPositionProperty() {
|
|
||||||
return windowYPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty windowWidthProperty() {
|
|
||||||
return windowWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty windowHeightProperty() {
|
|
||||||
return windowHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<String> displayConfigurationProperty() {
|
|
||||||
return displayConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty languageProperty() {
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
class SettingsJson {
|
||||||
|
|
||||||
|
@JsonProperty("directories")
|
||||||
|
List<VaultSettingsJson> directories = List.of();
|
||||||
|
|
||||||
|
@JsonProperty("writtenByVersion")
|
||||||
|
String writtenByVersion;
|
||||||
|
|
||||||
|
@JsonProperty("askedForUpdateCheck")
|
||||||
|
boolean askedForUpdateCheck = Settings.DEFAULT_ASKED_FOR_UPDATE_CHECK;
|
||||||
|
|
||||||
|
@JsonProperty("autoCloseVaults")
|
||||||
|
boolean autoCloseVaults = Settings.DEFAULT_AUTO_CLOSE_VAULTS;
|
||||||
|
|
||||||
|
@JsonProperty("checkForUpdatesEnabled")
|
||||||
|
boolean checkForUpdatesEnabled = Settings.DEFAULT_CHECK_FOR_UPDATES;
|
||||||
|
|
||||||
|
@JsonProperty("debugMode")
|
||||||
|
boolean debugMode = Settings.DEFAULT_DEBUG_MODE;
|
||||||
|
|
||||||
|
@JsonProperty("theme")
|
||||||
|
UiTheme theme = Settings.DEFAULT_THEME;
|
||||||
|
|
||||||
|
@JsonProperty("displayConfiguration")
|
||||||
|
String displayConfiguration;
|
||||||
|
|
||||||
|
@JsonProperty("keychainProvider")
|
||||||
|
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
|
||||||
|
|
||||||
|
@JsonProperty("language")
|
||||||
|
String language;
|
||||||
|
|
||||||
|
@JsonProperty("licenseKey")
|
||||||
|
String licenseKey;
|
||||||
|
|
||||||
|
@JsonProperty("mountService")
|
||||||
|
String mountService;
|
||||||
|
|
||||||
|
@JsonProperty("numTrayNotifications")
|
||||||
|
int numTrayNotifications = Settings.DEFAULT_NUM_TRAY_NOTIFICATIONS;
|
||||||
|
|
||||||
|
@JsonProperty("port")
|
||||||
|
int port = Settings.DEFAULT_PORT;
|
||||||
|
|
||||||
|
@JsonProperty("showMinimizeButton")
|
||||||
|
boolean showMinimizeButton = Settings.DEFAULT_SHOW_MINIMIZE_BUTTON;
|
||||||
|
|
||||||
|
@JsonProperty("showTrayIcon")
|
||||||
|
boolean showTrayIcon;
|
||||||
|
|
||||||
|
@JsonProperty("startHidden")
|
||||||
|
boolean startHidden = Settings.DEFAULT_START_HIDDEN;
|
||||||
|
|
||||||
|
@JsonProperty("uiOrientation")
|
||||||
|
String uiOrientation = Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
|
||||||
|
|
||||||
|
@JsonProperty("useKeychain")
|
||||||
|
boolean useKeychain = Settings.DEFAULT_USE_KEYCHAIN;
|
||||||
|
|
||||||
|
@JsonProperty("windowHeight")
|
||||||
|
int windowHeight;
|
||||||
|
|
||||||
|
@JsonProperty("windowWidth")
|
||||||
|
int windowWidth;
|
||||||
|
|
||||||
|
@JsonProperty("windowXPosition")
|
||||||
|
int windowXPosition;
|
||||||
|
|
||||||
|
@JsonProperty("windowYPosition")
|
||||||
|
int windowYPosition;
|
||||||
|
|
||||||
|
@Deprecated(since = "1.7.0")
|
||||||
|
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
|
String preferredVolumeImpl;
|
||||||
|
|
||||||
|
@JsonProperty("lastUpdateCheck")
|
||||||
|
String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the accompanying LICENSE file.
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.cryptomator.common.settings;
|
|
||||||
|
|
||||||
import com.google.gson.TypeAdapter;
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
import com.google.gson.stream.JsonToken;
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.Environment;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javafx.geometry.NodeOrientation;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
|
|
||||||
|
|
||||||
private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
|
|
||||||
private final Environment env;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SettingsJsonAdapter(Environment env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(JsonWriter out, Settings value) throws IOException {
|
|
||||||
out.beginObject();
|
|
||||||
out.name("writtenByVersion").value(env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse(""));
|
|
||||||
out.name("directories");
|
|
||||||
writeVaultSettingsArray(out, value.getDirectories());
|
|
||||||
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
|
|
||||||
out.name("autoCloseVaults").value(value.autoCloseVaults().get());
|
|
||||||
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
|
|
||||||
out.name("debugMode").value(value.debugMode().get());
|
|
||||||
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
|
|
||||||
out.name("keychainProvider").value(value.keychainProvider().get());
|
|
||||||
out.name("language").value((value.languageProperty().get()));
|
|
||||||
out.name("licenseKey").value(value.licenseKey().get());
|
|
||||||
out.name("mountService").value(value.mountService().get());
|
|
||||||
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
|
||||||
out.name("port").value(value.port().get());
|
|
||||||
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
|
|
||||||
out.name("showTrayIcon").value(value.showTrayIcon().get());
|
|
||||||
out.name("startHidden").value(value.startHidden().get());
|
|
||||||
out.name("theme").value(value.theme().get().name());
|
|
||||||
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
|
|
||||||
out.name("useKeychain").value(value.useKeychain().get());
|
|
||||||
out.name("windowHeight").value((value.windowHeightProperty().get()));
|
|
||||||
out.name("windowWidth").value((value.windowWidthProperty().get()));
|
|
||||||
out.name("windowXPosition").value((value.windowXPositionProperty().get()));
|
|
||||||
out.name("windowYPosition").value((value.windowYPositionProperty().get()));
|
|
||||||
out.endObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeVaultSettingsArray(JsonWriter out, Iterable<VaultSettings> vaultSettings) throws IOException {
|
|
||||||
out.beginArray();
|
|
||||||
for (VaultSettings value : vaultSettings) {
|
|
||||||
vaultSettingsJsonAdapter.write(out, value);
|
|
||||||
}
|
|
||||||
out.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Settings read(JsonReader in) throws IOException {
|
|
||||||
Settings settings = new Settings(env);
|
|
||||||
//1.6.x legacy
|
|
||||||
String volumeImpl = null;
|
|
||||||
//legacy end
|
|
||||||
in.beginObject();
|
|
||||||
while (in.hasNext()) {
|
|
||||||
String name = in.nextName();
|
|
||||||
switch (name) {
|
|
||||||
case "writtenByVersion" -> in.skipValue(); //noop
|
|
||||||
case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
|
|
||||||
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
|
|
||||||
case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
|
|
||||||
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
|
|
||||||
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
|
|
||||||
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
|
|
||||||
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
|
|
||||||
case "language" -> settings.languageProperty().set(in.nextString());
|
|
||||||
case "licenseKey" -> settings.licenseKey().set(in.nextString());
|
|
||||||
case "mountService" -> {
|
|
||||||
var token = in.peek();
|
|
||||||
if (JsonToken.STRING == token) {
|
|
||||||
settings.mountService().set(in.nextString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
|
|
||||||
case "port" -> settings.port().set(in.nextInt());
|
|
||||||
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
|
|
||||||
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
|
|
||||||
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
|
|
||||||
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
|
|
||||||
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
|
|
||||||
case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
|
|
||||||
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
|
|
||||||
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
|
|
||||||
case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
|
|
||||||
case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
|
|
||||||
//1.6.x legacy
|
|
||||||
case "preferredVolumeImpl" -> volumeImpl = in.nextString();
|
|
||||||
//legacy end
|
|
||||||
default -> {
|
|
||||||
LOG.warn("Unsupported vault setting found in JSON: {}", name);
|
|
||||||
in.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
in.endObject();
|
|
||||||
|
|
||||||
//1.6.x legacy
|
|
||||||
if (volumeImpl != null) {
|
|
||||||
settings.mountService().set(convertLegacyVolumeImplToMountService(volumeImpl));
|
|
||||||
}
|
|
||||||
//legacy end
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String convertLegacyVolumeImplToMountService(String volumeImpl) {
|
|
||||||
if (volumeImpl.equals("Dokany")) {
|
|
||||||
return "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
|
|
||||||
} else if (volumeImpl.equals("FUSE")) {
|
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
|
||||||
return "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
|
|
||||||
} else if (SystemUtils.IS_OS_MAC) {
|
|
||||||
return "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
|
|
||||||
} else {
|
|
||||||
return "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
|
||||||
return "org.cryptomator.frontend.webdav.mount.WindowsMounter";
|
|
||||||
} else if (SystemUtils.IS_OS_MAC) {
|
|
||||||
return "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
|
|
||||||
} else {
|
|
||||||
return "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private UiTheme parseUiTheme(String uiThemeName) {
|
|
||||||
try {
|
|
||||||
return UiTheme.valueOf(uiThemeName.toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOG.warn("Invalid ui theme {}. Defaulting to {}.", uiThemeName, Settings.DEFAULT_THEME);
|
|
||||||
return Settings.DEFAULT_THEME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NodeOrientation parseUiOrientation(String uiOrientationName) {
|
|
||||||
try {
|
|
||||||
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOG.warn("Invalid ui orientation {}. Defaulting to {}.", uiOrientationName, Settings.DEFAULT_USER_INTERFACE_ORIENTATION);
|
|
||||||
return Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<VaultSettings> readVaultSettingsArray(JsonReader in) throws IOException {
|
|
||||||
List<VaultSettings> result = new ArrayList<>();
|
|
||||||
in.beginArray();
|
|
||||||
while (!JsonToken.END_ARRAY.equals(in.peek())) {
|
|
||||||
result.add(vaultSettingsJsonAdapter.read(in));
|
|
||||||
}
|
|
||||||
in.endArray();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,12 +8,9 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonParseException;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -22,12 +19,7 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -44,6 +36,7 @@ import java.util.stream.Stream;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class SettingsProvider implements Supplier<Settings> {
|
public class SettingsProvider implements Supplier<Settings> {
|
||||||
|
|
||||||
|
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
||||||
private static final long SAVE_DELAY_MS = 1000;
|
private static final long SAVE_DELAY_MS = 1000;
|
||||||
|
|
||||||
@@ -51,16 +44,11 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
|
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Gson gson;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
|
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.gson = new GsonBuilder() //
|
|
||||||
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
|
||||||
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
|
||||||
.create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,28 +57,25 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Settings load() {
|
private Settings load() {
|
||||||
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings(env));
|
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElseGet(() -> Settings.create(env));
|
||||||
settings.setSaveCmd(this::scheduleSave);
|
settings.setSaveCmd(this::scheduleSave);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<Settings> tryLoad(Path path) {
|
private Stream<Settings> tryLoad(Path path) {
|
||||||
LOG.debug("Attempting to load settings from {}", path);
|
LOG.debug("Attempting to load settings from {}", path);
|
||||||
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
|
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
|
||||||
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
var json = JSON.reader().readValue(in, SettingsJson.class);
|
||||||
JsonElement json = JsonParser.parseReader(reader);
|
LOG.info("Settings loaded from {}", path);
|
||||||
if (json.isJsonObject()) {
|
var settings = new Settings(json);
|
||||||
Settings settings = gson.fromJson(json, Settings.class);
|
return Stream.of(settings);
|
||||||
LOG.info("Settings loaded from {}", path);
|
} catch (JacksonException e) {
|
||||||
return Stream.of(settings);
|
LOG.warn("Failed to parse json file {}", path, e);
|
||||||
} else {
|
return Stream.empty();
|
||||||
LOG.warn("Invalid json file {}", path);
|
|
||||||
return Stream.empty();
|
|
||||||
}
|
|
||||||
} catch (NoSuchFileException e) {
|
} catch (NoSuchFileException e) {
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
} catch (IOException | JsonParseException e) {
|
} catch (IOException e) {
|
||||||
LOG.warn("Exception while loading settings from " + path, e);
|
LOG.warn("Failed to load json file {}", path, e);
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,13 +101,14 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
try {
|
try {
|
||||||
Files.createDirectories(settingsPath.getParent());
|
Files.createDirectories(settingsPath.getParent());
|
||||||
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
|
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
|
||||||
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
|
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
|
||||||
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
var jsonObj = settings.serialized();
|
||||||
gson.toJson(settings, writer);
|
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
|
||||||
|
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
|
||||||
}
|
}
|
||||||
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
|
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
LOG.info("Settings saved to {}", settingsPath);
|
LOG.info("Settings saved to {}", settingsPath);
|
||||||
} catch (IOException | JsonParseException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to save settings.", e);
|
LOG.error("Failed to save settings.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
public enum UiTheme {
|
public enum UiTheme {
|
||||||
LIGHT("preferences.interface.theme.light"), //
|
@JsonEnumDefaultValue LIGHT("preferences.interface.theme.light"), //
|
||||||
DARK("preferences.interface.theme.dark"), //
|
DARK("preferences.interface.theme.dark"), //
|
||||||
AUTOMATIC("preferences.interface.theme.automatic");
|
AUTOMATIC("preferences.interface.theme.automatic");
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import com.google.common.base.CharMatcher;
|
import com.google.common.base.CharMatcher;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
@@ -20,6 +22,7 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
@@ -28,33 +31,45 @@ import java.util.Random;
|
|||||||
*/
|
*/
|
||||||
public class VaultSettings {
|
public class VaultSettings {
|
||||||
|
|
||||||
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
|
static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
|
||||||
public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
|
static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
|
||||||
public static final boolean DEFAULT_USES_READONLY_MODE = false;
|
static final boolean DEFAULT_USES_READONLY_MODE = false;
|
||||||
public static final String DEFAULT_MOUNT_FLAGS = "";
|
static final String DEFAULT_MOUNT_FLAGS = ""; // TODO: remove empty default mount flags and let this property be null if not used
|
||||||
public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
|
static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
|
||||||
public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
||||||
public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
||||||
public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
||||||
|
|
||||||
private static final Random RNG = new Random();
|
private static final Random RNG = new Random();
|
||||||
|
|
||||||
private final String id;
|
public final String id;
|
||||||
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
|
public final ObjectProperty<Path> path;
|
||||||
private final StringProperty displayName = new SimpleStringProperty();
|
public final StringProperty displayName;
|
||||||
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
|
public final BooleanProperty unlockAfterStartup;
|
||||||
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
|
public final BooleanProperty revealAfterMount;
|
||||||
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
|
public final BooleanProperty usesReadOnlyMode;
|
||||||
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS); //TODO: remove empty default mount flags and let this property be null if not used
|
public final StringProperty mountFlags;
|
||||||
private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
|
public final IntegerProperty maxCleartextFilenameLength;
|
||||||
private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
|
public final ObjectProperty<WhenUnlocked> actionAfterUnlock;
|
||||||
private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
|
public final BooleanProperty autoLockWhenIdle;
|
||||||
private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
|
public final IntegerProperty autoLockIdleSeconds;
|
||||||
private final StringExpression mountName;
|
public final ObjectProperty<Path> mountPoint;
|
||||||
private final ObjectProperty<Path> mountPoint = new SimpleObjectProperty<>();
|
public final StringExpression mountName;
|
||||||
|
|
||||||
public VaultSettings(String id) {
|
VaultSettings(VaultSettingsJson json) {
|
||||||
this.id = Objects.requireNonNull(id);
|
this.id = json.id;
|
||||||
|
this.path = new SimpleObjectProperty<>(this, "path", json.path == null ? null : Paths.get(json.path));
|
||||||
|
this.displayName = new SimpleStringProperty(this, "displayName", json.displayName);
|
||||||
|
this.unlockAfterStartup = new SimpleBooleanProperty(this, "unlockAfterStartup", json.unlockAfterStartup);
|
||||||
|
this.revealAfterMount = new SimpleBooleanProperty(this, "revealAfterMount", json.revealAfterMount);
|
||||||
|
this.usesReadOnlyMode = new SimpleBooleanProperty(this, "usesReadOnlyMode", json.usesReadOnlyMode);
|
||||||
|
this.mountFlags = new SimpleStringProperty(this, "mountFlags", json.mountFlags);
|
||||||
|
this.maxCleartextFilenameLength = new SimpleIntegerProperty(this, "maxCleartextFilenameLength", json.maxCleartextFilenameLength);
|
||||||
|
this.actionAfterUnlock = new SimpleObjectProperty<>(this, "actionAfterUnlock", json.actionAfterUnlock);
|
||||||
|
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
|
||||||
|
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
|
||||||
|
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||||
|
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
||||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||||
final String name;
|
final String name;
|
||||||
if (displayName.isEmpty().get()) {
|
if (displayName.isEmpty().get()) {
|
||||||
@@ -64,6 +79,18 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
return normalizeDisplayName(name);
|
return normalizeDisplayName(name);
|
||||||
}, displayName, path));
|
}, displayName, path));
|
||||||
|
|
||||||
|
migrateLegacySettings(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private void migrateLegacySettings(VaultSettingsJson json) {
|
||||||
|
// implicit migration of 1.6.x legacy setting "customMountPath" / "winDriveLetter":
|
||||||
|
if (json.useCustomMountPath && !Strings.isNullOrEmpty(json.customMountPath)) {
|
||||||
|
this.mountPoint.set(Path.of(json.customMountPath));
|
||||||
|
} else if (!Strings.isNullOrEmpty(json.winDriveLetter)) {
|
||||||
|
this.mountPoint.set(Path.of(json.winDriveLetter + ":\\"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
@@ -71,7 +98,9 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static VaultSettings withRandomId() {
|
public static VaultSettings withRandomId() {
|
||||||
return new VaultSettings(generateId());
|
var defaults = new VaultSettingsJson();
|
||||||
|
defaults.id = generateId();
|
||||||
|
return new VaultSettings(defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String generateId() {
|
private static String generateId() {
|
||||||
@@ -80,6 +109,23 @@ public class VaultSettings {
|
|||||||
return BaseEncoding.base64Url().encode(randomBytes);
|
return BaseEncoding.base64Url().encode(randomBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VaultSettingsJson serialized() {
|
||||||
|
var json = new VaultSettingsJson();
|
||||||
|
json.id = id;
|
||||||
|
json.path = path.map(Path::toString).getValue();
|
||||||
|
json.displayName = displayName.get();
|
||||||
|
json.unlockAfterStartup = unlockAfterStartup.get();
|
||||||
|
json.revealAfterMount = revealAfterMount.get();
|
||||||
|
json.usesReadOnlyMode = usesReadOnlyMode.get();
|
||||||
|
json.mountFlags = mountFlags.get();
|
||||||
|
json.maxCleartextFilenameLength = maxCleartextFilenameLength.get();
|
||||||
|
json.actionAfterUnlock = actionAfterUnlock.get();
|
||||||
|
json.autoLockWhenIdle = autoLockWhenIdle.get();
|
||||||
|
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
|
||||||
|
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
//visible for testing
|
//visible for testing
|
||||||
static String normalizeDisplayName(String original) {
|
static String normalizeDisplayName(String original) {
|
||||||
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
||||||
@@ -93,68 +139,6 @@ public class VaultSettings {
|
|||||||
return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_');
|
return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Path> path() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty displayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringExpression mountName() {
|
|
||||||
return mountName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty unlockAfterStartup() {
|
|
||||||
return unlockAfterStartup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty revealAfterMount() {
|
|
||||||
return revealAfterMount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getMountPoint() {
|
|
||||||
return mountPoint.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Path> mountPoint() {
|
|
||||||
return mountPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty usesReadOnlyMode() {
|
|
||||||
return usesReadOnlyMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty mountFlags() {
|
|
||||||
return mountFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty maxCleartextFilenameLength() {
|
|
||||||
return maxCleartextFilenameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<WhenUnlocked> actionAfterUnlock() {
|
|
||||||
return actionAfterUnlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WhenUnlocked getActionAfterUnlock() {
|
|
||||||
return actionAfterUnlock.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty autoLockWhenIdle() {
|
|
||||||
return autoLockWhenIdle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntegerProperty autoLockIdleSeconds() {
|
|
||||||
return autoLockIdleSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hashcode/Equals */
|
/* Hashcode/Equals */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
class VaultSettingsJson {
|
||||||
|
|
||||||
|
@JsonProperty(value = "id", required = true)
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@JsonProperty(value = "path")
|
||||||
|
String path;
|
||||||
|
|
||||||
|
@JsonProperty("displayName")
|
||||||
|
String displayName;
|
||||||
|
|
||||||
|
@JsonProperty("unlockAfterStartup")
|
||||||
|
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
||||||
|
|
||||||
|
@JsonProperty("revealAfterMount")
|
||||||
|
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
|
||||||
|
|
||||||
|
@JsonProperty("mountPoint")
|
||||||
|
String mountPoint;
|
||||||
|
|
||||||
|
@JsonProperty("usesReadOnlyMode")
|
||||||
|
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
||||||
|
|
||||||
|
@JsonProperty("mountFlags")
|
||||||
|
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
|
||||||
|
|
||||||
|
@JsonProperty("maxCleartextFilenameLength")
|
||||||
|
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
|
||||||
|
|
||||||
|
@JsonProperty("actionAfterUnlock")
|
||||||
|
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
|
||||||
|
|
||||||
|
@JsonProperty("autoLockWhenIdle")
|
||||||
|
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
|
||||||
|
|
||||||
|
@JsonProperty("autoLockIdleSeconds")
|
||||||
|
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
||||||
|
|
||||||
|
@Deprecated(since = "1.7.0")
|
||||||
|
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
|
String winDriveLetter;
|
||||||
|
|
||||||
|
@Deprecated(since = "1.7.0")
|
||||||
|
@JsonProperty(value = "useCustomMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
|
@JsonAlias("usesIndividualMountPath")
|
||||||
|
boolean useCustomMountPath;
|
||||||
|
|
||||||
|
@Deprecated(since = "1.7.0")
|
||||||
|
@JsonProperty(value = "customMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
|
@JsonAlias("individualMountPath")
|
||||||
|
String customMountPath;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the accompanying LICENSE file.
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.cryptomator.common.settings;
|
|
||||||
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
import com.google.gson.stream.JsonToken;
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.InvalidPathException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
class VaultSettingsJsonAdapter {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(VaultSettingsJsonAdapter.class);
|
|
||||||
|
|
||||||
public void write(JsonWriter out, VaultSettings value) throws IOException {
|
|
||||||
out.beginObject();
|
|
||||||
out.name("id").value(value.getId());
|
|
||||||
out.name("path").value(value.path().get().toString());
|
|
||||||
out.name("displayName").value(value.displayName().get());
|
|
||||||
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
|
|
||||||
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
|
||||||
var mountPoint = value.mountPoint().get();
|
|
||||||
out.name("mountPoint").value(mountPoint != null ? mountPoint.toAbsolutePath().toString() : null);
|
|
||||||
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
|
|
||||||
out.name("mountFlags").value(value.mountFlags().get());
|
|
||||||
out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
|
|
||||||
out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
|
|
||||||
out.name("autoLockWhenIdle").value(value.autoLockWhenIdle().get());
|
|
||||||
out.name("autoLockIdleSeconds").value(value.autoLockIdleSeconds().get());
|
|
||||||
out.endObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VaultSettings read(JsonReader in) throws IOException {
|
|
||||||
String id = null;
|
|
||||||
String path = null;
|
|
||||||
String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
|
|
||||||
String displayName = null;
|
|
||||||
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
|
||||||
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
|
|
||||||
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
|
||||||
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
|
|
||||||
Path mountPoint = null;
|
|
||||||
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
|
|
||||||
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
|
|
||||||
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
|
|
||||||
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
|
||||||
|
|
||||||
//legacy from 1.6.x
|
|
||||||
boolean useCustomMountPath = false;
|
|
||||||
String customMountPath = "";
|
|
||||||
String winDriveLetter = "";
|
|
||||||
//legacy end
|
|
||||||
|
|
||||||
in.beginObject();
|
|
||||||
while (in.hasNext()) {
|
|
||||||
String name = in.nextName();
|
|
||||||
switch (name) {
|
|
||||||
case "id" -> id = in.nextString();
|
|
||||||
case "path" -> path = in.nextString();
|
|
||||||
case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
|
|
||||||
case "displayName" -> displayName = in.nextString();
|
|
||||||
case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
|
|
||||||
case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
|
|
||||||
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
|
|
||||||
case "mountFlags" -> mountFlags = in.nextString();
|
|
||||||
case "mountPoint" -> {
|
|
||||||
if (JsonToken.NULL == in.peek()) {
|
|
||||||
in.nextNull();
|
|
||||||
} else {
|
|
||||||
mountPoint = parseMountPoint(in.nextString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
|
|
||||||
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
|
|
||||||
case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
|
|
||||||
case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
|
|
||||||
//legacy from 1.6.x
|
|
||||||
case "winDriveLetter" -> winDriveLetter = in.nextString();
|
|
||||||
case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
|
|
||||||
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
|
|
||||||
//legacy end
|
|
||||||
default -> {
|
|
||||||
LOG.warn("Unsupported vault setting found in JSON: {}", name);
|
|
||||||
in.skipValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in.endObject();
|
|
||||||
|
|
||||||
VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
|
|
||||||
if (displayName != null) { //see https://github.com/cryptomator/cryptomator/pull/1318
|
|
||||||
vaultSettings.displayName().set(displayName);
|
|
||||||
} else {
|
|
||||||
vaultSettings.displayName().set(mountName);
|
|
||||||
}
|
|
||||||
vaultSettings.path().set(Paths.get(path));
|
|
||||||
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
|
|
||||||
vaultSettings.revealAfterMount().set(revealAfterMount);
|
|
||||||
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
|
|
||||||
vaultSettings.mountFlags().set(mountFlags);
|
|
||||||
vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
|
|
||||||
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
|
|
||||||
vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
|
|
||||||
vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
|
|
||||||
vaultSettings.mountPoint().set(mountPoint);
|
|
||||||
//legacy from 1.6.x
|
|
||||||
if(useCustomMountPath && !customMountPath.isBlank()) {
|
|
||||||
vaultSettings.mountPoint().set(parseMountPoint(customMountPath));
|
|
||||||
} else if(!winDriveLetter.isBlank() ) {
|
|
||||||
vaultSettings.mountPoint().set(parseMountPoint(winDriveLetter+":\\"));
|
|
||||||
}
|
|
||||||
//legacy end
|
|
||||||
return vaultSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path parseMountPoint(String mountPoint) {
|
|
||||||
try {
|
|
||||||
return Path.of(mountPoint);
|
|
||||||
} catch (InvalidPathException e) {
|
|
||||||
LOG.warn("Invalid string as mount point. Defaulting to null.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private WhenUnlocked parseActionAfterUnlock(String actionAfterUnlockName) {
|
|
||||||
try {
|
|
||||||
return WhenUnlocked.valueOf(actionAfterUnlockName.toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOG.warn("Invalid action after unlock {}. Defaulting to {}.", actionAfterUnlockName, VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK);
|
|
||||||
return VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||||
public enum WhenUnlocked {
|
public enum WhenUnlocked {
|
||||||
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
|
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
|
||||||
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
|
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
|
||||||
ASK("vaultOptions.general.actionAfterUnlock.ask");
|
@JsonEnumDefaultValue ASK("vaultOptions.general.actionAfterUnlock.ask");
|
||||||
|
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -40,6 +41,7 @@ public class AutoLocker {
|
|||||||
private void autolock(Vault vault) {
|
private void autolock(Vault vault) {
|
||||||
try {
|
try {
|
||||||
vault.lock(false);
|
vault.lock(false);
|
||||||
|
Platform.runLater(() -> vault.stateProperty().set(VaultState.Value.LOCKED));
|
||||||
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
|
LOG.info("Autolocked {} after idle timeout", vault.getDisplayName());
|
||||||
} catch (UnmountFailedException | IOException e) {
|
} catch (UnmountFailedException | IOException e) {
|
||||||
LOG.error("Autolocking failed.", e);
|
LOG.error("Autolocking failed.", e);
|
||||||
@@ -48,8 +50,8 @@ public class AutoLocker {
|
|||||||
|
|
||||||
private boolean exceedsIdleTime(Vault vault) {
|
private boolean exceedsIdleTime(Vault vault) {
|
||||||
assert vault.isUnlocked();
|
assert vault.isUnlocked();
|
||||||
if (vault.getVaultSettings().autoLockWhenIdle().get()) {
|
if (vault.getVaultSettings().autoLockWhenIdle.get()) {
|
||||||
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
|
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds.get();
|
||||||
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
|
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
|
||||||
return deadline.isBefore(Instant.now());
|
return deadline.isBefore(Instant.now());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class Vault {
|
|||||||
private final Mounter mounter;
|
private final Mounter mounter;
|
||||||
private final BooleanProperty showingStats;
|
private final BooleanProperty showingStats;
|
||||||
|
|
||||||
private AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
|
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
|
||||||
@@ -80,7 +80,7 @@ public class Vault {
|
|||||||
this.state = state;
|
this.state = state;
|
||||||
this.lastKnownException = lastKnownException;
|
this.lastKnownException = lastKnownException;
|
||||||
this.stats = stats;
|
this.stats = stats;
|
||||||
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
|
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path);
|
||||||
this.locked = Bindings.createBooleanBinding(this::isLocked, state);
|
this.locked = Bindings.createBooleanBinding(this::isLocked, state);
|
||||||
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
|
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
|
||||||
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
|
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
|
||||||
@@ -98,29 +98,29 @@ public class Vault {
|
|||||||
|
|
||||||
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
|
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
|
||||||
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
|
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
|
||||||
if (vaultSettings.usesReadOnlyMode().get()) {
|
if (vaultSettings.usesReadOnlyMode.get()) {
|
||||||
flags.add(FileSystemFlags.READONLY);
|
flags.add(FileSystemFlags.READONLY);
|
||||||
} else if (vaultSettings.maxCleartextFilenameLength().get() == -1) {
|
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) {
|
||||||
LOG.debug("Determining cleartext filename length limitations...");
|
LOG.debug("Determining cleartext filename length limitations...");
|
||||||
var checker = new FileSystemCapabilityChecker();
|
var checker = new FileSystemCapabilityChecker();
|
||||||
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
|
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
|
||||||
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
|
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
|
||||||
if (ciphertextLimit < shorteningThreshold) {
|
if (ciphertextLimit < shorteningThreshold) {
|
||||||
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
|
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
|
||||||
vaultSettings.maxCleartextFilenameLength().set(cleartextLimit);
|
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit);
|
||||||
} else {
|
} else {
|
||||||
vaultSettings.maxCleartextFilenameLength().setValue(UNLIMITED_FILENAME_LENGTH);
|
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vaultSettings.maxCleartextFilenameLength().get() < UNLIMITED_FILENAME_LENGTH) {
|
if (vaultSettings.maxCleartextFilenameLength.get() < UNLIMITED_FILENAME_LENGTH) {
|
||||||
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength().get());
|
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||||
.withKeyLoader(keyLoader) //
|
.withKeyLoader(keyLoader) //
|
||||||
.withFlags(flags) //
|
.withFlags(flags) //
|
||||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
|
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
||||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||||
.build();
|
.build();
|
||||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||||
@@ -253,11 +253,11 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyStringProperty displayNameProperty() {
|
public ReadOnlyStringProperty displayNameProperty() {
|
||||||
return vaultSettings.displayName();
|
return vaultSettings.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return vaultSettings.displayName().get();
|
return vaultSettings.displayName.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectBinding<Mountpoint> mountPointProperty() {
|
public ObjectBinding<Mountpoint> mountPointProperty() {
|
||||||
@@ -274,7 +274,7 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayablePath() {
|
public String getDisplayablePath() {
|
||||||
Path p = vaultSettings.path().get();
|
Path p = vaultSettings.path.get();
|
||||||
if (p.startsWith(HOME_DIR)) {
|
if (p.startsWith(HOME_DIR)) {
|
||||||
Path relativePath = HOME_DIR.relativize(p);
|
Path relativePath = HOME_DIR.relativize(p);
|
||||||
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
|
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
|
||||||
@@ -311,7 +311,11 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Path getPath() {
|
public Path getPath() {
|
||||||
return vaultSettings.path().getValue();
|
return vaultSettings.path.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoFileSystem getCryptoFileSystem() {
|
||||||
|
return cryptoFileSystem.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -322,23 +326,7 @@ public class Vault {
|
|||||||
* @throws IllegalStateException if the vault is not unlocked
|
* @throws IllegalStateException if the vault is not unlocked
|
||||||
*/
|
*/
|
||||||
public Path getCiphertextPath(Path cleartextPath) throws IOException {
|
public Path getCiphertextPath(Path cleartextPath) throws IOException {
|
||||||
if (!state.getValue().equals(VaultState.Value.UNLOCKED)) {
|
return state.get().getCiphertextPath(this, cleartextPath);
|
||||||
throw new IllegalStateException("Vault is not unlocked");
|
|
||||||
}
|
|
||||||
var fs = cryptoFileSystem.get();
|
|
||||||
var osPathSeparator = cleartextPath.getFileSystem().getSeparator();
|
|
||||||
var cryptoFsPathSeparator = fs.getSeparator();
|
|
||||||
|
|
||||||
if (getMountPoint() instanceof Mountpoint.WithPath mp) {
|
|
||||||
var absoluteCryptoFsPath = cryptoFsPathSeparator + mp.path().relativize(cleartextPath).toString();
|
|
||||||
if (!cryptoFsPathSeparator.equals(osPathSeparator)) {
|
|
||||||
absoluteCryptoFsPath = absoluteCryptoFsPath.replace(osPathSeparator, cryptoFsPathSeparator);
|
|
||||||
}
|
|
||||||
var cryptoPath = fs.getPath(absoluteCryptoFsPath);
|
|
||||||
return fs.getCiphertextPath(cryptoPath);
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("URI mount points not supported.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VaultConfigCache getVaultConfigCache() {
|
public VaultConfigCache getVaultConfigCache() {
|
||||||
@@ -346,7 +334,7 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return vaultSettings.getId();
|
return vaultSettings.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class VaultConfigCache {
|
|||||||
|
|
||||||
void reloadConfig() throws IOException {
|
void reloadConfig() throws IOException {
|
||||||
try {
|
try {
|
||||||
config.set(readConfigFromStorage(this.settings.path().get()));
|
config.set(readConfigFromStorage(this.settings.path.get()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
config.set(null);
|
config.set(null);
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ public class VaultListManager {
|
|||||||
this.vaultComponentFactory = vaultComponentFactory;
|
this.vaultComponentFactory = vaultComponentFactory;
|
||||||
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
||||||
|
|
||||||
addAll(settings.getDirectories());
|
addAll(settings.directories);
|
||||||
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
|
vaultList.addListener(new VaultListChangeListener(settings.directories));
|
||||||
autoLocker.init();
|
autoLocker.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +70,11 @@ public class VaultListManager {
|
|||||||
|
|
||||||
private VaultSettings newVaultSettings(Path path) {
|
private VaultSettings newVaultSettings(Path path) {
|
||||||
VaultSettings vaultSettings = VaultSettings.withRandomId();
|
VaultSettings vaultSettings = VaultSettings.withRandomId();
|
||||||
vaultSettings.path().set(path);
|
vaultSettings.path.set(path);
|
||||||
if (path.getFileName() != null) {
|
if (path.getFileName() != null) {
|
||||||
vaultSettings.displayName().set(path.getFileName().toString());
|
vaultSettings.displayName.set(path.getFileName().toString());
|
||||||
} else {
|
} else {
|
||||||
vaultSettings.displayName().set(defaultVaultName);
|
vaultSettings.displayName.set(defaultVaultName);
|
||||||
}
|
}
|
||||||
return vaultSettings;
|
return vaultSettings;
|
||||||
}
|
}
|
||||||
@@ -95,13 +95,13 @@ public class VaultListManager {
|
|||||||
private Vault create(VaultSettings vaultSettings) {
|
private Vault create(VaultSettings vaultSettings) {
|
||||||
var wrapper = new VaultConfigCache(vaultSettings);
|
var wrapper = new VaultConfigCache(vaultSettings);
|
||||||
try {
|
try {
|
||||||
var vaultState = determineVaultState(vaultSettings.path().get());
|
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
|
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||||
wrapper.reloadConfig();
|
wrapper.reloadConfig();
|
||||||
}
|
}
|
||||||
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
|
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
|
LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e);
|
||||||
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
|
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ import javax.inject.Inject;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ObservableObjectValue;
|
import javafx.beans.value.ObservableObjectValue;
|
||||||
import javafx.beans.value.ObservableValueBase;
|
import javafx.beans.value.ObservableValueBase;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.Condition;
|
import java.util.concurrent.locks.Condition;
|
||||||
@@ -43,12 +46,40 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
|||||||
/**
|
/**
|
||||||
* Vault is unlocked
|
* Vault is unlocked
|
||||||
*/
|
*/
|
||||||
UNLOCKED,
|
UNLOCKED {
|
||||||
|
/**
|
||||||
|
* Gets from the cleartext path its ciphertext counterpart.
|
||||||
|
*
|
||||||
|
* @return Local os path to the ciphertext resource
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
* @throws IllegalStateException if the vault is not unlocked
|
||||||
|
*/
|
||||||
|
Path getCiphertextPath(Vault vault, Path cleartextPath) throws IOException {
|
||||||
|
var fs = vault.getCryptoFileSystem();
|
||||||
|
var osPathSeparator = cleartextPath.getFileSystem().getSeparator();
|
||||||
|
var cryptoFsPathSeparator = fs.getSeparator();
|
||||||
|
|
||||||
|
if (vault.getMountPoint() instanceof Mountpoint.WithPath mp) {
|
||||||
|
var absoluteCryptoFsPath = cryptoFsPathSeparator + mp.path().relativize(cleartextPath).toString();
|
||||||
|
if (!cryptoFsPathSeparator.equals(osPathSeparator)) {
|
||||||
|
absoluteCryptoFsPath = absoluteCryptoFsPath.replace(osPathSeparator, cryptoFsPathSeparator);
|
||||||
|
}
|
||||||
|
var cryptoPath = fs.getPath(absoluteCryptoFsPath);
|
||||||
|
return fs.getCiphertextPath(cryptoPath);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("URI mount points not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unknown state due to preceding unrecoverable exceptions.
|
* Unknown state due to preceding unrecoverable exceptions.
|
||||||
*/
|
*/
|
||||||
ERROR;
|
ERROR;
|
||||||
|
|
||||||
|
Path getCiphertextPath(Vault vault, Path cleartextPath) throws IOException {
|
||||||
|
throw new IllegalStateException("Vault is not unlocked");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicReference<Value> value;
|
private final AtomicReference<Value> value;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import java.nio.channels.ReadableByteChannel;
|
|||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
// TODO make sealed, remove enum
|
//TODO can the enum be removed?
|
||||||
interface IpcMessage {
|
sealed interface IpcMessage permits HandleLaunchArgsMessage, RevealRunningAppMessage {
|
||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
|
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import java.util.List;
|
|||||||
public interface IpcMessageListener {
|
public interface IpcMessageListener {
|
||||||
|
|
||||||
default void handleMessage(IpcMessage message) {
|
default void handleMessage(IpcMessage message) {
|
||||||
if (message instanceof RevealRunningAppMessage) {
|
switch (message) {
|
||||||
revealRunningApp();
|
case RevealRunningAppMessage m -> revealRunningApp(); // TODO: rename to _ with JEP 443
|
||||||
} else if (message instanceof HandleLaunchArgsMessage m) {
|
case HandleLaunchArgsMessage m -> handleLaunchArgs(m.args());
|
||||||
handleLaunchArgs(m.args());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.SubstitutingProperties;
|
||||||
import org.cryptomator.common.ShutdownHook;
|
import org.cryptomator.common.ShutdownHook;
|
||||||
import org.cryptomator.ipc.IpcCommunicator;
|
import org.cryptomator.ipc.IpcCommunicator;
|
||||||
import org.cryptomator.logging.DebugMode;
|
import org.cryptomator.logging.DebugMode;
|
||||||
@@ -29,10 +30,18 @@ import java.util.concurrent.Executors;
|
|||||||
public class Cryptomator {
|
public class Cryptomator {
|
||||||
|
|
||||||
private static final long STARTUP_TIME = System.currentTimeMillis();
|
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||||
|
|
||||||
|
static {
|
||||||
|
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
|
||||||
|
System.setProperties(lazyProcessedProps);
|
||||||
|
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||||
|
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||||
|
}
|
||||||
|
|
||||||
// DaggerCryptomatorComponent gets generated by Dagger.
|
// DaggerCryptomatorComponent gets generated by Dagger.
|
||||||
// Run Maven and include target/generated-sources/annotations in your IDE.
|
// Run Maven and include target/generated-sources/annotations in your IDE.
|
||||||
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT;
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
private static final Logger LOG;
|
||||||
|
|
||||||
private final DebugMode debugMode;
|
private final DebugMode debugMode;
|
||||||
private final SupportedLanguages supportedLanguages;
|
private final SupportedLanguages supportedLanguages;
|
||||||
@@ -63,7 +72,6 @@ public class Cryptomator {
|
|||||||
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
|
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
||||||
LOG.info("Exit {}", exitCode);
|
LOG.info("Exit {}", exitCode);
|
||||||
System.exit(exitCode); // end remaining non-daemon threads.
|
System.exit(exitCode); // end remaining non-daemon threads.
|
||||||
@@ -86,7 +94,7 @@ public class Cryptomator {
|
|||||||
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
||||||
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
|
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
|
||||||
*/
|
*/
|
||||||
try (var communicator = IpcCommunicator.create(env.ipcSocketPath().toList())) {
|
try (var communicator = IpcCommunicator.create(env.getIpcSocketPath().toList())) {
|
||||||
if (communicator.isClient()) {
|
if (communicator.isClient()) {
|
||||||
communicator.sendHandleLaunchargs(List.of(args));
|
communicator.sendHandleLaunchargs(List.of(args));
|
||||||
communicator.sendRevealRunningApp();
|
communicator.sendRevealRunningApp();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@@ -14,26 +16,39 @@ import java.util.Locale;
|
|||||||
public class SupportedLanguages {
|
public class SupportedLanguages {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
|
||||||
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
|
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_".
|
||||||
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fil", "fa", "fr", "gl", "he", //
|
// "en" is not part of this list - it is always inserted at the top.
|
||||||
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "si", "sk", "sr", "sr-Latn", "sv", "sw", //
|
public static final List<String> LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", //
|
||||||
"ta", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", //
|
||||||
|
"ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
||||||
|
public static final String ENGLISH = "en";
|
||||||
|
|
||||||
@Nullable
|
private final List<String> sortedLanguageTags;
|
||||||
private final String preferredLanguage;
|
|
||||||
|
private final Locale preferredLocale;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SupportedLanguages(Settings settings) {
|
public SupportedLanguages(Settings settings) {
|
||||||
this.preferredLanguage = settings.languageProperty().get();
|
var preferredLanguage = settings.language.get();
|
||||||
|
preferredLocale = preferredLanguage == null ? Locale.getDefault() : Locale.forLanguageTag(preferredLanguage);
|
||||||
|
var collator = Collator.getInstance(preferredLocale);
|
||||||
|
collator.setStrength(Collator.PRIMARY);
|
||||||
|
var sorted = new ArrayList<String>();
|
||||||
|
sorted.add(0, null);
|
||||||
|
sorted.add(1, ENGLISH);
|
||||||
|
LANGUAGE_TAGS.stream() //
|
||||||
|
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
|
||||||
|
.forEach(sorted::add);
|
||||||
|
sortedLanguageTags = Collections.unmodifiableList(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyPreferred() {
|
public void applyPreferred() {
|
||||||
if (preferredLanguage == null) {
|
LOG.debug("Using locale {}", preferredLocale);
|
||||||
LOG.debug("Using system locale");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
|
|
||||||
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
|
|
||||||
Locale.setDefault(preferredLocale);
|
Locale.setDefault(preferredLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getLanguageTags() {
|
||||||
|
return sortedLanguageTags;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ public class DebugMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
setLogLevels(settings.debugMode().get());
|
setLogLevels(settings.debugMode.get());
|
||||||
settings.debugMode().addListener(this::logLevelChanged);
|
settings.debugMode.addListener(this::logLevelChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
|
private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
|
||||||
|
|||||||
@@ -5,21 +5,23 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.changepassword.NewPasswordController;
|
||||||
|
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
|
||||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxControllerKey;
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.NewPasswordController;
|
|
||||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
@@ -65,6 +67,13 @@ public abstract class AddVaultModule {
|
|||||||
return new SimpleStringProperty("");
|
return new SimpleStringProperty("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("shorteningThreshold")
|
||||||
|
@AddVaultWizardScoped
|
||||||
|
static IntegerProperty provideShorteningThreshold() {
|
||||||
|
return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@AddVaultWizardWindow
|
@AddVaultWizardWindow
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@@ -130,6 +139,13 @@ public abstract class AddVaultModule {
|
|||||||
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS);
|
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS)
|
||||||
|
@AddVaultWizardScoped
|
||||||
|
static Scene provideCreateNewVaultExpertSettingsScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@@ -181,4 +197,9 @@ public abstract class AddVaultModule {
|
|||||||
@FxControllerKey(AddVaultSuccessController.class)
|
@FxControllerKey(AddVaultSuccessController.class)
|
||||||
abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller);
|
abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(CreateNewVaultExpertSettingsController.class)
|
||||||
|
abstract FxController bindCreateNewVaultExpertSettingsController(CreateNewVaultExpertSettingsController controller);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package org.cryptomator.ui.addvaultwizard;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.controls.NumericTextField;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
@AddVaultWizardScoped
|
||||||
|
public class CreateNewVaultExpertSettingsController implements FxController {
|
||||||
|
|
||||||
|
public static final int MAX_SHORTENING_THRESHOLD = 220;
|
||||||
|
public static final int MIN_SHORTENING_THRESHOLD = 36;
|
||||||
|
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/en/1.7/security/architecture/#name-shortening";
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final Lazy<Application> application;
|
||||||
|
private final Lazy<Scene> chooseLocationScene;
|
||||||
|
private final Lazy<Scene> choosePasswordScene;
|
||||||
|
private final StringProperty vaultNameProperty;
|
||||||
|
private final ObjectProperty<Path> vaultPathProperty;
|
||||||
|
private final IntegerProperty shorteningThreshold;
|
||||||
|
|
||||||
|
private final BooleanBinding validShorteningThreshold;
|
||||||
|
|
||||||
|
//FXML
|
||||||
|
public Label vaultNameLabel;
|
||||||
|
public Label vaultPathLabel;
|
||||||
|
public CheckBox expertSettingsCheckBox;
|
||||||
|
public NumericTextField shorteningThresholdTextField;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateNewVaultExpertSettingsController(@AddVaultWizardWindow Stage window, //
|
||||||
|
Lazy<Application> application, //
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, //
|
||||||
|
@Named("vaultName") StringProperty vaultName, //
|
||||||
|
ObjectProperty<Path> vaultPath, //
|
||||||
|
@Named("shorteningThreshold") IntegerProperty shorteningThreshold) {
|
||||||
|
this.window = window;
|
||||||
|
this.application = application;
|
||||||
|
this.chooseLocationScene = chooseLocationScene;
|
||||||
|
this.choosePasswordScene = choosePasswordScene;
|
||||||
|
this.vaultNameProperty = vaultName;
|
||||||
|
this.vaultPathProperty = vaultPath;
|
||||||
|
this.shorteningThreshold = shorteningThreshold;
|
||||||
|
this.validShorteningThreshold = Bindings.createBooleanBinding(this::isValidShorteningThreshold, shorteningThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
vaultNameLabel.textProperty().bind(vaultNameProperty);
|
||||||
|
vaultPathLabel.textProperty().bind(vaultPathProperty.asString());
|
||||||
|
shorteningThresholdTextField.setPromptText(MIN_SHORTENING_THRESHOLD + "-" + MAX_SHORTENING_THRESHOLD);
|
||||||
|
shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
|
||||||
|
shorteningThresholdTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
try {
|
||||||
|
int intValue = Integer.parseInt(newValue);
|
||||||
|
shorteningThreshold.set(intValue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
shorteningThreshold.set(0); //the value is set to 0 to ensure that an invalid value assignment is detected during a NumberFormatException
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void toggleUseExpertSettings() {
|
||||||
|
if (!expertSettingsCheckBox.isSelected()) {
|
||||||
|
shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(chooseLocationScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void next() {
|
||||||
|
window.setScene(choosePasswordScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding validShorteningThresholdProperty() {
|
||||||
|
return validShorteningThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidShorteningThreshold() {
|
||||||
|
var value = shorteningThreshold.get();
|
||||||
|
return value >= MIN_SHORTENING_THRESHOLD && value <= MAX_SHORTENING_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openDocs() {
|
||||||
|
application.get().getHostServices().showDocument(DOCS_NAME_SHORTENING_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getVaultPath() {
|
||||||
|
return vaultPathProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVaultName() {
|
||||||
|
return vaultNameProperty.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package org.cryptomator.ui.addvaultwizard;
|
package org.cryptomator.ui.addvaultwizard;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.common.ObservableUtil;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPreset;
|
||||||
|
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
@@ -10,22 +13,19 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.Toggle;
|
import javafx.scene.control.Toggle;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.DirectoryChooser;
|
import javafx.stage.DirectoryChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -34,6 +34,9 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@@ -45,71 +48,78 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Lazy<Scene> chooseNameScene;
|
private final Lazy<Scene> chooseNameScene;
|
||||||
private final Lazy<Scene> choosePasswordScene;
|
private final Lazy<Scene> chooseExpertSettingsScene;
|
||||||
private final ObservedLocationPresets locationPresets;
|
private final List<RadioButton> locationPresetBtns;
|
||||||
private final ObjectProperty<Path> vaultPath;
|
private final ObjectProperty<Path> vaultPath;
|
||||||
private final StringProperty vaultName;
|
private final StringProperty vaultName;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final BooleanBinding validVaultPath;
|
private final ObservableValue<VaultPathStatus> vaultPathStatus;
|
||||||
|
private final ObservableValue<Boolean> validVaultPath;
|
||||||
private final BooleanProperty usePresetPath;
|
private final BooleanProperty usePresetPath;
|
||||||
private final StringProperty statusText;
|
|
||||||
private final ObjectProperty<Node> statusGraphic;
|
|
||||||
|
|
||||||
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
|
||||||
|
|
||||||
//FXML
|
//FXML
|
||||||
public ToggleGroup predefinedLocationToggler;
|
public ToggleGroup locationPresetsToggler;
|
||||||
public RadioButton iclouddriveRadioButton;
|
public VBox radioButtonVBox;
|
||||||
public RadioButton dropboxRadioButton;
|
|
||||||
public RadioButton gdriveRadioButton;
|
|
||||||
public RadioButton onedriveRadioButton;
|
|
||||||
public RadioButton megaRadioButton;
|
|
||||||
public RadioButton pcloudRadioButton;
|
|
||||||
public RadioButton customRadioButton;
|
public RadioButton customRadioButton;
|
||||||
public Label vaultPathStatus;
|
public Label locationStatusLabel;
|
||||||
public FontAwesome5IconView goodLocation;
|
public FontAwesome5IconView goodLocation;
|
||||||
public FontAwesome5IconView badLocation;
|
public FontAwesome5IconView badLocation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, //
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, //
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
|
||||||
|
ObjectProperty<Path> vaultPath, //
|
||||||
|
@Named("vaultName") StringProperty vaultName, //
|
||||||
|
ResourceBundle resourceBundle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.chooseNameScene = chooseNameScene;
|
this.chooseNameScene = chooseNameScene;
|
||||||
this.choosePasswordScene = choosePasswordScene;
|
this.chooseExpertSettingsScene = chooseExpertSettingsScene;
|
||||||
this.locationPresets = locationPresets;
|
|
||||||
this.vaultPath = vaultPath;
|
this.vaultPath = vaultPath;
|
||||||
this.vaultName = vaultName;
|
this.vaultName = vaultName;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.validVaultPath = Bindings.createBooleanBinding(this::validateVaultPathAndSetStatus, this.vaultPath);
|
this.vaultPathStatus = ObservableUtil.mapWithDefault(vaultPath, this::validatePath, new VaultPathStatus(false, "error.message"));
|
||||||
|
this.validVaultPath = ObservableUtil.mapWithDefault(vaultPathStatus, VaultPathStatus::valid, false);
|
||||||
|
this.vaultPathStatus.addListener(this::updateStatusLabel);
|
||||||
this.usePresetPath = new SimpleBooleanProperty();
|
this.usePresetPath = new SimpleBooleanProperty();
|
||||||
this.statusText = new SimpleStringProperty();
|
this.locationPresetBtns = LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
|
||||||
this.statusGraphic = new SimpleObjectProperty<>();
|
.flatMap(LocationPresetsProvider::getLocations) //
|
||||||
|
.sorted(Comparator.comparing(LocationPreset::name)) //
|
||||||
|
.map(preset -> { //
|
||||||
|
var btn = new RadioButton(preset.name());
|
||||||
|
btn.setUserData(preset.path());
|
||||||
|
return btn;
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateVaultPathAndSetStatus() {
|
private VaultPathStatus validatePath(Path p) throws NullPointerException {
|
||||||
final Path p = vaultPath.get();
|
if (!Files.exists(p.getParent())) {
|
||||||
if (p == null) {
|
return new VaultPathStatus(false, "addvaultwizard.new.locationDoesNotExist");
|
||||||
statusText.set("Error: Path is NULL.");
|
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!Files.exists(p.getParent())) {
|
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
|
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!isActuallyWritable(p.getParent())) {
|
} else if (!isActuallyWritable(p.getParent())) {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
|
return new VaultPathStatus(false, "addvaultwizard.new.locationIsNotWritable");
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else if (!Files.notExists(p)) {
|
} else if (!Files.notExists(p)) {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
|
return new VaultPathStatus(false, "addvaultwizard.new.fileAlreadyExists");
|
||||||
statusGraphic.set(badLocation);
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsOk"));
|
return new VaultPathStatus(true, "addvaultwizard.new.locationIsOk");
|
||||||
statusGraphic.set(goodLocation);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateStatusLabel(ObservableValue<? extends VaultPathStatus> observable, VaultPathStatus oldValue, VaultPathStatus newValue) {
|
||||||
|
if (newValue.valid()) {
|
||||||
|
locationStatusLabel.setGraphic(goodLocation);
|
||||||
|
locationStatusLabel.getStyleClass().remove("label-red");
|
||||||
|
locationStatusLabel.getStyleClass().add("label-muted");
|
||||||
|
} else {
|
||||||
|
locationStatusLabel.setGraphic(badLocation);
|
||||||
|
locationStatusLabel.getStyleClass().remove("label-muted");
|
||||||
|
locationStatusLabel.getStyleClass().add("label-red");
|
||||||
|
}
|
||||||
|
this.locationStatusLabel.setText(resourceBundle.getString(newValue.localizationKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isActuallyWritable(Path p) {
|
private boolean isActuallyWritable(Path p) {
|
||||||
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
|
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
|
||||||
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
|
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
|
||||||
@@ -127,26 +137,15 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
radioButtonVBox.getChildren().addAll(1, locationPresetBtns); //first item is the list header
|
||||||
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
locationPresetsToggler.getToggles().addAll(locationPresetBtns);
|
||||||
|
locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
|
||||||
|
usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
|
||||||
if (iclouddriveRadioButton.equals(newValue)) {
|
var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath);
|
||||||
vaultPath.set(locationPresets.getIclouddriveLocation().resolve(vaultName.get()));
|
vaultPath.set(storagePath.resolve(vaultName.get()));
|
||||||
} else if (dropboxRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getDropboxLocation().resolve(vaultName.get()));
|
|
||||||
} else if (gdriveRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
|
|
||||||
} else if (onedriveRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get()));
|
|
||||||
} else if (megaRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get()));
|
|
||||||
} else if (pcloudRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get()));
|
|
||||||
} else if (customRadioButton.equals(newValue)) {
|
|
||||||
vaultPath.set(customVaultPath.resolve(vaultName.get()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -156,10 +155,8 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void next() {
|
public void next() {
|
||||||
if (validateVaultPathAndSetStatus()) {
|
if (validVaultPath.getValue()) {
|
||||||
window.setScene(choosePasswordScene.get());
|
window.setScene(chooseExpertSettingsScene.get());
|
||||||
} else {
|
|
||||||
validVaultPath.invalidate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +176,12 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Internal classes */
|
||||||
|
|
||||||
|
private record VaultPathStatus(boolean valid, String localizationKey) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public Path getVaultPath() {
|
public Path getVaultPath() {
|
||||||
@@ -189,47 +192,28 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
return vaultPath;
|
return vaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding validVaultPathProperty() {
|
public ObservableValue<Boolean> validVaultPathProperty() {
|
||||||
return validVaultPath;
|
return validVaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getValidVaultPath() {
|
public boolean isValidVaultPath() {
|
||||||
return validVaultPath.get();
|
return validVaultPath.getValue();
|
||||||
}
|
|
||||||
|
|
||||||
public ObservedLocationPresets getObservedLocationPresets() {
|
|
||||||
return locationPresets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty usePresetPathProperty() {
|
public BooleanProperty usePresetPathProperty() {
|
||||||
return usePresetPath;
|
return usePresetPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getUsePresetPath() {
|
public boolean isUsePresetPath() {
|
||||||
return usePresetPath.get();
|
return usePresetPath.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding anyRadioButtonSelectedProperty() {
|
public BooleanBinding anyRadioButtonSelectedProperty() {
|
||||||
return predefinedLocationToggler.selectedToggleProperty().isNotNull();
|
return locationPresetsToggler.selectedToggleProperty().isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAnyRadioButtonSelected() {
|
public boolean isAnyRadioButtonSelected() {
|
||||||
return anyRadioButtonSelectedProperty().get();
|
return anyRadioButtonSelectedProperty().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringProperty statusTextProperty() {
|
|
||||||
return statusText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatusText() {
|
|
||||||
return statusText.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Node> statusGraphicProperty() {
|
|
||||||
return statusGraphic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getStatusGraphic() {
|
|
||||||
return statusGraphic.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
|
|||||||
import org.cryptomator.cryptolib.api.Masterkey;
|
import org.cryptomator.cryptolib.api.Masterkey;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||||
|
import org.cryptomator.ui.changepassword.NewPasswordController;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.NewPasswordController;
|
|
||||||
import org.cryptomator.ui.common.Tasks;
|
import org.cryptomator.ui.common.Tasks;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -26,6 +25,7 @@ import javax.inject.Named;
|
|||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
@@ -37,7 +37,6 @@ import javafx.scene.control.ToggleGroup;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -48,16 +47,16 @@ import java.util.ResourceBundle;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
|
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
|
||||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
public class CreateNewVaultPasswordController implements FxController {
|
public class CreateNewVaultPasswordController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class);
|
||||||
private static final URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); // TODO better place?
|
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Lazy<Scene> chooseLocationScene;
|
private final Lazy<Scene> chooseExpertSettingsScene;
|
||||||
private final Lazy<Scene> recoveryKeyScene;
|
private final Lazy<Scene> recoveryKeyScene;
|
||||||
private final Lazy<Scene> successScene;
|
private final Lazy<Scene> successScene;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
@@ -75,6 +74,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
|||||||
private final BooleanProperty processing;
|
private final BooleanProperty processing;
|
||||||
private final BooleanProperty readyToCreateVault;
|
private final BooleanProperty readyToCreateVault;
|
||||||
private final ObjectBinding<ContentDisplay> createVaultButtonState;
|
private final ObjectBinding<ContentDisplay> createVaultButtonState;
|
||||||
|
private final IntegerProperty shorteningThreshold;
|
||||||
|
|
||||||
/* FXML */
|
/* FXML */
|
||||||
public ToggleGroup recoveryKeyChoice;
|
public ToggleGroup recoveryKeyChoice;
|
||||||
@@ -83,9 +83,25 @@ public class CreateNewVaultPasswordController implements FxController {
|
|||||||
public NewPasswordController newPasswordSceneController;
|
public NewPasswordController newPasswordSceneController;
|
||||||
|
|
||||||
@Inject
|
@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, 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) {
|
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, //
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
|
||||||
|
@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, //
|
||||||
|
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
|
||||||
|
ReadmeGenerator readmeGenerator, //
|
||||||
|
SecureRandom csprng, //
|
||||||
|
MasterkeyFileAccess masterkeyFileAccess) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.chooseLocationScene = chooseLocationScene;
|
this.chooseExpertSettingsScene = chooseExpertSettingsScene;
|
||||||
this.recoveryKeyScene = recoveryKeyScene;
|
this.recoveryKeyScene = recoveryKeyScene;
|
||||||
this.successScene = successScene;
|
this.successScene = successScene;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
@@ -103,6 +119,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
|||||||
this.processing = new SimpleBooleanProperty();
|
this.processing = new SimpleBooleanProperty();
|
||||||
this.readyToCreateVault = new SimpleBooleanProperty();
|
this.readyToCreateVault = new SimpleBooleanProperty();
|
||||||
this.createVaultButtonState = Bindings.when(processing).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
|
this.createVaultButtonState = Bindings.when(processing).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
|
||||||
|
this.shorteningThreshold = shorteningThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -116,7 +133,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void back() {
|
public void back() {
|
||||||
window.setScene(chooseLocationScene.get());
|
window.setScene(chooseExpertSettingsScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -176,7 +193,11 @@ public class CreateNewVaultPasswordController implements FxController {
|
|||||||
// 2. initialize vault:
|
// 2. initialize vault:
|
||||||
try {
|
try {
|
||||||
MasterkeyLoader loader = ignored -> masterkey.copy();
|
MasterkeyLoader loader = ignored -> masterkey.copy();
|
||||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(CryptorProvider.Scheme.SIV_GCM).withKeyLoader(loader).build();
|
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||||
|
.withCipherCombo(CryptorProvider.Scheme.SIV_GCM) //
|
||||||
|
.withKeyLoader(loader) //
|
||||||
|
.withShorteningThreshold(shorteningThreshold.get()) //
|
||||||
|
.build();
|
||||||
CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID);
|
CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID);
|
||||||
|
|
||||||
// 3. write vault-internal readme file:
|
// 3. write vault-internal readme file:
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
package org.cryptomator.ui.addvaultwizard;
|
|
||||||
|
|
||||||
import org.cryptomator.common.LocationPreset;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
|
||||||
public class ObservedLocationPresets {
|
|
||||||
|
|
||||||
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> dropboxLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> gdriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> onedriveLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> megaLocation;
|
|
||||||
private final ReadOnlyObjectProperty<Path> pcloudLocation;
|
|
||||||
private final BooleanBinding foundIclouddrive;
|
|
||||||
private final BooleanBinding foundDropbox;
|
|
||||||
private final BooleanBinding foundGdrive;
|
|
||||||
private final BooleanBinding foundOnedrive;
|
|
||||||
private final BooleanBinding foundMega;
|
|
||||||
private final BooleanBinding foundPcloud;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ObservedLocationPresets() {
|
|
||||||
this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath());
|
|
||||||
this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath());
|
|
||||||
this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath());
|
|
||||||
this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath());
|
|
||||||
this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath());
|
|
||||||
this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath());
|
|
||||||
this.foundIclouddrive = iclouddriveLocation.isNotNull();
|
|
||||||
this.foundDropbox = dropboxLocation.isNotNull();
|
|
||||||
this.foundGdrive = gdriveLocation.isNotNull();
|
|
||||||
this.foundOnedrive = onedriveLocation.isNotNull();
|
|
||||||
this.foundMega = megaLocation.isNotNull();
|
|
||||||
this.foundPcloud = pcloudLocation.isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Observables */
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
|
|
||||||
return iclouddriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getIclouddriveLocation() {
|
|
||||||
return iclouddriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundIclouddriveProperty() {
|
|
||||||
return foundIclouddrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundIclouddrive() {
|
|
||||||
return foundIclouddrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> dropboxLocationProperty() {
|
|
||||||
return dropboxLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getDropboxLocation() {
|
|
||||||
return dropboxLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundDropboxProperty() {
|
|
||||||
return foundDropbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundDropbox() {
|
|
||||||
return foundDropbox.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> gdriveLocationProperty() {
|
|
||||||
return gdriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getGdriveLocation() {
|
|
||||||
return gdriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundGdriveProperty() {
|
|
||||||
return foundGdrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundGdrive() {
|
|
||||||
return foundGdrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> onedriveLocationProperty() {
|
|
||||||
return onedriveLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getOnedriveLocation() {
|
|
||||||
return onedriveLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundOnedriveProperty() {
|
|
||||||
return foundOnedrive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundOnedrive() {
|
|
||||||
return foundOnedrive.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> megaLocationProperty() {
|
|
||||||
return megaLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getMegaLocation() {
|
|
||||||
return megaLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundMegaProperty() {
|
|
||||||
return foundMega;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundMega() {
|
|
||||||
return foundMega.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<Path> pcloudLocationProperty() {
|
|
||||||
return pcloudLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getPcloudLocation() {
|
|
||||||
return pcloudLocation.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding foundPcloudProperty() {
|
|
||||||
return foundPcloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFoundPcloud() {
|
|
||||||
return foundPcloud.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
|||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.ui.common.Animations;
|
import org.cryptomator.ui.common.Animations;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.NewPasswordController;
|
|
||||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import org.cryptomator.ui.common.FxControllerKey;
|
|||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.NewPasswordController;
|
|
||||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.cryptomator.ui.common;
|
package org.cryptomator.ui.changepassword;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Passphrase;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||||
|
|
||||||
@@ -91,4 +93,8 @@ public class NewPasswordController implements FxController {
|
|||||||
return passwordStrength.get();
|
return passwordStrength.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Passphrase getNewPassword() {
|
||||||
|
return passwordField.getCharacters();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* Contributors:
|
* Contributors:
|
||||||
* Jean-Noël Charon - initial API and implementation
|
* Jean-Noël Charon - initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui.common;
|
package org.cryptomator.ui.changepassword;
|
||||||
|
|
||||||
import com.nulabinc.zxcvbn.Zxcvbn;
|
import com.nulabinc.zxcvbn.Zxcvbn;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
@@ -36,7 +36,7 @@ public class DefaultSceneFactory implements Function<Parent, Scene> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void configureRoot(Parent root) {
|
protected void configureRoot(Parent root) {
|
||||||
root.nodeOrientationProperty().bind(settings.userInterfaceOrientation());
|
root.nodeOrientationProperty().bind(settings.userInterfaceOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureScene(Scene scene) {
|
protected void configureScene(Scene scene) {
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
package org.cryptomator.ui.common;
|
|
||||||
|
|
||||||
import org.cryptomator.common.Environment;
|
|
||||||
import org.cryptomator.common.ErrorCode;
|
|
||||||
import org.cryptomator.common.Nullable;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.input.Clipboard;
|
|
||||||
import javafx.scene.input.ClipboardContent;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class ErrorController implements FxController {
|
|
||||||
|
|
||||||
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
|
|
||||||
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
|
|
||||||
private static final String SEARCH_ERRORCODE_DELIM = " OR ";
|
|
||||||
private static final String REPORT_BODY_TEMPLATE = """
|
|
||||||
OS: %s / %s
|
|
||||||
App: %s / %s
|
|
||||||
|
|
||||||
<!-- ✏ Please describe what happened as accurately as possible. -->
|
|
||||||
|
|
||||||
<!-- 📋 Please also copy and paste the detail text from the error window. -->
|
|
||||||
|
|
||||||
<!-- ℹ Text enclosed like this (chevrons, exclamation mark, two dashes) is not visible to others! -->
|
|
||||||
|
|
||||||
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
|
|
||||||
""";
|
|
||||||
|
|
||||||
private final Application application;
|
|
||||||
private final String stackTrace;
|
|
||||||
private final ErrorCode errorCode;
|
|
||||||
private final Scene previousScene;
|
|
||||||
private final Stage window;
|
|
||||||
private final Environment environment;
|
|
||||||
|
|
||||||
private BooleanProperty copiedDetails = new SimpleBooleanProperty();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment) {
|
|
||||||
this.application = application;
|
|
||||||
this.stackTrace = stackTrace;
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.previousScene = previousScene;
|
|
||||||
this.window = window;
|
|
||||||
this.environment = environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void back() {
|
|
||||||
if (previousScene != null) {
|
|
||||||
window.setScene(previousScene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void close() {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void searchError() {
|
|
||||||
var searchTerm = URLEncoder.encode(getErrorCode().replace(ErrorCode.DELIM, SEARCH_ERRORCODE_DELIM), StandardCharsets.UTF_8);
|
|
||||||
application.getHostServices().showDocument(SEARCH_URL_FORMAT.formatted(searchTerm));
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void reportError() {
|
|
||||||
var title = URLEncoder.encode(getErrorCode(), StandardCharsets.UTF_8);
|
|
||||||
var enhancedTemplate = String.format(REPORT_BODY_TEMPLATE, //
|
|
||||||
System.getProperty("os.name"), //
|
|
||||||
System.getProperty("os.version"), //
|
|
||||||
environment.getAppVersion(), //
|
|
||||||
environment.getBuildNumber().orElse("undefined"));
|
|
||||||
var body = URLEncoder.encode(enhancedTemplate, StandardCharsets.UTF_8);
|
|
||||||
application.getHostServices().showDocument(REPORT_URL_FORMAT.formatted(title, body));
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void copyDetails() {
|
|
||||||
ClipboardContent clipboardContent = new ClipboardContent();
|
|
||||||
clipboardContent.putString(getDetailText());
|
|
||||||
Clipboard.getSystemClipboard().setContent(clipboardContent);
|
|
||||||
|
|
||||||
copiedDetails.set(true);
|
|
||||||
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
|
|
||||||
copiedDetails.set(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter/Setter */
|
|
||||||
|
|
||||||
public boolean isPreviousScenePresent() {
|
|
||||||
return previousScene != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStackTrace() {
|
|
||||||
return stackTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getErrorCode() {
|
|
||||||
return errorCode.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDetailText() {
|
|
||||||
return "```\nError Code " + getErrorCode() + "\n" + getStackTrace() + "\n```";
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty copiedDetailsProperty() {
|
|
||||||
return copiedDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getCopiedDetails() {
|
|
||||||
return copiedDetails.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,15 @@ public enum FxmlFile {
|
|||||||
ADDVAULT_EXISTING("/fxml/addvault_existing.fxml"), //
|
ADDVAULT_EXISTING("/fxml/addvault_existing.fxml"), //
|
||||||
ADDVAULT_NEW_NAME("/fxml/addvault_new_name.fxml"), //
|
ADDVAULT_NEW_NAME("/fxml/addvault_new_name.fxml"), //
|
||||||
ADDVAULT_NEW_LOCATION("/fxml/addvault_new_location.fxml"), //
|
ADDVAULT_NEW_LOCATION("/fxml/addvault_new_location.fxml"), //
|
||||||
|
ADDVAULT_NEW_EXPERT_SETTINGS("/fxml/addvault_new_expert_settings.fxml"), //
|
||||||
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
|
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
|
||||||
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
|
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
|
||||||
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
||||||
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
|
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
|
||||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||||
|
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
|
||||||
|
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
||||||
|
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
|
||||||
ERROR("/fxml/error.fxml"), //
|
ERROR("/fxml/error.fxml"), //
|
||||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||||
HEALTH_START("/fxml/health_start.fxml"), //
|
HEALTH_START("/fxml/health_start.fxml"), //
|
||||||
@@ -38,6 +42,7 @@ public enum FxmlFile {
|
|||||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||||
|
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
||||||
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
||||||
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
|
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
|
||||||
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public enum FontAwesome5Icon {
|
|||||||
COPY("\uF0C5"), //
|
COPY("\uF0C5"), //
|
||||||
CROWN("\uF521"), //
|
CROWN("\uF521"), //
|
||||||
EDIT("\uF044"), //
|
EDIT("\uF044"), //
|
||||||
|
EXCHANGE_ALT("\uF362"), //
|
||||||
EXCLAMATION("\uF12A"), //
|
EXCLAMATION("\uF12A"), //
|
||||||
EXCLAMATION_CIRCLE("\uF06A"), //
|
EXCLAMATION_CIRCLE("\uF06A"), //
|
||||||
EXCLAMATION_TRIANGLE("\uF071"), //
|
EXCLAMATION_TRIANGLE("\uF071"), //
|
||||||
@@ -39,6 +40,7 @@ public enum FontAwesome5Icon {
|
|||||||
LOCK("\uF023"), //
|
LOCK("\uF023"), //
|
||||||
LOCK_OPEN("\uF3C1"), //
|
LOCK_OPEN("\uF3C1"), //
|
||||||
MAGIC("\uF0D0"), //
|
MAGIC("\uF0D0"), //
|
||||||
|
PENCIL("\uF303"), //
|
||||||
PLUS("\uF067"), //
|
PLUS("\uF067"), //
|
||||||
PRINT("\uF02F"), //
|
PRINT("\uF02F"), //
|
||||||
QUESTION("\uF128"), //
|
QUESTION("\uF128"), //
|
||||||
|
|||||||
@@ -13,18 +13,19 @@ public class FormattedLabel extends Label {
|
|||||||
private final StringProperty format = new SimpleStringProperty("");
|
private final StringProperty format = new SimpleStringProperty("");
|
||||||
private final ObjectProperty<Object> arg1 = new SimpleObjectProperty<>();
|
private final ObjectProperty<Object> arg1 = new SimpleObjectProperty<>();
|
||||||
private final ObjectProperty<Object> arg2 = new SimpleObjectProperty<>();
|
private final ObjectProperty<Object> arg2 = new SimpleObjectProperty<>();
|
||||||
// add arg2, arg3, ... on demand
|
private final ObjectProperty<Object> arg3 = new SimpleObjectProperty<>();
|
||||||
|
// add arg4, arg5, ... on demand
|
||||||
|
|
||||||
public FormattedLabel() {
|
public FormattedLabel() {
|
||||||
textProperty().bind(createStringBinding());
|
textProperty().bind(createStringBinding());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected StringBinding createStringBinding() {
|
protected StringBinding createStringBinding() {
|
||||||
return Bindings.createStringBinding(this::updateText, format, arg1, arg2);
|
return Bindings.createStringBinding(this::updateText, format, arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String updateText() {
|
private String updateText() {
|
||||||
return String.format(format.get(), arg1.get(), arg2.get());
|
return String.format(format.get(), arg1.get(), arg2.get(), arg3.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Observables */
|
/* Observables */
|
||||||
@@ -64,4 +65,16 @@ public class FormattedLabel extends Label {
|
|||||||
public void setArg2(Object arg2) {
|
public void setArg2(Object arg2) {
|
||||||
this.arg2.set(arg2);
|
this.arg2.set(arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<Object> arg3Property() {
|
||||||
|
return arg3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getArg3() {
|
||||||
|
return arg3.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArg3(Object arg3) {
|
||||||
|
this.arg3.set(arg3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class SecurePasswordField extends TextField {
|
|||||||
private static final char WIPE_CHAR = ' ';
|
private static final char WIPE_CHAR = ' ';
|
||||||
private static final int INITIAL_BUFFER_SIZE = 50;
|
private static final int INITIAL_BUFFER_SIZE = 50;
|
||||||
private static final int GROW_BUFFER_SIZE = 50;
|
private static final int GROW_BUFFER_SIZE = 50;
|
||||||
private static final String DEFAULT_PLACEHOLDER = "●";
|
private static final String DEFAULT_PLACEHOLDER = "•";
|
||||||
private static final String STYLE_CLASS = "secure-password-field";
|
private static final String STYLE_CLASS = "secure-password-field";
|
||||||
private static final KeyCodeCombination SHORTCUT_BACKSPACE = new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCombination.SHORTCUT_DOWN);
|
private static final KeyCodeCombination SHORTCUT_BACKSPACE = new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCombination.SHORTCUT_DOWN);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the accompanying LICENSE file.
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.cryptomator.ui.convertvault;
|
||||||
|
|
||||||
|
import dagger.BindsInstance;
|
||||||
|
import dagger.Lazy;
|
||||||
|
import dagger.Subcomponent;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
@ConvertVaultScoped
|
||||||
|
@Subcomponent(modules = {ConvertVaultModule.class})
|
||||||
|
public interface ConvertVaultComponent {
|
||||||
|
|
||||||
|
@ConvertVaultWindow
|
||||||
|
Stage window();
|
||||||
|
|
||||||
|
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START)
|
||||||
|
Lazy<Scene> hubToPasswordScene();
|
||||||
|
|
||||||
|
default void showHubToPasswordWindow() {
|
||||||
|
Stage stage = window();
|
||||||
|
stage.setScene(hubToPasswordScene().get());
|
||||||
|
stage.sizeToScene();
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcomponent.Factory
|
||||||
|
interface Factory {
|
||||||
|
|
||||||
|
ConvertVaultComponent create(@BindsInstance @ConvertVaultWindow Vault vault, @BindsInstance @Named("convertVaultOwner") Stage owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package org.cryptomator.ui.convertvault;
|
||||||
|
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.VaultConfig;
|
||||||
|
import org.cryptomator.ui.changepassword.NewPasswordController;
|
||||||
|
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
|
||||||
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
|
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||||
|
import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
abstract class ConvertVaultModule {
|
||||||
|
|
||||||
|
//TODO: if this fails, we cannot display an error
|
||||||
|
@Provides
|
||||||
|
@ConvertVaultWindow
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static VaultConfig.UnverifiedVaultConfig vaultConfig(@ConvertVaultWindow Vault vault) {
|
||||||
|
try {
|
||||||
|
return vault.getVaultConfigCache().get();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ConvertVaultWindow
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static StringProperty provideRecoveryKeyProperty() {
|
||||||
|
return new SimpleStringProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ConvertVaultWindow
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||||
|
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ConvertVaultWindow
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static Stage provideStage(StageFactory factory, @Named("convertVaultOwner") Stage owner, ResourceBundle resourceBundle) {
|
||||||
|
Stage stage = factory.create();
|
||||||
|
stage.setResizable(false);
|
||||||
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
|
stage.initOwner(owner);
|
||||||
|
stage.setTitle(resourceBundle.getString("convertVault.title"));
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START)
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static Scene provideHubToPasswordStartScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_CONVERT)
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static Scene provideHubToPasswordConvertScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_CONVERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_SUCCESS)
|
||||||
|
@ConvertVaultScoped
|
||||||
|
static Scene provideHubToPasswordSuccessScene(@ConvertVaultWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.CONVERTVAULT_HUBTOPASSWORD_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(HubToPasswordStartController.class)
|
||||||
|
abstract FxController bindHubToPasswordStartController(HubToPasswordStartController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(HubToPasswordConvertController.class)
|
||||||
|
abstract FxController bindHubToPasswordConvertController(HubToPasswordConvertController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(HubToPasswordSuccessController.class)
|
||||||
|
abstract FxController bindHubToPasswordSuccessController(HubToPasswordSuccessController controller);
|
||||||
|
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(NewPasswordController.class)
|
||||||
|
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
|
||||||
|
return new NewPasswordController(resourceBundle, strengthRater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(RecoveryKeyValidateController.class)
|
||||||
|
static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
|
||||||
|
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user