mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
559
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c40386106 | ||
|
|
c865805f55 | ||
|
|
8e352301cd | ||
|
|
52db1e8bdd | ||
|
|
1947623be8 | ||
|
|
4f8602684d | ||
|
|
75f66e40bf | ||
|
|
73587437cb | ||
|
|
18f6e70bba | ||
|
|
e067b94aca | ||
|
|
b04d5669fb | ||
|
|
ad44af338d | ||
|
|
4f4ddbc3e0 | ||
|
|
4e9af1c713 | ||
|
|
9a94b6f95e | ||
|
|
371b826aa0 | ||
|
|
d3fb90bf6d | ||
|
|
d24c478faa | ||
|
|
48d0aa423c | ||
|
|
5695fa2916 | ||
|
|
bcea1a4b32 | ||
|
|
2f8d4568fc | ||
|
|
5b55318e52 | ||
|
|
459f2ea2c3 | ||
|
|
d26147a34a | ||
|
|
afb9d4f010 | ||
|
|
db3bf9723e | ||
|
|
3a5937b35f | ||
|
|
f5cddda7a8 | ||
|
|
e9fa1d6476 | ||
|
|
084c1881ba | ||
|
|
ce131e4653 | ||
|
|
a121c7d0fe | ||
|
|
603bf99b25 | ||
|
|
9076fe5b46 | ||
|
|
6848f1a38e | ||
|
|
f4103fc917 | ||
|
|
c838da9df4 | ||
|
|
78df4e24b3 | ||
|
|
c0e2d01297 | ||
|
|
313f8b36d2 | ||
|
|
343794c1fe | ||
|
|
813c01aaed | ||
|
|
510ea8a6f6 | ||
|
|
a22fbea467 | ||
|
|
f64144d1da | ||
|
|
c01dd225c9 | ||
|
|
18b408a83a | ||
|
|
efaf107b20 | ||
|
|
86cba5e8a7 | ||
|
|
f4cee6d161 | ||
|
|
28c40b2277 | ||
|
|
8853054ed4 | ||
|
|
3eb44b06af | ||
|
|
1554437884 | ||
|
|
268bae8879 | ||
|
|
b382e436e2 | ||
|
|
6ccd248675 | ||
|
|
c760e63995 | ||
|
|
959dee153e | ||
|
|
aa22f656e8 | ||
|
|
d24734e16f | ||
|
|
01522e8c97 | ||
|
|
14e056551d | ||
|
|
82a42ea183 | ||
|
|
bfde26be79 | ||
|
|
51e43a0567 | ||
|
|
9a375cb3f6 | ||
|
|
2b8f39a149 | ||
|
|
2720a999d1 | ||
|
|
f675bd5017 | ||
|
|
16be3e7a97 | ||
|
|
551d9d1af1 | ||
|
|
0acc8ddb0a | ||
|
|
f0e1bcb899 | ||
|
|
d331c30224 | ||
|
|
d0d39a1c72 | ||
|
|
8904df91b4 | ||
|
|
786365c095 | ||
|
|
eb8f5f523d | ||
|
|
9c22adc97d | ||
|
|
265212cc0f | ||
|
|
9c860cc17c | ||
|
|
7f63153bc1 | ||
|
|
9531d44ab5 | ||
|
|
253abd4930 | ||
|
|
022ba81f2f | ||
|
|
26160e9bbc | ||
|
|
0a32de2c92 | ||
|
|
60aeb6a04b | ||
|
|
0206fc0b8f | ||
|
|
06b3343a6d | ||
|
|
598bec3050 | ||
|
|
0e5d72c22a | ||
|
|
21f4fab9ed | ||
|
|
58e2ec4742 | ||
|
|
6b6867b826 | ||
|
|
92321a7569 | ||
|
|
7781572b6d | ||
|
|
b5efe39eb8 | ||
|
|
cae86cd71f | ||
|
|
667617b480 | ||
|
|
9329311491 | ||
|
|
22438d1eba | ||
|
|
d295084312 | ||
|
|
ab538cd215 | ||
|
|
f9cb438a0b | ||
|
|
354de225ed | ||
|
|
0b7f8d71ed | ||
|
|
9657a13912 | ||
|
|
c5a3b29554 | ||
|
|
ae957c2588 | ||
|
|
da84133732 | ||
|
|
10275eb80f | ||
|
|
29a9c7dffe | ||
|
|
d5b8996a39 | ||
|
|
f012977aac | ||
|
|
8680bd1dee | ||
|
|
528510eb53 | ||
|
|
e8d17cfb7b | ||
|
|
cdbcd20241 | ||
|
|
fef3ade5c6 | ||
|
|
194d7f0da9 | ||
|
|
f891a9f1a7 | ||
|
|
2a335eb7af | ||
|
|
6578ac0121 | ||
|
|
83f9514a42 | ||
|
|
c92f1aa655 | ||
|
|
688993ba37 | ||
|
|
ddfc8d5bc3 | ||
|
|
c3141e3601 | ||
|
|
f49974180b | ||
|
|
eb6ade49f6 | ||
|
|
6553c04256 | ||
|
|
a8cb015a06 | ||
|
|
ee81dca71c | ||
|
|
ce06ddf312 | ||
|
|
fee2d4bc0c | ||
|
|
a2a990bd0c | ||
|
|
cedbe6523d | ||
|
|
ec64055d93 | ||
|
|
6f542ac8b8 | ||
|
|
8a3c101764 | ||
|
|
49a8689090 | ||
|
|
8f5c4997b1 | ||
|
|
900fdd7f6f | ||
|
|
4723416d45 | ||
|
|
8483f71f57 | ||
|
|
5dac594cc9 | ||
|
|
06f4e160d6 | ||
|
|
1d38ee2fcb | ||
|
|
921b70ebaa | ||
|
|
0f56d424da | ||
|
|
4cd43a2b1c | ||
|
|
1ef359e68b | ||
|
|
58ce1e45b8 | ||
|
|
bd854e7f7a | ||
|
|
3ecc0cf906 | ||
|
|
ea6925f905 | ||
|
|
f45b78d8c0 | ||
|
|
75ca8a91f8 | ||
|
|
16462d05c8 | ||
|
|
cbef54f05c | ||
|
|
f601ff4ce3 | ||
|
|
84fbf7392b | ||
|
|
bbf9379f56 | ||
|
|
6aa14fcfb1 | ||
|
|
710c7d0da7 | ||
|
|
841d4eb9c7 | ||
|
|
8c4d35d3db | ||
|
|
4110057fa1 | ||
|
|
9b1d6d2f2f | ||
|
|
1c45455cb2 | ||
|
|
411aca8695 | ||
|
|
3de69021df | ||
|
|
8418038736 | ||
|
|
3bf2b499a7 | ||
|
|
430da2b78d | ||
|
|
51047dcbce | ||
|
|
708bcaa630 | ||
|
|
724b20c826 | ||
|
|
69f8c46f15 | ||
|
|
b7f2fb0bdf | ||
|
|
222ffd8c53 | ||
|
|
00e0a0f79a | ||
|
|
d6bbc4383b | ||
|
|
b51a2602a9 | ||
|
|
c3fbddc671 | ||
|
|
ce262b691a | ||
|
|
65de6fef9b | ||
|
|
31d95ae5b0 | ||
|
|
429098733c | ||
|
|
597899d2bf | ||
|
|
b1c66b181d | ||
|
|
100b836979 | ||
|
|
817907c25a | ||
|
|
25195fffe2 | ||
|
|
0e784a6ffc | ||
|
|
351f96fa8b | ||
|
|
703fb4fb51 | ||
|
|
a975df6f8c | ||
|
|
fb90128b63 | ||
|
|
ea4c91fb37 | ||
|
|
7c1d6973ba | ||
|
|
4f2120b729 | ||
|
|
51f7d74e4a | ||
|
|
32a810fe1d | ||
|
|
570482521a | ||
|
|
3bd2a2f911 | ||
|
|
1ac6d086bd | ||
|
|
8374599183 | ||
|
|
0091c401df | ||
|
|
00dd67e345 | ||
|
|
158677bd54 | ||
|
|
a1b375316c | ||
|
|
dae2814b0f | ||
|
|
341a98848d | ||
|
|
902e66906a | ||
|
|
c59d3cd03f | ||
|
|
ed9f16e012 | ||
|
|
ed77a4aacd | ||
|
|
ff2fa70bd6 | ||
|
|
3519346df9 | ||
|
|
45f6caa7bd | ||
|
|
e2e7755af2 | ||
|
|
4ab7bfaa20 | ||
|
|
7e0ffb43a6 | ||
|
|
526c8328f6 | ||
|
|
81bad4eb25 | ||
|
|
dcaa6e81a3 | ||
|
|
e78155396d | ||
|
|
d3063c8117 | ||
|
|
cd1ed088c3 | ||
|
|
e831debb94 | ||
|
|
8d15a6612f | ||
|
|
9c034f3be6 | ||
|
|
60dd9349a8 | ||
|
|
330b1cbf7d | ||
|
|
9ffa38d54e | ||
|
|
c925727269 | ||
|
|
ac2dc06bd6 | ||
|
|
80b83adb53 | ||
|
|
ddc31ac2bb | ||
|
|
eb7c5d0b2b | ||
|
|
e5ce3cb62d | ||
|
|
7a69692999 | ||
|
|
d00d9d98dc | ||
|
|
1846c5c14c | ||
|
|
0d9db783f7 | ||
|
|
d3a6964d8f | ||
|
|
55e9cabf51 | ||
|
|
5180f2061a | ||
|
|
05154cf6aa | ||
|
|
f61e955945 | ||
|
|
d80605532f | ||
|
|
8d5fb14b50 | ||
|
|
570286c7df | ||
|
|
11e3ee8d90 | ||
|
|
0664c670f7 | ||
|
|
f36f9d412c | ||
|
|
bd734f1960 | ||
|
|
675a0b1a73 | ||
|
|
a2816277bf | ||
|
|
e37f1f914b | ||
|
|
adf7694308 | ||
|
|
c13449c6ad | ||
|
|
f72035210c | ||
|
|
89ef5238ea | ||
|
|
8198f66c1f | ||
|
|
eb5aa4ee44 | ||
|
|
adb09a0efe | ||
|
|
241eb8bed5 | ||
|
|
842a0d6ff3 | ||
|
|
3200917df2 | ||
|
|
12dcf0647d | ||
|
|
22859c9ffa | ||
|
|
29182156df | ||
|
|
357d63f398 | ||
|
|
e594bf208d | ||
|
|
2f0de3520a | ||
|
|
8def68eb02 | ||
|
|
aef33dc864 | ||
|
|
06d2f2d9e9 | ||
|
|
dad0ad76fb | ||
|
|
99fa8e7c8e | ||
|
|
ab0f175edf | ||
|
|
8cb9728565 | ||
|
|
d91d27f2a4 | ||
|
|
b2a6e038ae | ||
|
|
75f360903c | ||
|
|
79c3137b90 | ||
|
|
d2189d379c | ||
|
|
49aead7323 | ||
|
|
7bd610563f | ||
|
|
5c1a1ad162 | ||
|
|
86906d0049 | ||
|
|
93011dc754 | ||
|
|
b084b651af | ||
|
|
fecf9c0423 | ||
|
|
117fe78a4a | ||
|
|
153d43573a | ||
|
|
9145f5d2f8 | ||
|
|
835ea3b640 | ||
|
|
1e7eb23d1b | ||
|
|
cfe25d0bf5 | ||
|
|
b14939bd77 | ||
|
|
26e140ee22 | ||
|
|
1c5ecf8c01 | ||
|
|
9b528a05b5 | ||
|
|
3a7aa6d64f | ||
|
|
55820e47f9 | ||
|
|
94af8bd15a | ||
|
|
4712c4f593 | ||
|
|
42c856fc6f | ||
|
|
3618c4b8db | ||
|
|
bb681fa6d9 | ||
|
|
22e3840caa | ||
|
|
461ec3ca43 | ||
|
|
8aa3da14a4 | ||
|
|
670d4a165c | ||
|
|
9a9b19e6e2 | ||
|
|
206ba4c69a | ||
|
|
1702c6a243 | ||
|
|
8d2fe2fc03 | ||
|
|
4864eb3204 | ||
|
|
5721b63135 | ||
|
|
c99e0ea656 | ||
|
|
b1dc983d6b | ||
|
|
808223d58e | ||
|
|
4d5f6cbb52 | ||
|
|
fcdbc7a6cc | ||
|
|
113b745050 | ||
|
|
e4cde7f66f | ||
|
|
f6c834fee2 | ||
|
|
96990788d9 | ||
|
|
c204ed2601 | ||
|
|
b882296c19 | ||
|
|
186ed5c115 | ||
|
|
db29513376 | ||
|
|
0469e99baa | ||
|
|
37fcae8f0e | ||
|
|
2902479fc1 | ||
|
|
5ef7a3e76d | ||
|
|
041ce2504c | ||
|
|
8db889dbf5 | ||
|
|
a869fc219d | ||
|
|
002935af60 | ||
|
|
f3db7722b1 | ||
|
|
169014ef62 | ||
|
|
32d9e37708 | ||
|
|
57ee96a739 | ||
|
|
72f2bf4c42 | ||
|
|
b8b2265f9d | ||
|
|
56db682571 | ||
|
|
3b690dfc6e | ||
|
|
243c74b0cb | ||
|
|
a9a983d7ed | ||
|
|
880260d467 | ||
|
|
07e5e18d4d | ||
|
|
9e243daffa | ||
|
|
3ec2d4b701 | ||
|
|
3eee522897 | ||
|
|
408d16fabf | ||
|
|
830941755b | ||
|
|
84cc81ec40 | ||
|
|
a8b76a6914 | ||
|
|
c9f560eb04 | ||
|
|
8993b3584b | ||
|
|
93048647f5 | ||
|
|
75e35ca0e1 | ||
|
|
9205bafc56 | ||
|
|
4a9dd7f175 | ||
|
|
8d0c370f5a | ||
|
|
200e8f7b79 | ||
|
|
467e87813d | ||
|
|
5b2af73c50 | ||
|
|
d107917065 | ||
|
|
d111a34736 | ||
|
|
a40847a280 | ||
|
|
6fc010e638 | ||
|
|
d42ff80b25 | ||
|
|
1e76a5e92c | ||
|
|
05d21f6784 | ||
|
|
c1bd777c17 | ||
|
|
128176db1f | ||
|
|
005c4787a2 | ||
|
|
d1b364fd1b | ||
|
|
006d201400 | ||
|
|
2a964ae501 | ||
|
|
09b6b0da28 | ||
|
|
cceea1c7c1 | ||
|
|
8c339d4f3c | ||
|
|
85eb459182 | ||
|
|
fc818b37fd | ||
|
|
cb9a33eeb3 | ||
|
|
10d39f6192 | ||
|
|
2e6246528e | ||
|
|
0732a55d0e | ||
|
|
c7597fbcc2 | ||
|
|
84bf7f0332 | ||
|
|
abc518f907 | ||
|
|
25fd0de5d4 | ||
|
|
70fcaff7f6 | ||
|
|
cf668e820b | ||
|
|
f688db310f | ||
|
|
45c7bc12bf | ||
|
|
3d0d2903f7 | ||
|
|
99ca7168e3 | ||
|
|
c8e33acaf7 | ||
|
|
ce90b2b286 | ||
|
|
168245b31f | ||
|
|
cc86b11db9 | ||
|
|
bca8121732 | ||
|
|
a0807c2e0c | ||
|
|
96a3c025ab | ||
|
|
63a51ecee7 | ||
|
|
4976df4139 | ||
|
|
7fc5b1e55f | ||
|
|
cdb0fb37a9 | ||
|
|
6dbe136f39 | ||
|
|
73266d53d1 | ||
|
|
76ddca0b47 | ||
|
|
3f2f368dca | ||
|
|
54c0df51d5 | ||
|
|
7f8659afed | ||
|
|
4881565744 | ||
|
|
f2f14063a4 | ||
|
|
bf27ec2968 | ||
|
|
1c30b20969 | ||
|
|
f4a2d960cf | ||
|
|
70b70c55f3 | ||
|
|
99ed87de17 | ||
|
|
8033c85727 | ||
|
|
0eec66a9e0 | ||
|
|
fca793e403 | ||
|
|
b5a8442ed2 | ||
|
|
99315f8038 | ||
|
|
2c422d5e78 | ||
|
|
37d84d3f7f | ||
|
|
de1bed6742 | ||
|
|
c3db5438da | ||
|
|
c6d88f1dc4 | ||
|
|
4a2d71405d | ||
|
|
f98c3aa5ca | ||
|
|
7d389b561d | ||
|
|
7056d35254 | ||
|
|
6a8234ddd9 | ||
|
|
3d3eed0ed2 | ||
|
|
e479b35846 | ||
|
|
83c7421a96 | ||
|
|
116091f2e0 | ||
|
|
ce11017609 | ||
|
|
caa8c84d8a | ||
|
|
e14fc56b37 | ||
|
|
0d29e56948 | ||
|
|
d2a27c782d | ||
|
|
864e92ea59 | ||
|
|
97dfe9a1d4 | ||
|
|
d7edfd13a7 | ||
|
|
35207de7cc | ||
|
|
adf0e3720d | ||
|
|
f73c1889b7 | ||
|
|
18ff1d2898 | ||
|
|
c6e2fa11a4 | ||
|
|
3fac8b7cd6 | ||
|
|
f62faa72ce | ||
|
|
57256d0733 | ||
|
|
e1f44fb48a | ||
|
|
62676d5a83 | ||
|
|
7755256956 | ||
|
|
e1cf8546b7 | ||
|
|
e40837da2c | ||
|
|
e7c3c0ab53 | ||
|
|
2336b9c622 | ||
|
|
4e2122a64f | ||
|
|
833bb085e6 | ||
|
|
1ec9b0507d | ||
|
|
b6a4f70ec9 | ||
|
|
eb047e29a0 | ||
|
|
5dc1d9b2e0 | ||
|
|
362f3eac63 | ||
|
|
f0b26c60c4 | ||
|
|
e827d86967 | ||
|
|
04533c0338 | ||
|
|
e148e39fef | ||
|
|
cb97905c98 | ||
|
|
7ec7cecbfd | ||
|
|
3369401f1c | ||
|
|
09b9cfe027 | ||
|
|
e94ac3720b | ||
|
|
4414d60026 | ||
|
|
a6e680e32d | ||
|
|
d60a024c63 | ||
|
|
cc1b536656 | ||
|
|
85e773abef | ||
|
|
4690bc56aa | ||
|
|
292c2676fd | ||
|
|
f7c6604c7c | ||
|
|
22310eb957 | ||
|
|
6d17d1298b | ||
|
|
e90a680620 | ||
|
|
35d0ccfe89 | ||
|
|
62cc454367 | ||
|
|
8bb277fe56 | ||
|
|
67485b76af | ||
|
|
270e60d7cf | ||
|
|
11ffec862e | ||
|
|
d37e0dd5e1 | ||
|
|
960c3665e0 | ||
|
|
3396c79c38 | ||
|
|
cc1517f1b8 | ||
|
|
4f3ff55ae7 | ||
|
|
f51362e95e | ||
|
|
415dfece58 | ||
|
|
3bdb5353da | ||
|
|
6ddf1b59ef | ||
|
|
ef88f11c4f | ||
|
|
ec800c5439 | ||
|
|
a2f3f5d254 | ||
|
|
f475f70adf | ||
|
|
93c3da66da | ||
|
|
9b6145ce3f | ||
|
|
148eed172a | ||
|
|
c97f146964 | ||
|
|
a1034f5663 | ||
|
|
938b351f33 | ||
|
|
d870ecbcdb | ||
|
|
5dff2126c4 | ||
|
|
41a6cc15e8 | ||
|
|
f21b30c009 | ||
|
|
d69b63acc3 | ||
|
|
f95b2baad5 | ||
|
|
1a4d1fffb3 | ||
|
|
ac536ba125 | ||
|
|
362b225d66 | ||
|
|
7d0bdc1a63 | ||
|
|
94e3b21e44 | ||
|
|
23f89c1dc9 | ||
|
|
ce9c2f2c7a | ||
|
|
44e97ab046 | ||
|
|
5d0a1fd49f | ||
|
|
ffc1d8dc1e | ||
|
|
54fca93bba | ||
|
|
c281687910 | ||
|
|
6229d7abbe | ||
|
|
01644eddb6 | ||
|
|
6e30df3796 | ||
|
|
f87fa319ff | ||
|
|
9b019726bb | ||
|
|
cceafb76ed | ||
|
|
39e2994c69 | ||
|
|
1ddfcc3219 | ||
|
|
687f11596e | ||
|
|
b445f614fb | ||
|
|
c6fc1d93a0 | ||
|
|
28d58922e3 | ||
|
|
b884ea7ddc | ||
|
|
1717e20b61 | ||
|
|
6222f96b01 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [overheadhunter, tobihagemann] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: [cryptomator] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ labels: type:bug
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
⚠️⚠️⚠️ READ CAREFULLY ⚠️⚠️⚠️
|
||||||
|
|
||||||
Do you want to ask a QUESTION? Are you looking for SUPPORT?
|
Do you want to ask a QUESTION? Are you looking for SUPPORT?
|
||||||
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
|
We're happy to help you via our support channels! Please read: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
|
||||||
@@ -13,7 +14,9 @@ By filing an issue, you are expected to comply with our code of conduct: https:/
|
|||||||
|
|
||||||
Of course, we also expect you to search for existing similar issues first! ;) https://github.com/cryptomator/cryptomator/issues?q=
|
Of course, we also expect you to search for existing similar issues first! ;) https://github.com/cryptomator/cryptomator/issues?q=
|
||||||
|
|
||||||
|
⚠️ IMPORTANT: If you don't stick to this template, the issue will get closed. To proof that you read this, please remove the X from the following line:
|
||||||
-->
|
-->
|
||||||
|
<!-- oooXooo -->
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
@@ -23,7 +26,7 @@ Of course, we also expect you to search for existing similar issues first! ;) ht
|
|||||||
|
|
||||||
* Operating system and version: [Windows/macOS/Linux + Version]
|
* Operating system and version: [Windows/macOS/Linux + Version]
|
||||||
* Cryptomator version: [Shown in the settings]
|
* Cryptomator version: [Shown in the settings]
|
||||||
* Drive: [Dokany/FUSE/WebDAV]
|
* Volume type: [Dokany/FUSE/WebDAV, shown in the settings]
|
||||||
|
|
||||||
### Steps to Reproduce
|
### Steps to Reproduce
|
||||||
|
|
||||||
@@ -58,4 +61,4 @@ Log file location:
|
|||||||
- macOS: ~/Library/Logs/Cryptomator
|
- macOS: ~/Library/Logs/Cryptomator
|
||||||
- Linux: ~/.local/share/Cryptomator/logs
|
- Linux: ~/.local/share/Cryptomator/logs
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|||||||
+3
-1
@@ -1,12 +1,14 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 90
|
daysUntilStale: 180
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 30
|
daysUntilClose: 30
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- type:security-issue # never close automatically
|
- type:security-issue # never close automatically
|
||||||
|
- type:feature-request # never close automatically
|
||||||
- state:awaiting-response # handled by different bot
|
- state:awaiting-response # handled by different bot
|
||||||
- state:blocked
|
- state:blocked
|
||||||
|
- state:confirmed
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
# Set to true to ignore issues in a milestone (defaults to false)
|
||||||
exemptMilestones: true
|
exemptMilestones: true
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
[push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 14
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
- name: Ensure to use tagged version
|
||||||
|
run: mvn versions:set --file main/pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags'
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
- name: Build and Test
|
||||||
|
run: mvn -B install --file main/pom.xml -Pcoverage
|
||||||
|
- name: Run Codacy Coverage Reporter
|
||||||
|
run: |
|
||||||
|
curl -o ~/codacy-coverage-reporter.jar https://repo.maven.apache.org/maven2/com/codacy/codacy-coverage-reporter/7.1.0/codacy-coverage-reporter-7.1.0-assembly.jar
|
||||||
|
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial
|
||||||
|
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial
|
||||||
|
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial
|
||||||
|
$JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final
|
||||||
|
env:
|
||||||
|
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||||
|
- name: Assemble buildkit-linux.zip
|
||||||
|
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,linux
|
||||||
|
- name: Upload buildkit-linux.zip
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-linux.zip
|
||||||
|
path: main/buildkit/target/buildkit-linux.zip
|
||||||
|
- name: Assemble buildkit-mac.zip
|
||||||
|
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,mac
|
||||||
|
- name: Upload buildkit-mac.zip
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-mac.zip
|
||||||
|
path: main/buildkit/target/buildkit-mac.zip
|
||||||
|
- name: Assemble buildkit-win.zip
|
||||||
|
run: mvn -B clean package -DskipTests --file main/pom.xml --resume-from=buildkit -Prelease,windows
|
||||||
|
- name: Upload buildkit-win.zip
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-win.zip
|
||||||
|
path: main/buildkit/target/buildkit-win.zip
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Draft a Release on GitHub Releases
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
steps:
|
||||||
|
- name: Download buildkit-linux.zip
|
||||||
|
uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-linux.zip
|
||||||
|
path: .
|
||||||
|
- name: Download buildkit-mac.zip
|
||||||
|
uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-mac.zip
|
||||||
|
path: .
|
||||||
|
- name: Download buildkit-win.zip
|
||||||
|
uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: buildkit-win.zip
|
||||||
|
path: .
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ github.ref }}
|
||||||
|
body: |
|
||||||
|
:construction: Work in Progress
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
- name: Upload buildkit-linux.zip to GitHub Releases
|
||||||
|
uses: actions/upload-release-asset@v1.0.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: buildkit-linux.zip
|
||||||
|
asset_name: buildkit-linux.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- name: Upload buildkit-mac.zip to GitHub Releases
|
||||||
|
uses: actions/upload-release-asset@v1.0.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: buildkit-mac.zip
|
||||||
|
asset_name: buildkit-mac.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- name: Upload buildkit-win.zip to GitHub Releases
|
||||||
|
uses: actions/upload-release-asset@v1.0.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: buildkit-win.zip
|
||||||
|
asset_name: buildkit-win.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Bug Report Triage
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
closeTemplateViolation:
|
||||||
|
name: Close bug reports that violate the issue template
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- if: |
|
||||||
|
contains(github.event.issue.labels.*.name, 'type:bug')
|
||||||
|
&& (
|
||||||
|
!contains(github.event.issue.body, '<!-- oooooo -->')
|
||||||
|
|| !contains(github.event.issue.body, '### Description')
|
||||||
|
)
|
||||||
|
name: Close Issue
|
||||||
|
uses: peter-evans/close-issue@v1
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
This bug report did ignore our issue template. 😞
|
||||||
|
Auto-closing this issue, since it is most likely not useful.
|
||||||
|
|
||||||
|
_This decision was made by a bot. If you think the bot is wrong, let us know and we'll reopen this issue._
|
||||||
@@ -18,5 +18,8 @@ pom.xml.versionsBackup
|
|||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
.idea/dictionaries
|
.idea/dictionaries
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/encodings.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
.idea/**/libraries/
|
.idea/**/libraries/
|
||||||
*.iml
|
*.iml
|
||||||
Generated
+28
-1
@@ -9,14 +9,36 @@
|
|||||||
<option name="RIGHT_MARGIN" value="220" />
|
<option name="RIGHT_MARGIN" value="220" />
|
||||||
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
||||||
<JavaCodeStyleSettings>
|
<JavaCodeStyleSettings>
|
||||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="30" />
|
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
|
||||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="10" />
|
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="10" />
|
||||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||||
<value />
|
<value />
|
||||||
</option>
|
</option>
|
||||||
|
<option name="IMPORT_LAYOUT_TABLE">
|
||||||
|
<value>
|
||||||
|
<package name="" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="javax" withSubpackages="true" static="false" />
|
||||||
|
<package name="javafx" withSubpackages="true" static="false" />
|
||||||
|
<package name="java" withSubpackages="true" static="false" />
|
||||||
|
<emptyLine />
|
||||||
|
<package name="" withSubpackages="true" static="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
|
<Properties>
|
||||||
|
<option name="KEEP_BLANK_LINES" value="true" />
|
||||||
|
</Properties>
|
||||||
|
<XML>
|
||||||
|
<option name="XML_ATTRIBUTE_WRAP" value="0" />
|
||||||
|
</XML>
|
||||||
|
<codeStyleSettings language="CSS">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="Groovy">
|
<codeStyleSettings language="Groovy">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="USE_TAB_CHARACTER" value="true" />
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
@@ -30,12 +52,17 @@
|
|||||||
<codeStyleSettings language="JAVA">
|
<codeStyleSettings language="JAVA">
|
||||||
<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="KEEP_SIMPLE_BLOCKS_IN_ONE_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="ENUM_CONSTANTS_WRAP" value="2" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="USE_TAB_CHARACTER" value="true" />
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules />
|
||||||
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="JSON">
|
<codeStyleSettings language="JSON">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
|
|||||||
Generated
-41
@@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<annotationProcessing>
|
|
||||||
<profile name="Annotation profile for Cryptomator" enabled="true">
|
|
||||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
|
||||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
|
||||||
<outputRelativeToContentRoot value="true" />
|
|
||||||
<processorPath useClasspath="false">
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.25.2/dagger-compiler-2.25.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.25.2/dagger-2.25.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.25.2/dagger-producers-2.25.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/27.1-jre/guava-27.1-jre.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/2.5.2/checker-qual-2.5.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/codehaus/mojo/animal-sniffer-annotations/1.17/animal-sniffer-annotations-1.17.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.25.2/dagger-spi-2.25.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.11.1/javapoet-1.11.1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/annotation/jsr250-api/1.0/jsr250-api-1.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.3.50/kotlin-stdlib-1.3.50.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.50/kotlin-stdlib-common-1.3.50.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.1.0/kotlinx-metadata-jvm-0.1.0.jar" />
|
|
||||||
</processorPath>
|
|
||||||
<module name="commons" />
|
|
||||||
<module name="keychain" />
|
|
||||||
<module name="ui" />
|
|
||||||
<module name="launcher" />
|
|
||||||
</profile>
|
|
||||||
</annotationProcessing>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-11
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" addBOMForNewFiles="with NO BOM">
|
|
||||||
<file url="file://$PROJECT_DIR$/main" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/main/buildkit" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/main/commons" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/main/keychain" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/main/launcher" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/main/ui" charset="UTF-8" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_14" default="false" project-jdk-name="14" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
|
<module name="launcher" />
|
||||||
|
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath="~/.config/Cryptomator-Dev/settings.json" -Dcryptomator.ipcPortPath="~/.config/Cryptomator-Dev/ipcPort.bin" -Dcryptomator.logDir="~/.local/share/Cryptomator-Dev/logs" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator-Dev/mnt" -Dfuse.experimental="true" -Xss20m -Xmx512m" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
+1
-1
@@ -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="launcher" />
|
<module name="launcher" />
|
||||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" -Xss2m -Xmx512m" />
|
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator" -Xss2m -Xmx512m" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
|
<module name="launcher" />
|
||||||
|
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator-Dev/ipcPort.bin" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator-Dev" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator-Dev" -Dfuse.experimental="true" -Xss2m -Xmx512m" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
+3
@@ -1,5 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Cryptomator macOS" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator macOS" type="Application" factoryName="Application">
|
||||||
|
<envs>
|
||||||
|
<env name="LD_LIBRARY_PATH" value="/usr/local/lib" />
|
||||||
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="launcher" />
|
<module name="launcher" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator macOS Dev" type="Application" factoryName="Application">
|
||||||
|
<envs>
|
||||||
|
<env name="LD_LIBRARY_PATH" value="/usr/local/lib" />
|
||||||
|
</envs>
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
|
<module name="launcher" />
|
||||||
|
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator-Dev/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator-Dev" -Dcryptomator.mountPointsDir="/Volumes/" -Dfuse.experimental="true" -Xss2m -Xmx512m -ea" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"package": {
|
|
||||||
"name": "buildkit",
|
|
||||||
"repo": "cryptomator",
|
|
||||||
"subject": "cryptomator"
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "$TRAVIS_TAG",
|
|
||||||
"desc": "Cryptomator version $TRAVIS_TAG",
|
|
||||||
"released": "$TODAY",
|
|
||||||
"vcs_tag": "$TRAVIS_TAG",
|
|
||||||
"gpgSign": true
|
|
||||||
},
|
|
||||||
"files":
|
|
||||||
[
|
|
||||||
{"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/$TRAVIS_TAG/$1"}
|
|
||||||
],
|
|
||||||
"publish": true
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"package": {
|
|
||||||
"name": "buildkit",
|
|
||||||
"repo": "cryptomator",
|
|
||||||
"subject": "cryptomator"
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"name": "snapshot"
|
|
||||||
},
|
|
||||||
"files":
|
|
||||||
[
|
|
||||||
{"includePattern": "main/buildkit/target/(buildkit-[a-z]+\\.zip)", "uploadPattern": "/snapshot/$1", "matrixParams": {"override": 1}}
|
|
||||||
],
|
|
||||||
"publish": true
|
|
||||||
}
|
|
||||||
-65
@@ -1,65 +0,0 @@
|
|||||||
dist: bionic
|
|
||||||
language: java
|
|
||||||
sudo: false
|
|
||||||
jdk:
|
|
||||||
- openjdk11
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.m2
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: "HftEaabMmWn5GwKFKksUkOcelc3Mn7xazwAEy+4d4gL1+F8VhID/6DCK7nas+afUymWnxTano8Rv4Ci5MWryNkNkTH+FUPWmF3xWezc3hajSyS7RB92IZ8VPetl4Fo8UI1WwM5apDEaugalPxkIf8a7N+lpG5X/Gpumwzo3Be3w=" # BINTRAY_API_KEY
|
|
||||||
- secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN
|
|
||||||
- secure: "zJxgytA2Ks5Xzv+7kUaUq+EBFNQw9Qec63lcMJVuXVWczjL16nKW1EzzV515ag+OWL46z3lEPForDhufw0VtFnNmaX68jkO0mp01eLrHApc1llN2Y/U8GBXfNNazN4+Kom4H+z/AO+wJr8EsKMMUczCdQ3APgd9uVI0hzXw/Z3M=" # GITHUB_API_KEY
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- haveged
|
|
||||||
install:
|
|
||||||
- curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/e60ee486e34ee0c79f89f947abe2c83b4290c6bb/settings.xml
|
|
||||||
- mvn -fmain/pom.xml clean install -DskipTests -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN org.codehaus.mojo:versions-maven-plugin:help dependency:go-offline -Pcoverage,release # "clean install" needed until we can exclude artifacts currently in the reactor, see https://maven.apache.org/plugins/maven-dependency-plugin/go-offline-mojo.html#excludeReactor and https://issues.apache.org/jira/browse/MDEP-568
|
|
||||||
before_script:
|
|
||||||
- |
|
|
||||||
if [[ -n "$TRAVIS_TAG" ]]; then
|
|
||||||
mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=$TRAVIS_TAG
|
|
||||||
else
|
|
||||||
mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7)
|
|
||||||
fi
|
|
||||||
script:
|
|
||||||
- mvn --update-snapshots -fmain/pom.xml clean test verify -Pcoverage
|
|
||||||
after_success:
|
|
||||||
- curl -o ~/codacy-coverage-reporter.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/6.0.7/codacy-coverage-reporter-6.0.7-assembly.jar
|
|
||||||
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial
|
|
||||||
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/keychain/target/site/jacoco/jacoco.xml --partial
|
|
||||||
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial
|
|
||||||
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial
|
|
||||||
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar final
|
|
||||||
before_deploy:
|
|
||||||
- mvn -fmain/pom.xml package -Prelease -DskipTests
|
|
||||||
- export TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json
|
|
||||||
deploy:
|
|
||||||
- provider: bintray # SNAPSHOTS
|
|
||||||
file: .travis-deploy-snapshot.json
|
|
||||||
user: cryptobot
|
|
||||||
key: $BINTRAY_API_KEY
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
repo: cryptomator/cryptomator
|
|
||||||
branch: develop
|
|
||||||
- provider: bintray # RELEASES
|
|
||||||
file: .travis-deploy-release.json
|
|
||||||
user: cryptobot
|
|
||||||
key: $BINTRAY_API_KEY
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
repo: cryptomator/cryptomator
|
|
||||||
tags: true
|
|
||||||
- provider: releases
|
|
||||||
api_key: $GITHUB_API_KEY
|
|
||||||
file_glob: true
|
|
||||||
file:
|
|
||||||
- "main/buildkit/target/buildkit-*.zip"
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
repo: cryptomator/cryptomator
|
|
||||||
tags: true
|
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 19 November 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The GNU Affero General Public License is a free, copyleft license for
|
|
||||||
software and other kinds of works, specifically designed to ensure
|
|
||||||
cooperation with the community in the case of network server software.
|
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
|
||||||
to take away your freedom to share and change the works. By contrast,
|
|
||||||
our General Public Licenses are intended to guarantee your freedom to
|
|
||||||
share and change all versions of a program--to make sure it remains free
|
|
||||||
software for all its users.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
them if you wish), that you receive source code or can get it if you
|
|
||||||
want it, that you can change the software or use pieces of it in new
|
|
||||||
free programs, and that you know you can do these things.
|
|
||||||
|
|
||||||
Developers that use our General Public Licenses protect your rights
|
|
||||||
with two steps: (1) assert copyright on the software, and (2) offer
|
|
||||||
you this License which gives you legal permission to copy, distribute
|
|
||||||
and/or modify the software.
|
|
||||||
|
|
||||||
A secondary benefit of defending all users' freedom is that
|
|
||||||
improvements made in alternate versions of the program, if they
|
|
||||||
receive widespread use, become available for other developers to
|
|
||||||
incorporate. Many developers of free software are heartened and
|
|
||||||
encouraged by the resulting cooperation. However, in the case of
|
|
||||||
software used on network servers, this result may fail to come about.
|
|
||||||
The GNU General Public License permits making a modified version and
|
|
||||||
letting the public access it on a server without ever releasing its
|
|
||||||
source code to the public.
|
|
||||||
|
|
||||||
The GNU Affero General Public License is designed specifically to
|
|
||||||
ensure that, in such cases, the modified source code becomes available
|
|
||||||
to the community. It requires the operator of a network server to
|
|
||||||
provide the source code of the modified version running there to the
|
|
||||||
users of that server. Therefore, public use of a modified version, on
|
|
||||||
a publicly accessible server, gives the public access to the source
|
|
||||||
code of the modified version.
|
|
||||||
|
|
||||||
An older license, called the Affero General Public License and
|
|
||||||
published by Affero, was designed to accomplish similar goals. This is
|
|
||||||
a different license, not a version of the Affero GPL, but Affero has
|
|
||||||
released a new version of the Affero GPL which permits relicensing under
|
|
||||||
this license.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
0. Definitions.
|
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
||||||
works, such as semiconductor masks.
|
|
||||||
|
|
||||||
"The Program" refers to any copyrightable work licensed under this
|
|
||||||
License. Each licensee is addressed as "you". "Licensees" and
|
|
||||||
"recipients" may be individuals or organizations.
|
|
||||||
|
|
||||||
To "modify" a work means to copy from or adapt all or part of the work
|
|
||||||
in a fashion requiring copyright permission, other than the making of an
|
|
||||||
exact copy. The resulting work is called a "modified version" of the
|
|
||||||
earlier work or a work "based on" the earlier work.
|
|
||||||
|
|
||||||
A "covered work" means either the unmodified Program or a work based
|
|
||||||
on the Program.
|
|
||||||
|
|
||||||
To "propagate" a work means to do anything with it that, without
|
|
||||||
permission, would make you directly or secondarily liable for
|
|
||||||
infringement under applicable copyright law, except executing it on a
|
|
||||||
computer or modifying a private copy. Propagation includes copying,
|
|
||||||
distribution (with or without modification), making available to the
|
|
||||||
public, and in some countries other activities as well.
|
|
||||||
|
|
||||||
To "convey" a work means any kind of propagation that enables other
|
|
||||||
parties to make or receive copies. Mere interaction with a user through
|
|
||||||
a computer network, with no transfer of a copy, is not conveying.
|
|
||||||
|
|
||||||
An interactive user interface displays "Appropriate Legal Notices"
|
|
||||||
to the extent that it includes a convenient and prominently visible
|
|
||||||
feature that (1) displays an appropriate copyright notice, and (2)
|
|
||||||
tells the user that there is no warranty for the work (except to the
|
|
||||||
extent that warranties are provided), that licensees may convey the
|
|
||||||
work under this License, and how to view a copy of this License. If
|
|
||||||
the interface presents a list of user commands or options, such as a
|
|
||||||
menu, a prominent item in the list meets this criterion.
|
|
||||||
|
|
||||||
1. Source Code.
|
|
||||||
|
|
||||||
The "source code" for a work means the preferred form of the work
|
|
||||||
for making modifications to it. "Object code" means any non-source
|
|
||||||
form of a work.
|
|
||||||
|
|
||||||
A "Standard Interface" means an interface that either is an official
|
|
||||||
standard defined by a recognized standards body, or, in the case of
|
|
||||||
interfaces specified for a particular programming language, one that
|
|
||||||
is widely used among developers working in that language.
|
|
||||||
|
|
||||||
The "System Libraries" of an executable work include anything, other
|
|
||||||
than the work as a whole, that (a) is included in the normal form of
|
|
||||||
packaging a Major Component, but which is not part of that Major
|
|
||||||
Component, and (b) serves only to enable use of the work with that
|
|
||||||
Major Component, or to implement a Standard Interface for which an
|
|
||||||
implementation is available to the public in source code form. A
|
|
||||||
"Major Component", in this context, means a major essential component
|
|
||||||
(kernel, window system, and so on) of the specific operating system
|
|
||||||
(if any) on which the executable work runs, or a compiler used to
|
|
||||||
produce the work, or an object code interpreter used to run it.
|
|
||||||
|
|
||||||
The "Corresponding Source" for a work in object code form means all
|
|
||||||
the source code needed to generate, install, and (for an executable
|
|
||||||
work) run the object code and to modify the work, including scripts to
|
|
||||||
control those activities. However, it does not include the work's
|
|
||||||
System Libraries, or general-purpose tools or generally available free
|
|
||||||
programs which are used unmodified in performing those activities but
|
|
||||||
which are not part of the work. For example, Corresponding Source
|
|
||||||
includes interface definition files associated with source files for
|
|
||||||
the work, and the source code for shared libraries and dynamically
|
|
||||||
linked subprograms that the work is specifically designed to require,
|
|
||||||
such as by intimate data communication or control flow between those
|
|
||||||
subprograms and other parts of the work.
|
|
||||||
|
|
||||||
The Corresponding Source need not include anything that users
|
|
||||||
can regenerate automatically from other parts of the Corresponding
|
|
||||||
Source.
|
|
||||||
|
|
||||||
The Corresponding Source for a work in source code form is that
|
|
||||||
same work.
|
|
||||||
|
|
||||||
2. Basic Permissions.
|
|
||||||
|
|
||||||
All rights granted under this License are granted for the term of
|
|
||||||
copyright on the Program, and are irrevocable provided the stated
|
|
||||||
conditions are met. This License explicitly affirms your unlimited
|
|
||||||
permission to run the unmodified Program. The output from running a
|
|
||||||
covered work is covered by this License only if the output, given its
|
|
||||||
content, constitutes a covered work. This License acknowledges your
|
|
||||||
rights of fair use or other equivalent, as provided by copyright law.
|
|
||||||
|
|
||||||
You may make, run and propagate covered works that you do not
|
|
||||||
convey, without conditions so long as your license otherwise remains
|
|
||||||
in force. You may convey covered works to others for the sole purpose
|
|
||||||
of having them make modifications exclusively for you, or provide you
|
|
||||||
with facilities for running those works, provided that you comply with
|
|
||||||
the terms of this License in conveying all material for which you do
|
|
||||||
not control copyright. Those thus making or running the covered works
|
|
||||||
for you must do so exclusively on your behalf, under your direction
|
|
||||||
and control, on terms that prohibit them from making any copies of
|
|
||||||
your copyrighted material outside their relationship with you.
|
|
||||||
|
|
||||||
Conveying under any other circumstances is permitted solely under
|
|
||||||
the conditions stated below. Sublicensing is not allowed; section 10
|
|
||||||
makes it unnecessary.
|
|
||||||
|
|
||||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
||||||
|
|
||||||
No covered work shall be deemed part of an effective technological
|
|
||||||
measure under any applicable law fulfilling obligations under article
|
|
||||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
||||||
similar laws prohibiting or restricting circumvention of such
|
|
||||||
measures.
|
|
||||||
|
|
||||||
When you convey a covered work, you waive any legal power to forbid
|
|
||||||
circumvention of technological measures to the extent such circumvention
|
|
||||||
is effected by exercising rights under this License with respect to
|
|
||||||
the covered work, and you disclaim any intention to limit operation or
|
|
||||||
modification of the work as a means of enforcing, against the work's
|
|
||||||
users, your or third parties' legal rights to forbid circumvention of
|
|
||||||
technological measures.
|
|
||||||
|
|
||||||
4. Conveying Verbatim Copies.
|
|
||||||
|
|
||||||
You may convey verbatim copies of the Program's source code as you
|
|
||||||
receive it, in any medium, provided that you conspicuously and
|
|
||||||
appropriately publish on each copy an appropriate copyright notice;
|
|
||||||
keep intact all notices stating that this License and any
|
|
||||||
non-permissive terms added in accord with section 7 apply to the code;
|
|
||||||
keep intact all notices of the absence of any warranty; and give all
|
|
||||||
recipients a copy of this License along with the Program.
|
|
||||||
|
|
||||||
You may charge any price or no price for each copy that you convey,
|
|
||||||
and you may offer support or warranty protection for a fee.
|
|
||||||
|
|
||||||
5. Conveying Modified Source Versions.
|
|
||||||
|
|
||||||
You may convey a work based on the Program, or the modifications to
|
|
||||||
produce it from the Program, in the form of source code under the
|
|
||||||
terms of section 4, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The work must carry prominent notices stating that you modified
|
|
||||||
it, and giving a relevant date.
|
|
||||||
|
|
||||||
b) The work must carry prominent notices stating that it is
|
|
||||||
released under this License and any conditions added under section
|
|
||||||
7. This requirement modifies the requirement in section 4 to
|
|
||||||
"keep intact all notices".
|
|
||||||
|
|
||||||
c) You must license the entire work, as a whole, under this
|
|
||||||
License to anyone who comes into possession of a copy. This
|
|
||||||
License will therefore apply, along with any applicable section 7
|
|
||||||
additional terms, to the whole of the work, and all its parts,
|
|
||||||
regardless of how they are packaged. This License gives no
|
|
||||||
permission to license the work in any other way, but it does not
|
|
||||||
invalidate such permission if you have separately received it.
|
|
||||||
|
|
||||||
d) If the work has interactive user interfaces, each must display
|
|
||||||
Appropriate Legal Notices; however, if the Program has interactive
|
|
||||||
interfaces that do not display Appropriate Legal Notices, your
|
|
||||||
work need not make them do so.
|
|
||||||
|
|
||||||
A compilation of a covered work with other separate and independent
|
|
||||||
works, which are not by their nature extensions of the covered work,
|
|
||||||
and which are not combined with it such as to form a larger program,
|
|
||||||
in or on a volume of a storage or distribution medium, is called an
|
|
||||||
"aggregate" if the compilation and its resulting copyright are not
|
|
||||||
used to limit the access or legal rights of the compilation's users
|
|
||||||
beyond what the individual works permit. Inclusion of a covered work
|
|
||||||
in an aggregate does not cause this License to apply to the other
|
|
||||||
parts of the aggregate.
|
|
||||||
|
|
||||||
6. Conveying Non-Source Forms.
|
|
||||||
|
|
||||||
You may convey a covered work in object code form under the terms
|
|
||||||
of sections 4 and 5, provided that you also convey the
|
|
||||||
machine-readable Corresponding Source under the terms of this License,
|
|
||||||
in one of these ways:
|
|
||||||
|
|
||||||
a) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by the
|
|
||||||
Corresponding Source fixed on a durable physical medium
|
|
||||||
customarily used for software interchange.
|
|
||||||
|
|
||||||
b) Convey the object code in, or embodied in, a physical product
|
|
||||||
(including a physical distribution medium), accompanied by a
|
|
||||||
written offer, valid for at least three years and valid for as
|
|
||||||
long as you offer spare parts or customer support for that product
|
|
||||||
model, to give anyone who possesses the object code either (1) a
|
|
||||||
copy of the Corresponding Source for all the software in the
|
|
||||||
product that is covered by this License, on a durable physical
|
|
||||||
medium customarily used for software interchange, for a price no
|
|
||||||
more than your reasonable cost of physically performing this
|
|
||||||
conveying of source, or (2) access to copy the
|
|
||||||
Corresponding Source from a network server at no charge.
|
|
||||||
|
|
||||||
c) Convey individual copies of the object code with a copy of the
|
|
||||||
written offer to provide the Corresponding Source. This
|
|
||||||
alternative is allowed only occasionally and noncommercially, and
|
|
||||||
only if you received the object code with such an offer, in accord
|
|
||||||
with subsection 6b.
|
|
||||||
|
|
||||||
d) Convey the object code by offering access from a designated
|
|
||||||
place (gratis or for a charge), and offer equivalent access to the
|
|
||||||
Corresponding Source in the same way through the same place at no
|
|
||||||
further charge. You need not require recipients to copy the
|
|
||||||
Corresponding Source along with the object code. If the place to
|
|
||||||
copy the object code is a network server, the Corresponding Source
|
|
||||||
may be on a different server (operated by you or a third party)
|
|
||||||
that supports equivalent copying facilities, provided you maintain
|
|
||||||
clear directions next to the object code saying where to find the
|
|
||||||
Corresponding Source. Regardless of what server hosts the
|
|
||||||
Corresponding Source, you remain obligated to ensure that it is
|
|
||||||
available for as long as needed to satisfy these requirements.
|
|
||||||
|
|
||||||
e) Convey the object code using peer-to-peer transmission, provided
|
|
||||||
you inform other peers where the object code and Corresponding
|
|
||||||
Source of the work are being offered to the general public at no
|
|
||||||
charge under subsection 6d.
|
|
||||||
|
|
||||||
A separable portion of the object code, whose source code is excluded
|
|
||||||
from the Corresponding Source as a System Library, need not be
|
|
||||||
included in conveying the object code work.
|
|
||||||
|
|
||||||
A "User Product" is either (1) a "consumer product", which means any
|
|
||||||
tangible personal property which is normally used for personal, family,
|
|
||||||
or household purposes, or (2) anything designed or sold for incorporation
|
|
||||||
into a dwelling. In determining whether a product is a consumer product,
|
|
||||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
||||||
product received by a particular user, "normally used" refers to a
|
|
||||||
typical or common use of that class of product, regardless of the status
|
|
||||||
of the particular user or of the way in which the particular user
|
|
||||||
actually uses, or expects or is expected to use, the product. A product
|
|
||||||
is a consumer product regardless of whether the product has substantial
|
|
||||||
commercial, industrial or non-consumer uses, unless such uses represent
|
|
||||||
the only significant mode of use of the product.
|
|
||||||
|
|
||||||
"Installation Information" for a User Product means any methods,
|
|
||||||
procedures, authorization keys, or other information required to install
|
|
||||||
and execute modified versions of a covered work in that User Product from
|
|
||||||
a modified version of its Corresponding Source. The information must
|
|
||||||
suffice to ensure that the continued functioning of the modified object
|
|
||||||
code is in no case prevented or interfered with solely because
|
|
||||||
modification has been made.
|
|
||||||
|
|
||||||
If you convey an object code work under this section in, or with, or
|
|
||||||
specifically for use in, a User Product, and the conveying occurs as
|
|
||||||
part of a transaction in which the right of possession and use of the
|
|
||||||
User Product is transferred to the recipient in perpetuity or for a
|
|
||||||
fixed term (regardless of how the transaction is characterized), the
|
|
||||||
Corresponding Source conveyed under this section must be accompanied
|
|
||||||
by the Installation Information. But this requirement does not apply
|
|
||||||
if neither you nor any third party retains the ability to install
|
|
||||||
modified object code on the User Product (for example, the work has
|
|
||||||
been installed in ROM).
|
|
||||||
|
|
||||||
The requirement to provide Installation Information does not include a
|
|
||||||
requirement to continue to provide support service, warranty, or updates
|
|
||||||
for a work that has been modified or installed by the recipient, or for
|
|
||||||
the User Product in which it has been modified or installed. Access to a
|
|
||||||
network may be denied when the modification itself materially and
|
|
||||||
adversely affects the operation of the network or violates the rules and
|
|
||||||
protocols for communication across the network.
|
|
||||||
|
|
||||||
Corresponding Source conveyed, and Installation Information provided,
|
|
||||||
in accord with this section must be in a format that is publicly
|
|
||||||
documented (and with an implementation available to the public in
|
|
||||||
source code form), and must require no special password or key for
|
|
||||||
unpacking, reading or copying.
|
|
||||||
|
|
||||||
7. Additional Terms.
|
|
||||||
|
|
||||||
"Additional permissions" are terms that supplement the terms of this
|
|
||||||
License by making exceptions from one or more of its conditions.
|
|
||||||
Additional permissions that are applicable to the entire Program shall
|
|
||||||
be treated as though they were included in this License, to the extent
|
|
||||||
that they are valid under applicable law. If additional permissions
|
|
||||||
apply only to part of the Program, that part may be used separately
|
|
||||||
under those permissions, but the entire Program remains governed by
|
|
||||||
this License without regard to the additional permissions.
|
|
||||||
|
|
||||||
When you convey a copy of a covered work, you may at your option
|
|
||||||
remove any additional permissions from that copy, or from any part of
|
|
||||||
it. (Additional permissions may be written to require their own
|
|
||||||
removal in certain cases when you modify the work.) You may place
|
|
||||||
additional permissions on material, added by you to a covered work,
|
|
||||||
for which you have or can give appropriate copyright permission.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, for material you
|
|
||||||
add to a covered work, you may (if authorized by the copyright holders of
|
|
||||||
that material) supplement the terms of this License with terms:
|
|
||||||
|
|
||||||
a) Disclaiming warranty or limiting liability differently from the
|
|
||||||
terms of sections 15 and 16 of this License; or
|
|
||||||
|
|
||||||
b) Requiring preservation of specified reasonable legal notices or
|
|
||||||
author attributions in that material or in the Appropriate Legal
|
|
||||||
Notices displayed by works containing it; or
|
|
||||||
|
|
||||||
c) Prohibiting misrepresentation of the origin of that material, or
|
|
||||||
requiring that modified versions of such material be marked in
|
|
||||||
reasonable ways as different from the original version; or
|
|
||||||
|
|
||||||
d) Limiting the use for publicity purposes of names of licensors or
|
|
||||||
authors of the material; or
|
|
||||||
|
|
||||||
e) Declining to grant rights under trademark law for use of some
|
|
||||||
trade names, trademarks, or service marks; or
|
|
||||||
|
|
||||||
f) Requiring indemnification of licensors and authors of that
|
|
||||||
material by anyone who conveys the material (or modified versions of
|
|
||||||
it) with contractual assumptions of liability to the recipient, for
|
|
||||||
any liability that these contractual assumptions directly impose on
|
|
||||||
those licensors and authors.
|
|
||||||
|
|
||||||
All other non-permissive additional terms are considered "further
|
|
||||||
restrictions" within the meaning of section 10. If the Program as you
|
|
||||||
received it, or any part of it, contains a notice stating that it is
|
|
||||||
governed by this License along with a term that is a further
|
|
||||||
restriction, you may remove that term. If a license document contains
|
|
||||||
a further restriction but permits relicensing or conveying under this
|
|
||||||
License, you may add to a covered work material governed by the terms
|
|
||||||
of that license document, provided that the further restriction does
|
|
||||||
not survive such relicensing or conveying.
|
|
||||||
|
|
||||||
If you add terms to a covered work in accord with this section, you
|
|
||||||
must place, in the relevant source files, a statement of the
|
|
||||||
additional terms that apply to those files, or a notice indicating
|
|
||||||
where to find the applicable terms.
|
|
||||||
|
|
||||||
Additional terms, permissive or non-permissive, may be stated in the
|
|
||||||
form of a separately written license, or stated as exceptions;
|
|
||||||
the above requirements apply either way.
|
|
||||||
|
|
||||||
8. Termination.
|
|
||||||
|
|
||||||
You may not propagate or modify a covered work except as expressly
|
|
||||||
provided under this License. Any attempt otherwise to propagate or
|
|
||||||
modify it is void, and will automatically terminate your rights under
|
|
||||||
this License (including any patent licenses granted under the third
|
|
||||||
paragraph of section 11).
|
|
||||||
|
|
||||||
However, if you cease all violation of this License, then your
|
|
||||||
license from a particular copyright holder is reinstated (a)
|
|
||||||
provisionally, unless and until the copyright holder explicitly and
|
|
||||||
finally terminates your license, and (b) permanently, if the copyright
|
|
||||||
holder fails to notify you of the violation by some reasonable means
|
|
||||||
prior to 60 days after the cessation.
|
|
||||||
|
|
||||||
Moreover, your license from a particular copyright holder is
|
|
||||||
reinstated permanently if the copyright holder notifies you of the
|
|
||||||
violation by some reasonable means, this is the first time you have
|
|
||||||
received notice of violation of this License (for any work) from that
|
|
||||||
copyright holder, and you cure the violation prior to 30 days after
|
|
||||||
your receipt of the notice.
|
|
||||||
|
|
||||||
Termination of your rights under this section does not terminate the
|
|
||||||
licenses of parties who have received copies or rights from you under
|
|
||||||
this License. If your rights have been terminated and not permanently
|
|
||||||
reinstated, you do not qualify to receive new licenses for the same
|
|
||||||
material under section 10.
|
|
||||||
|
|
||||||
9. Acceptance Not Required for Having Copies.
|
|
||||||
|
|
||||||
You are not required to accept this License in order to receive or
|
|
||||||
run a copy of the Program. Ancillary propagation of a covered work
|
|
||||||
occurring solely as a consequence of using peer-to-peer transmission
|
|
||||||
to receive a copy likewise does not require acceptance. However,
|
|
||||||
nothing other than this License grants you permission to propagate or
|
|
||||||
modify any covered work. These actions infringe copyright if you do
|
|
||||||
not accept this License. Therefore, by modifying or propagating a
|
|
||||||
covered work, you indicate your acceptance of this License to do so.
|
|
||||||
|
|
||||||
10. Automatic Licensing of Downstream Recipients.
|
|
||||||
|
|
||||||
Each time you convey a covered work, the recipient automatically
|
|
||||||
receives a license from the original licensors, to run, modify and
|
|
||||||
propagate that work, subject to this License. You are not responsible
|
|
||||||
for enforcing compliance by third parties with this License.
|
|
||||||
|
|
||||||
An "entity transaction" is a transaction transferring control of an
|
|
||||||
organization, or substantially all assets of one, or subdividing an
|
|
||||||
organization, or merging organizations. If propagation of a covered
|
|
||||||
work results from an entity transaction, each party to that
|
|
||||||
transaction who receives a copy of the work also receives whatever
|
|
||||||
licenses to the work the party's predecessor in interest had or could
|
|
||||||
give under the previous paragraph, plus a right to possession of the
|
|
||||||
Corresponding Source of the work from the predecessor in interest, if
|
|
||||||
the predecessor has it or can get it with reasonable efforts.
|
|
||||||
|
|
||||||
You may not impose any further restrictions on the exercise of the
|
|
||||||
rights granted or affirmed under this License. For example, you may
|
|
||||||
not impose a license fee, royalty, or other charge for exercise of
|
|
||||||
rights granted under this License, and you may not initiate litigation
|
|
||||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
|
||||||
any patent claim is infringed by making, using, selling, offering for
|
|
||||||
sale, or importing the Program or any portion of it.
|
|
||||||
|
|
||||||
11. Patents.
|
|
||||||
|
|
||||||
A "contributor" is a copyright holder who authorizes use under this
|
|
||||||
License of the Program or a work on which the Program is based. The
|
|
||||||
work thus licensed is called the contributor's "contributor version".
|
|
||||||
|
|
||||||
A contributor's "essential patent claims" are all patent claims
|
|
||||||
owned or controlled by the contributor, whether already acquired or
|
|
||||||
hereafter acquired, that would be infringed by some manner, permitted
|
|
||||||
by this License, of making, using, or selling its contributor version,
|
|
||||||
but do not include claims that would be infringed only as a
|
|
||||||
consequence of further modification of the contributor version. For
|
|
||||||
purposes of this definition, "control" includes the right to grant
|
|
||||||
patent sublicenses in a manner consistent with the requirements of
|
|
||||||
this License.
|
|
||||||
|
|
||||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
|
||||||
patent license under the contributor's essential patent claims, to
|
|
||||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
|
||||||
propagate the contents of its contributor version.
|
|
||||||
|
|
||||||
In the following three paragraphs, a "patent license" is any express
|
|
||||||
agreement or commitment, however denominated, not to enforce a patent
|
|
||||||
(such as an express permission to practice a patent or covenant not to
|
|
||||||
sue for patent infringement). To "grant" such a patent license to a
|
|
||||||
party means to make such an agreement or commitment not to enforce a
|
|
||||||
patent against the party.
|
|
||||||
|
|
||||||
If you convey a covered work, knowingly relying on a patent license,
|
|
||||||
and the Corresponding Source of the work is not available for anyone
|
|
||||||
to copy, free of charge and under the terms of this License, through a
|
|
||||||
publicly available network server or other readily accessible means,
|
|
||||||
then you must either (1) cause the Corresponding Source to be so
|
|
||||||
available, or (2) arrange to deprive yourself of the benefit of the
|
|
||||||
patent license for this particular work, or (3) arrange, in a manner
|
|
||||||
consistent with the requirements of this License, to extend the patent
|
|
||||||
license to downstream recipients. "Knowingly relying" means you have
|
|
||||||
actual knowledge that, but for the patent license, your conveying the
|
|
||||||
covered work in a country, or your recipient's use of the covered work
|
|
||||||
in a country, would infringe one or more identifiable patents in that
|
|
||||||
country that you have reason to believe are valid.
|
|
||||||
|
|
||||||
If, pursuant to or in connection with a single transaction or
|
|
||||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
|
||||||
covered work, and grant a patent license to some of the parties
|
|
||||||
receiving the covered work authorizing them to use, propagate, modify
|
|
||||||
or convey a specific copy of the covered work, then the patent license
|
|
||||||
you grant is automatically extended to all recipients of the covered
|
|
||||||
work and works based on it.
|
|
||||||
|
|
||||||
A patent license is "discriminatory" if it does not include within
|
|
||||||
the scope of its coverage, prohibits the exercise of, or is
|
|
||||||
conditioned on the non-exercise of one or more of the rights that are
|
|
||||||
specifically granted under this License. You may not convey a covered
|
|
||||||
work if you are a party to an arrangement with a third party that is
|
|
||||||
in the business of distributing software, under which you make payment
|
|
||||||
to the third party based on the extent of your activity of conveying
|
|
||||||
the work, and under which the third party grants, to any of the
|
|
||||||
parties who would receive the covered work from you, a discriminatory
|
|
||||||
patent license (a) in connection with copies of the covered work
|
|
||||||
conveyed by you (or copies made from those copies), or (b) primarily
|
|
||||||
for and in connection with specific products or compilations that
|
|
||||||
contain the covered work, unless you entered into that arrangement,
|
|
||||||
or that patent license was granted, prior to 28 March 2007.
|
|
||||||
|
|
||||||
Nothing in this License shall be construed as excluding or limiting
|
|
||||||
any implied license or other defenses to infringement that may
|
|
||||||
otherwise be available to you under applicable patent law.
|
|
||||||
|
|
||||||
12. No Surrender of Others' Freedom.
|
|
||||||
|
|
||||||
If conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot convey a
|
|
||||||
covered work so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you may
|
|
||||||
not convey it at all. For example, if you agree to terms that obligate you
|
|
||||||
to collect a royalty for further conveying from those to whom you convey
|
|
||||||
the Program, the only way you could satisfy both those terms and this
|
|
||||||
License would be to refrain entirely from conveying the Program.
|
|
||||||
|
|
||||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, if you modify the
|
|
||||||
Program, your modified version must prominently offer all users
|
|
||||||
interacting with it remotely through a computer network (if your version
|
|
||||||
supports such interaction) an opportunity to receive the Corresponding
|
|
||||||
Source of your version by providing access to the Corresponding Source
|
|
||||||
from a network server at no charge, through some standard or customary
|
|
||||||
means of facilitating copying of software. This Corresponding Source
|
|
||||||
shall include the Corresponding Source for any work covered by version 3
|
|
||||||
of the GNU General Public License that is incorporated pursuant to the
|
|
||||||
following paragraph.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
|
||||||
permission to link or combine any covered work with a work licensed
|
|
||||||
under version 3 of the GNU General Public License into a single
|
|
||||||
combined work, and to convey the resulting work. The terms of this
|
|
||||||
License will continue to apply to the part which is the covered work,
|
|
||||||
but the work with which it is combined will remain governed by version
|
|
||||||
3 of the GNU General Public License.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
|
||||||
the GNU Affero General Public License from time to time. Such new versions
|
|
||||||
will be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Program specifies that a certain numbered version of the GNU Affero General
|
|
||||||
Public License "or any later version" applies to it, you have the
|
|
||||||
option of following the terms and conditions either of that numbered
|
|
||||||
version or of any later version published by the Free Software
|
|
||||||
Foundation. If the Program does not specify a version number of the
|
|
||||||
GNU Affero General Public License, you may choose any version ever published
|
|
||||||
by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
|
||||||
versions of the GNU Affero General Public License can be used, that proxy's
|
|
||||||
public statement of acceptance of a version permanently authorizes you
|
|
||||||
to choose that version for the Program.
|
|
||||||
|
|
||||||
Later license versions may give you additional or different
|
|
||||||
permissions. However, no additional obligations are imposed on any
|
|
||||||
author or copyright holder as a result of your choosing to follow a
|
|
||||||
later version.
|
|
||||||
|
|
||||||
15. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
|
||||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
|
||||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
|
||||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
|
||||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
|
||||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. Limitation of Liability.
|
|
||||||
|
|
||||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
|
||||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
||||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
|
||||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
|
||||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
|
||||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
|
||||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGES.
|
|
||||||
|
|
||||||
17. Interpretation of Sections 15 and 16.
|
|
||||||
|
|
||||||
If the disclaimer of warranty and limitation of liability provided
|
|
||||||
above cannot be given local legal effect according to their terms,
|
|
||||||
reviewing courts shall apply local law that most closely approximates
|
|
||||||
an absolute waiver of all civil liability in connection with the
|
|
||||||
Program, unless a warranty or assumption of liability accompanies a
|
|
||||||
copy of the Program in return for a fee.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
state the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If your software can interact with users remotely through a computer
|
|
||||||
network, you should also make sure that it provides a way for users to
|
|
||||||
get its source. For example, if your program is a web application, its
|
|
||||||
interface could display a "Source" link that leads users to an archive
|
|
||||||
of the code. There are many ways you could offer source, and different
|
|
||||||
solutions will be better for different programs; see section 13 for the
|
|
||||||
specific requirements.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,502 +0,0 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
|
||||||
specially designated software packages--typically libraries--of the
|
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
|
||||||
can use it too, but we suggest you first think carefully about whether
|
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
|
||||||
not price. Our General Public Licenses are designed to make sure that
|
|
||||||
you have the freedom to distribute copies of free software (and charge
|
|
||||||
for this service if you wish); that you receive source code or can get
|
|
||||||
it if you want it; that you can change the software and use pieces of
|
|
||||||
it in new free programs; and that you are informed that you can do
|
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
|
||||||
rights. These restrictions translate to certain responsibilities for
|
|
||||||
you if you distribute copies of the library or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link other code with the library, you must provide
|
|
||||||
complete object files to the recipients, so that they can relink them
|
|
||||||
with the library after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
|
||||||
library, and (2) we offer you this license, which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
|
||||||
there is no warranty for the free library. Also, if the library is
|
|
||||||
modified by someone else and passed on, the recipients should know
|
|
||||||
that what they have is not the original version, so that the original
|
|
||||||
author's reputation will not be affected by problems that might be
|
|
||||||
introduced by others.
|
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
|
||||||
any free program. We wish to make sure that a company cannot
|
|
||||||
effectively restrict the users of a free program by obtaining a
|
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
|
||||||
any patent license obtained for a version of the library must be
|
|
||||||
consistent with the full freedom of use specified in this license.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
|
||||||
General Public License, applies to certain designated libraries, and
|
|
||||||
is quite different from the ordinary General Public License. We use
|
|
||||||
this license for certain libraries in order to permit linking those
|
|
||||||
libraries into non-free programs.
|
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
|
||||||
a shared library, the combination of the two is legally speaking a
|
|
||||||
combined work, a derivative of the original library. The ordinary
|
|
||||||
General Public License therefore permits such linking only if the
|
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
|
||||||
Public License permits more lax criteria for linking other code with
|
|
||||||
the library.
|
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
|
||||||
does Less to protect the user's freedom than the ordinary General
|
|
||||||
Public License. It also provides other free software developers Less
|
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
|
||||||
allowed to use the library. A more frequent case is that a free
|
|
||||||
library does the same job as widely used non-free libraries. In this
|
|
||||||
case, there is little to gain by limiting the free library to free
|
|
||||||
software only, so we use the Lesser General Public License.
|
|
||||||
|
|
||||||
In other cases, permission to use a particular library in non-free
|
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, whereas the latter must
|
|
||||||
be combined with the library in order to run.
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
|
||||||
program which contains a notice placed by the copyright holder or
|
|
||||||
other authorized party saying it may be distributed under the terms of
|
|
||||||
this Lesser General Public License (also called "this License").
|
|
||||||
Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the materials to be distributed need not include anything that is
|
|
||||||
normally distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties with
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Lesser General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
||||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
||||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
||||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
||||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
||||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
|
||||||
|
|
||||||
If you develop a new library, and you want it to be of the greatest
|
|
||||||
possible use to the public, we recommend making it free software that
|
|
||||||
everyone can redistribute and change. You can do so by permitting
|
|
||||||
redistribution under these terms (or, alternatively, under the terms of the
|
|
||||||
ordinary General Public License).
|
|
||||||
|
|
||||||
To apply these terms, attach the following notices to the library. It is
|
|
||||||
safest to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least the
|
|
||||||
"copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the library's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
|
||||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1990
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
That's all there is to it!
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) <year> <copyright holders>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) [year], [fullname]
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of [project] nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
Copyright (c) [year], [fullname]
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
# Third party softwares
|
|
||||||
Cryptomator uses third party libraries and fonts that may be licensed under different licenses.
|
|
||||||
|
|
||||||
## AquaFX
|
|
||||||
The ProgressIndicator in ui/src/main/resource/css/mac_theme.css contains code from the AquaFX project.
|
|
||||||
|
|
||||||
Copyright Claudine Zillmann (http://aquafx-project.com/)
|
|
||||||
|
|
||||||
Licensed under the accompanying Modified BSD license file.
|
|
||||||
|
|
||||||
## Apache Commons + Apache HttpComponents + Jackrabbit WebDAV Library
|
|
||||||
Copyright The Apache Software Foundation
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0
|
|
||||||
|
|
||||||
### Commons Codec
|
|
||||||
|
|
||||||
src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java contains test data
|
|
||||||
from http://aspell.net/test/orig/batch0.tab. Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
|
|
||||||
|
|
||||||
### Commons Lang
|
|
||||||
|
|
||||||
This product includes software from the Spring Framework,
|
|
||||||
under the Apache License 2.0 (see: StringUtils.containsWhitespace())
|
|
||||||
|
|
||||||
### Jackrabbit WebDAV Library
|
|
||||||
|
|
||||||
Based on source code originally developed by
|
|
||||||
Day Software (http://www.day.com/).
|
|
||||||
|
|
||||||
## CryptoLib + CryptoFS
|
|
||||||
Copyright 2016, 2017 Skymatic UG (haftungsbeschränkt)
|
|
||||||
|
|
||||||
Licensed under the GNU Affero General Public License, Version 3
|
|
||||||
|
|
||||||
## Dagger 2
|
|
||||||
Copyright 2014 Google, Inc.
|
|
||||||
Copyright 2012 Square, Inc.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0
|
|
||||||
|
|
||||||
## EasyBind
|
|
||||||
Copyright (c) 2014, TomasMikula
|
|
||||||
|
|
||||||
Licensed under the accompanying simplified BSD license.
|
|
||||||
|
|
||||||
|
|
||||||
## GSON + Guava
|
|
||||||
Copyright Google, Inc.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0
|
|
||||||
|
|
||||||
## Jetty
|
|
||||||
Copyright 1995-2017 Mort Bay Consulting Pty Ltd.
|
|
||||||
|
|
||||||
The UnixCrypt.java code implements the one way cryptography used by
|
|
||||||
Unix systems for simple password protection. Copyright 1996 Aki Yoshida,
|
|
||||||
modified April 2001 by Iris Van den Broeke, Daniel Deville.
|
|
||||||
Permission to use, copy, modify and distribute UnixCrypt
|
|
||||||
for non-commercial or commercial purposes and without fee is
|
|
||||||
granted provided that the copyright notice appears in all copies.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0
|
|
||||||
|
|
||||||
|
|
||||||
## Logback
|
|
||||||
Copyright (C) 1999-2017, QOS.ch. All rights reserved.
|
|
||||||
|
|
||||||
Licensed under the GNU Lesser General Public License, Version 2.1
|
|
||||||
|
|
||||||
## SIV-Mode
|
|
||||||
Copyright (c) Sebastian Stenzel
|
|
||||||
|
|
||||||
Licensed under the MIT / X11 License
|
|
||||||
|
|
||||||
## SLF4J
|
|
||||||
Copyright (c) 2004-2017 QOS.ch
|
|
||||||
|
|
||||||
Licensed under the MIT / X11 License
|
|
||||||
|
|
||||||
|
|
||||||
# Other third party assets
|
|
||||||
Non-software work included in Cryptomator
|
|
||||||
|
|
||||||
## Ionicons
|
|
||||||
Copyright (c) 2016 Drifty (http://drifty.com/)
|
|
||||||
|
|
||||||
ionicons.ttf Licensed under the accompanying MIT license
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[](https://cryptomator.org/)
|
[](https://cryptomator.org/)
|
||||||
|
|
||||||
[](https://travis-ci.org/cryptomator/cryptomator)
|
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
|
||||||
[](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
|
[](https://snyk.io/test/github/cryptomator/cryptomator?targetFile=main%2Fpom.xml)
|
||||||
[](https://www.codacy.com/app/cryptomator/cryptomator?utm_source=github.com&utm_medium=referral&utm_content=cryptomator/cryptomator&utm_campaign=Badge_Grade)
|
[](https://www.codacy.com/app/cryptomator/cryptomator?utm_source=github.com&utm_medium=referral&utm_content=cryptomator/cryptomator&utm_campaign=Badge_Grade)
|
||||||
[](http://twitter.com/Cryptomator)
|
[](http://twitter.com/Cryptomator)
|
||||||
@@ -15,6 +15,10 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
|||||||
- [One-time or recurring donation via Cryptomator's website.](https://cryptomator.org/#donate)
|
- [One-time or recurring donation via Cryptomator's website.](https://cryptomator.org/#donate)
|
||||||
- [Become a sponsor via Cryptomator's sponsors website.](https://cryptomator.org/sponsors/)
|
- [Become a sponsor via Cryptomator's sponsors website.](https://cryptomator.org/sponsors/)
|
||||||
|
|
||||||
|
### Gold Sponsors
|
||||||
|
|
||||||
|
[<img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="96">](https://www.gee-whiz.de/)
|
||||||
|
|
||||||
### Silver Sponsors
|
### Silver Sponsors
|
||||||
|
|
||||||
[](https://thebestvpn.com/)
|
[](https://thebestvpn.com/)
|
||||||
@@ -37,7 +41,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
|||||||
- File names get encrypted
|
- File names get encrypted
|
||||||
- Folder structure gets obfuscated
|
- Folder structure gets obfuscated
|
||||||
- Use as many vaults in your Dropbox as you want, each having individual passwords
|
- Use as many vaults in your Dropbox as you want, each having individual passwords
|
||||||
- One thousand commits for the security of your data!! :tada:
|
- Two thousand commits for the security of your data!! :tada:
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
|
|
||||||
@@ -55,13 +59,13 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
|||||||
|
|
||||||
### Security Architecture
|
### Security Architecture
|
||||||
|
|
||||||
For more information on the security details visit [cryptomator.org](https://cryptomator.org/architecture/).
|
For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/en/latest/security/architecture/).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 11 (we recommend to use the latest version)
|
* JDK 14 (e.g. adoptopenjdk)
|
||||||
* Maven 3
|
* Maven 3
|
||||||
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
|
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
|
||||||
|
|
||||||
@@ -69,7 +73,9 @@ For more information on the security details visit [cryptomator.org](https://cry
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd main
|
cd main
|
||||||
mvn clean install -Prelease
|
mvn clean install -Prelease,windows
|
||||||
|
# or mvn clean install -Prelease,mac
|
||||||
|
# or mvn clean install -Prelease,linux
|
||||||
```
|
```
|
||||||
|
|
||||||
This will build all the jars and bundle them together with their OS-specific dependencies under `main/buildkit/target`. This can now be used to build native packages.
|
This will build all the jars and bundle them together with their OS-specific dependencies under `main/buildkit/target`. This can now be used to build native packages.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
<id>tarball</id>
|
<id>tarball</id>
|
||||||
<includeBaseDirectory>false</includeBaseDirectory>
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
@@ -14,13 +14,6 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/</directory>
|
|
||||||
<includes>
|
|
||||||
<include>ffi-version.txt</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>target/</directory>
|
<directory>target/</directory>
|
||||||
<includes>
|
<includes>
|
||||||
@@ -43,12 +36,5 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/linux-libs</directory>
|
|
||||||
<includes>
|
|
||||||
<include>*.jar</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
</fileSets>
|
</fileSets>
|
||||||
</assembly>
|
</assembly>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
<id>tarball</id>
|
<id>tarball</id>
|
||||||
<includeBaseDirectory>false</includeBaseDirectory>
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
@@ -14,13 +14,6 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/</directory>
|
|
||||||
<includes>
|
|
||||||
<include>ffi-version.txt</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>target/</directory>
|
<directory>target/</directory>
|
||||||
<includes>
|
<includes>
|
||||||
@@ -43,12 +36,5 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/mac-libs</directory>
|
|
||||||
<includes>
|
|
||||||
<include>*.jar</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
</fileSets>
|
</fileSets>
|
||||||
</assembly>
|
</assembly>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
<id>tarball</id>
|
<id>tarball</id>
|
||||||
<includeBaseDirectory>false</includeBaseDirectory>
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
@@ -14,13 +14,6 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/</directory>
|
|
||||||
<includes>
|
|
||||||
<include>ffi-version.txt</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
<fileSet>
|
||||||
<directory>target/</directory>
|
<directory>target/</directory>
|
||||||
<includes>
|
<includes>
|
||||||
@@ -43,12 +36,5 @@
|
|||||||
</includes>
|
</includes>
|
||||||
<outputDirectory>libs</outputDirectory>
|
<outputDirectory>libs</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
<fileSet>
|
|
||||||
<directory>target/win-libs</directory>
|
|
||||||
<includes>
|
|
||||||
<include>*.jar</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
</fileSets>
|
</fileSets>
|
||||||
</assembly>
|
</assembly>
|
||||||
+144
-102
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta1</version>
|
<version>1.5.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>buildkit</artifactId>
|
<artifactId>buildkit</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-resources</id>
|
<id>copy-resources</id>
|
||||||
@@ -55,8 +54,8 @@
|
|||||||
|
|
||||||
<!-- copy libraries to target/libs/: -->
|
<!-- copy libraries to target/libs/: -->
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
<version>3.1.1</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-libs</id>
|
<id>copy-libs</id>
|
||||||
@@ -65,110 +64,153 @@
|
|||||||
<goal>copy-dependencies</goal>
|
<goal>copy-dependencies</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<includeScope>runtime</includeScope>
|
||||||
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
<excludeClassifiers>linux,mac,win</excludeClassifiers>
|
<excludeClassifiers>linux,mac,win</excludeClassifiers>
|
||||||
<excludeArtifactIds>dbus-java,secret-service,hkdf,java-utils</excludeArtifactIds>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>copy-linux-libs</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/linux-libs</outputDirectory>
|
|
||||||
<includeGroupIds>org.openjfx</includeGroupIds>
|
|
||||||
<classifier>linux</classifier>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>copy-linux-secret-service</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/linux-libs</outputDirectory>
|
|
||||||
<includeArtifactIds>dbus-java,secret-service,hkdf,java-utils</includeArtifactIds>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>copy-mac-libs</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/mac-libs</outputDirectory>
|
|
||||||
<includeGroupIds>org.openjfx</includeGroupIds>
|
|
||||||
<classifier>mac</classifier>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>copy-win-libs</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/win-libs</outputDirectory>
|
|
||||||
<includeGroupIds>org.openjfx</includeGroupIds>
|
|
||||||
<classifier>win</classifier>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- create buildkit.zip: -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>3.1.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>assemble-linux</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<descriptors>
|
|
||||||
<descriptor>assembly-linux.xml</descriptor>
|
|
||||||
</descriptors>
|
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
|
||||||
<finalName>buildkit-linux</finalName>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>assemble-mac</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<descriptors>
|
|
||||||
<descriptor>assembly-mac.xml</descriptor>
|
|
||||||
</descriptors>
|
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
|
||||||
<finalName>buildkit-mac</finalName>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>assemble-win</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<descriptors>
|
|
||||||
<descriptor>assembly-win.xml</descriptor>
|
|
||||||
</descriptors>
|
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
|
||||||
<finalName>buildkit-win</finalName>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>linux</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble-linux</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>assembly-linux.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<finalName>buildkit-linux</finalName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-linux-libs</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<includeGroupIds>org.openjfx</includeGroupIds>
|
||||||
|
<classifier>linux</classifier>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>mac</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble-mac</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>assembly-mac.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<finalName>buildkit-mac</finalName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-mac-libs</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<includeGroupIds>org.openjfx</includeGroupIds>
|
||||||
|
<classifier>mac</classifier>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>windows</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble-win</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>assembly-win.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<finalName>buildkit-win</finalName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-win-libs</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<includeGroupIds>org.openjfx</includeGroupIds>
|
||||||
|
<classifier>win</classifier>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
@@ -1 +0,0 @@
|
|||||||
${cryptomator.jni.version}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
cd $(dirname $0)
|
||||||
java \
|
java \
|
||||||
-cp "libs/*" \
|
-cp "libs/*" \
|
||||||
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" \
|
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json" \
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
cd $(dirname $0)
|
||||||
java \
|
java \
|
||||||
-cp "libs/*" \
|
-cp "libs/*" \
|
||||||
-Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" \
|
-Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" \
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ java ^
|
|||||||
-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" ^
|
-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" ^
|
||||||
-Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" ^
|
-Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" ^
|
||||||
-Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" ^
|
-Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" ^
|
||||||
|
-Dcryptomator.mountPointsDir="~/Cryptomator" ^
|
||||||
-Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" ^
|
-Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" ^
|
||||||
-Xss20m ^
|
-Xss20m ^
|
||||||
-Xmx512m ^
|
-Xmx512m ^
|
||||||
|
|||||||
+10
-4
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta1</version>
|
<version>1.5.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
<name>Cryptomator Commons</name>
|
<name>Cryptomator Commons</name>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>jni</artifactId>
|
<artifactId>integrations-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JavaFx -->
|
<!-- JavaFx -->
|
||||||
@@ -44,10 +44,16 @@
|
|||||||
|
|
||||||
<!-- EasyBind -->
|
<!-- EasyBind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.fxmisc.easybind</groupId>
|
<groupId>com.tobiasdiez</groupId>
|
||||||
<artifactId>easybind</artifactId>
|
<artifactId>easybind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.auth0</groupId>
|
||||||
|
<artifactId>java-jwt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Google -->
|
<!-- Google -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
|
|||||||
@@ -5,35 +5,53 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
import dagger.Binds;
|
import com.tobiasdiez.easybind.EasyBind;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import javafx.beans.binding.Binding;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.keychain.KeychainModule;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.SettingsProvider;
|
import org.cryptomator.common.settings.SettingsProvider;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultComponent;
|
import org.cryptomator.common.vaults.VaultComponent;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javafx.beans.binding.Binding;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@Module(subcomponents = {VaultComponent.class})
|
@Module(subcomponents = {VaultComponent.class}, includes = {KeychainModule.class})
|
||||||
public abstract class CommonsModule {
|
public abstract class CommonsModule {
|
||||||
|
|
||||||
private static final int NUM_SCHEDULER_THREADS = 4;
|
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
|
||||||
|
private static final int NUM_SCHEDULER_THREADS = 2;
|
||||||
|
private static final int NUM_CORE_BG_THREADS = 6;
|
||||||
|
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("licensePublicKey")
|
||||||
|
static String provideLicensePublicKey() {
|
||||||
|
// in PEM format without the dash-escaped begin/end lines
|
||||||
|
return "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB7NfnqiZbg2KTmoflmZ71PbXru7oW" //
|
||||||
|
+ "fmnV2yv3eDjlDfGruBrqz9TtXBZV/eYWt31xu1osIqaT12lKBvZ511aaAkIBeOEV" //
|
||||||
|
+ "gwcBIlJr6kUw7NKzeJt7r2rrsOyQoOG2nWc/Of/NBqA3mIZRHk5Aq1YupFdD26QE" //
|
||||||
|
+ "r0DzRyj4ixPIt38CQB8=";
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -56,21 +74,41 @@ public abstract class CommonsModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
|
||||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
||||||
|
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
|
||||||
Thread t = new Thread(r);
|
Thread t = new Thread(r);
|
||||||
t.setName("Background Thread " + threadNumber.getAndIncrement());
|
t.setName(name);
|
||||||
|
t.setUncaughtExceptionHandler(CommonsModule::handleUncaughtExceptionInBackgroundThread);
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
|
LOG.debug("Starting {}", t.getName());
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
shutdownTaskScheduler.accept(executorService::shutdown);
|
shutdownHook.runOnShutdown(executorService::shutdown);
|
||||||
return executorService;
|
return executorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
abstract ExecutorService bindExecutorService(ScheduledExecutorService executor);
|
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
|
||||||
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
|
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||||
|
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName(name);
|
||||||
|
t.setUncaughtExceptionHandler(CommonsModule::handleUncaughtExceptionInBackgroundThread);
|
||||||
|
t.setDaemon(true);
|
||||||
|
LOG.debug("Starting {}", t.getName());
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
shutdownHook.runOnShutdown(executorService::shutdown);
|
||||||
|
return executorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleUncaughtExceptionInBackgroundThread(Thread thread, Throwable throwable) {
|
||||||
|
LOG.error("Uncaught exception in " + thread.getName(), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
public interface Constants {
|
||||||
|
|
||||||
|
String MASTERKEY_FILENAME = "masterkey.cryptomator";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ public class Environment {
|
|||||||
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
|
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
|
||||||
private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
|
private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
|
||||||
private static final char PATH_LIST_SEP = ':';
|
private static final char PATH_LIST_SEP = ':';
|
||||||
|
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Environment() {
|
public Environment() {
|
||||||
@@ -37,6 +38,9 @@ public class Environment {
|
|||||||
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
|
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
|
||||||
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
|
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
|
||||||
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
|
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
|
||||||
|
LOG.debug("cryptomator.minPwLength: {}", System.getProperty("cryptomator.minPwLength"));
|
||||||
|
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
|
||||||
|
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean useCustomLogbackConfig() {
|
public boolean useCustomLogbackConfig() {
|
||||||
@@ -63,12 +67,33 @@ public class Environment {
|
|||||||
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
|
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getBuildNumber() {
|
||||||
|
return Optional.ofNullable(System.getProperty("cryptomator.buildNumber"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinPwLength() {
|
||||||
|
return getInt("cryptomator.minPwLength", DEFAULT_MIN_PW_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useExperimentalFuse() {
|
||||||
|
return Boolean.getBoolean("fuse.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInt(String propertyName, int defaultValue) {
|
||||||
|
String value = System.getProperty(propertyName);
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) { // includes "null" values
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<Path> getPath(String propertyName) {
|
private Optional<Path> getPath(String propertyName) {
|
||||||
String value = System.getProperty(propertyName);
|
String value = System.getProperty(propertyName);
|
||||||
return Optional.ofNullable(value).map(Paths::get);
|
return Optional.ofNullable(value).map(Paths::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
// visible for testing
|
// visible for testing
|
||||||
|
|
||||||
Stream<Path> getPaths(String propertyName) {
|
Stream<Path> getPaths(String propertyName) {
|
||||||
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
|
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
|
||||||
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
|
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
|
||||||
@@ -92,5 +117,4 @@ public class Environment {
|
|||||||
return StreamSupport.stream(spliter, false);
|
return StreamSupport.stream(spliter, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2019 Skymatic GmbH.
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the accompanying LICENSE file.
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.cryptomator.common;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import org.cryptomator.jni.JniFunctions;
|
|
||||||
import org.cryptomator.jni.MacFunctions;
|
|
||||||
import org.cryptomator.jni.WinFunctions;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class JniModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Optional<MacFunctions> provideOptionalMacFunctions() {
|
|
||||||
return JniFunctions.macFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Optional<WinFunctions> provideOptionalWinFunctions() {
|
|
||||||
return JniFunctions.winFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,12 +5,12 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
public final class LazyInitializer {
|
public final class LazyInitializer {
|
||||||
|
|
||||||
private LazyInitializer() {
|
private LazyInitializer() {
|
||||||
@@ -18,7 +18,7 @@ public final class LazyInitializer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #initializeLazily(AtomicReference, SupplierThrowingException, Class)} except that no checked exception may be thrown by the factory function.
|
* Same as {@link #initializeLazily(AtomicReference, SupplierThrowingException, Class)} except that no checked exception may be thrown by the factory function.
|
||||||
*
|
*
|
||||||
* @param <T> Type of the value
|
* @param <T> Type of the value
|
||||||
* @param reference A reference to a maybe not yet initialized value.
|
* @param reference A reference to a maybe not yet initialized value.
|
||||||
* @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
|
* @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
|
||||||
@@ -31,7 +31,7 @@ public final class LazyInitializer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
|
* Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
|
||||||
*
|
*
|
||||||
* @param <T> Type of the value
|
* @param <T> Type of the value
|
||||||
* @param <E> Type of the any expected exception that may occur during initialization
|
* @param <E> Type of the any expected exception that may occur during initialization
|
||||||
* @param reference A reference to a maybe not yet initialized value.
|
* @param reference A reference to a maybe not yet initialized value.
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class LicenseChecker {
|
||||||
|
|
||||||
|
private final JWTVerifier verifier;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public LicenseChecker(@Named("licensePublicKey") String pemEncodedPublicKey) {
|
||||||
|
Algorithm algorithm = Algorithm.ECDSA512(decodePublicKey(pemEncodedPublicKey), null);
|
||||||
|
this.verifier = JWT.require(algorithm).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECPublicKey decodePublicKey(String pemEncodedPublicKey) {
|
||||||
|
try {
|
||||||
|
byte[] keyBytes = BaseEncoding.base64().decode(pemEncodedPublicKey);
|
||||||
|
PublicKey key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(keyBytes));
|
||||||
|
if (key instanceof ECPublicKey) {
|
||||||
|
return (ECPublicKey) key;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Key not an EC public key.");
|
||||||
|
}
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid license public key", e);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<DecodedJWT> check(String licenseKey) {
|
||||||
|
try {
|
||||||
|
return Optional.of(verifier.verify(licenseKey));
|
||||||
|
} catch (JWTVerificationException exception) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class LicenseHolder {
|
||||||
|
|
||||||
|
private final Settings settings;
|
||||||
|
private final LicenseChecker licenseChecker;
|
||||||
|
private final ObjectProperty<DecodedJWT> validJwtClaims;
|
||||||
|
private final StringBinding licenseSubject;
|
||||||
|
private final BooleanBinding validLicenseProperty;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public LicenseHolder(LicenseChecker licenseChecker, Settings settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.licenseChecker = licenseChecker;
|
||||||
|
this.validJwtClaims = new SimpleObjectProperty<>();
|
||||||
|
this.licenseSubject = Bindings.createStringBinding(this::getLicenseSubject, validJwtClaims);
|
||||||
|
this.validLicenseProperty = validJwtClaims.isNotNull();
|
||||||
|
|
||||||
|
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
|
||||||
|
validJwtClaims.set(claims.orElse(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateAndStoreLicense(String licenseKey) {
|
||||||
|
Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
|
||||||
|
validJwtClaims.set(claims.orElse(null));
|
||||||
|
if (claims.isPresent()) {
|
||||||
|
settings.licenseKey().set(licenseKey);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Observable Properties */
|
||||||
|
|
||||||
|
public Optional<String> getLicenseKey() {
|
||||||
|
DecodedJWT claims = validJwtClaims.get();
|
||||||
|
if (claims != null) {
|
||||||
|
return Optional.of(claims.getToken());
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBinding licenseSubjectProperty() {
|
||||||
|
return licenseSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLicenseSubject() {
|
||||||
|
DecodedJWT claims = validJwtClaims.get();
|
||||||
|
if (claims != null) {
|
||||||
|
return claims.getSubject();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding validLicenseProperty() {
|
||||||
|
return validLicenseProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidLicense() {
|
||||||
|
return validLicenseProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,28 +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;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public final class Optionals {
|
|
||||||
|
|
||||||
private Optionals() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a function that is equivalent to the input function but immediately gets the value of the returned optional when invoked.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the input to the function
|
|
||||||
* @param <R> the type of the result of the function
|
|
||||||
* @param function An {@code Optional}-bearing input function {@code Function<Foo, Optional<Bar>>}
|
|
||||||
* @return A {@code Function<Foo, Bar>}, that may throw a NoSuchElementException, if the original function returns an empty optional.
|
|
||||||
*/
|
|
||||||
public static <T, R> Function<T, R> unwrap(Function<T, Optional<R>> function) {
|
|
||||||
return t -> function.apply(t).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
|
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Runnables;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ShutdownHook extends Thread {
|
||||||
|
|
||||||
|
private static final int PRIO_VERY_LAST = Integer.MIN_VALUE;
|
||||||
|
public static final int PRIO_LAST = PRIO_VERY_LAST + 1;
|
||||||
|
public static final int PRIO_DEFAULT = 0;
|
||||||
|
public static final int PRIO_FIRST = Integer.MAX_VALUE;
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ShutdownHook.class);
|
||||||
|
private static final OrderedTask POISON = new OrderedTask(PRIO_VERY_LAST, Runnables.doNothing());
|
||||||
|
private final Queue<OrderedTask> tasks = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ShutdownHook() {
|
||||||
|
super(null, null, "ShutdownTasks", 0);
|
||||||
|
Runtime.getRuntime().addShutdownHook(this);
|
||||||
|
LOG.debug("Registered shutdown hook.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LOG.debug("Running graceful shutdown tasks...");
|
||||||
|
tasks.add(POISON);
|
||||||
|
Runnable task;
|
||||||
|
while ((task = tasks.remove()) != POISON) {
|
||||||
|
try {
|
||||||
|
task.run();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
LOG.error("Exception while shutting down.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a task to be run during shutdown with default order
|
||||||
|
*
|
||||||
|
* @param task The task to be scheduled
|
||||||
|
*/
|
||||||
|
public void runOnShutdown(Runnable task) {
|
||||||
|
runOnShutdown(PRIO_DEFAULT, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a task to be run with the given priority
|
||||||
|
*
|
||||||
|
* @param priority Tasks with high priority will be run before task with lower priority
|
||||||
|
* @param task The task to be scheduled
|
||||||
|
*/
|
||||||
|
public void runOnShutdown(int priority, Runnable task) {
|
||||||
|
tasks.add(new OrderedTask(priority, task));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OrderedTask implements Comparable<OrderedTask>, Runnable {
|
||||||
|
|
||||||
|
private final int priority;
|
||||||
|
private final Runnable task;
|
||||||
|
|
||||||
|
public OrderedTask(int priority, Runnable task) {
|
||||||
|
this.priority = priority;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(OrderedTask other) {
|
||||||
|
// overflow-safe signum impl:
|
||||||
|
if (this.priority > other.priority) {
|
||||||
|
return -1; // higher prio -> this before other
|
||||||
|
} else if (this.priority < other.priority) {
|
||||||
|
return +1; // lower prio -> other before this
|
||||||
|
} else {
|
||||||
|
return 0; // same prio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.binding.ObjectExpression;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class KeychainManager implements KeychainAccessProvider {
|
||||||
|
|
||||||
|
private final ObjectExpression<KeychainAccessProvider> keychain;
|
||||||
|
private final LoadingCache<String, BooleanProperty> passphraseStoredProperties;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
||||||
|
this.keychain = selectedKeychain;
|
||||||
|
this.passphraseStoredProperties = CacheBuilder.newBuilder() //
|
||||||
|
.weakValues() //
|
||||||
|
.build(CacheLoader.from(this::createStoredPassphraseProperty));
|
||||||
|
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException {
|
||||||
|
var result = keychain.getValue();
|
||||||
|
if (result == null) {
|
||||||
|
throw new NoKeychainAccessProviderException();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
||||||
|
getKeychainOrFail().storePassphrase(key, passphrase);
|
||||||
|
setPassphraseStored(key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char[] loadPassphrase(String key) throws KeychainAccessException {
|
||||||
|
char[] passphrase = getKeychainOrFail().loadPassphrase(key);
|
||||||
|
setPassphraseStored(key, passphrase != null);
|
||||||
|
return passphrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePassphrase(String key) throws KeychainAccessException {
|
||||||
|
getKeychainOrFail().deletePassphrase(key);
|
||||||
|
setPassphraseStored(key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
||||||
|
getKeychainOrFail().changePassphrase(key, passphrase);
|
||||||
|
setPassphraseStored(key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return keychain.getValue() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the keychain knows a passphrase for the given key.
|
||||||
|
* <p>
|
||||||
|
* Expensive operation. If possible, use {@link #getPassphraseStoredProperty(String)} instead.
|
||||||
|
*
|
||||||
|
* @param key The key to look up
|
||||||
|
* @return <code>true</code> if a password for <code>key</code> is stored.
|
||||||
|
* @throws KeychainAccessException
|
||||||
|
*/
|
||||||
|
public boolean isPassphraseStored(String key) throws KeychainAccessException {
|
||||||
|
char[] storedPw = null;
|
||||||
|
try {
|
||||||
|
storedPw = getKeychainOrFail().loadPassphrase(key);
|
||||||
|
return storedPw != null;
|
||||||
|
} finally {
|
||||||
|
if (storedPw != null) {
|
||||||
|
Arrays.fill(storedPw, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPassphraseStored(String key, boolean value) {
|
||||||
|
BooleanProperty property = passphraseStoredProperties.getIfPresent(key);
|
||||||
|
if (property != null) {
|
||||||
|
if (Platform.isFxApplicationThread()) {
|
||||||
|
property.set(value);
|
||||||
|
} else {
|
||||||
|
Platform.runLater(() -> property.set(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable property for use in the UI that tells whether a passphrase is stored for the given key.
|
||||||
|
* <p>
|
||||||
|
* Assuming that this process is the only process modifying Cryptomator-related items in the system keychain, this
|
||||||
|
* property stays in memory in an attempt to avoid unnecessary calls to the system keychain. Note that due to this
|
||||||
|
* fact the value stored in the returned property is not 100% reliable. Code defensively!
|
||||||
|
*
|
||||||
|
* @param key The key to look up
|
||||||
|
* @return An observable property which is <code>true</code> when it almost certain that a password for <code>key</code> is stored.
|
||||||
|
* @see #isPassphraseStored(String)
|
||||||
|
*/
|
||||||
|
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
||||||
|
return passphraseStoredProperties.getUnchecked(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BooleanProperty createStoredPassphraseProperty(String key) {
|
||||||
|
try {
|
||||||
|
return new SimpleBooleanProperty(isPassphraseStored(key));
|
||||||
|
} catch (KeychainAccessException e) {
|
||||||
|
return new SimpleBooleanProperty(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.ObjectExpression;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class KeychainModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories() {
|
||||||
|
return ServiceLoader.load(KeychainAccessProvider.class).stream().collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) {
|
||||||
|
return availableFactories.stream() //
|
||||||
|
.map(ServiceLoader.Provider::get) //
|
||||||
|
.filter(KeychainAccessProvider::isSupported) //
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
|
||||||
|
return Bindings.createObjectBinding(() -> {
|
||||||
|
var selectedProviderClass = settings.keychainBackend().get().getProviderClass();
|
||||||
|
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
|
||||||
|
var fallbackProvider = providers.stream().findAny().orElse(null);
|
||||||
|
return selectedProvider.orElse(fallbackProvider);
|
||||||
|
}, settings.keychainBackend());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown by {@link KeychainManager} if attempted to access a keychain despite no supported keychain access provider being available.
|
||||||
|
*/
|
||||||
|
public class NoKeychainAccessProviderException extends KeychainAccessException {
|
||||||
|
|
||||||
|
public NoKeychainAccessProviderException() {
|
||||||
|
super("Did not find any supported keychain access provider.");
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
import org.cryptomator.common.vaults.WindowsDriveLetters;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class AvailableDriveLetterChooser implements MountPointChooser {
|
||||||
|
|
||||||
|
public static final int PRIORITY = 200;
|
||||||
|
|
||||||
|
private final WindowsDriveLetters windowsDriveLetters;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public AvailableDriveLetterChooser(WindowsDriveLetters windowsDriveLetters) {
|
||||||
|
this.windowsDriveLetters = windowsDriveLetters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(Volume caller) {
|
||||||
|
return SystemUtils.IS_OS_WINDOWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Path> chooseMountPoint(Volume caller) {
|
||||||
|
return this.windowsDriveLetters.getAvailableDriveLetterPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CustomDriveLetterChooser implements MountPointChooser {
|
||||||
|
|
||||||
|
public static final int PRIORITY = 100;
|
||||||
|
|
||||||
|
private final VaultSettings vaultSettings;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CustomDriveLetterChooser(VaultSettings vaultSettings) {
|
||||||
|
this.vaultSettings = vaultSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(Volume caller) {
|
||||||
|
return SystemUtils.IS_OS_WINDOWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Path> chooseMountPoint(Volume caller) {
|
||||||
|
return this.vaultSettings.getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.NotDirectoryException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CustomMountPointChooser implements MountPointChooser {
|
||||||
|
|
||||||
|
public static final int PRIORITY = 0;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
|
||||||
|
|
||||||
|
private final VaultSettings vaultSettings;
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CustomMountPointChooser(VaultSettings vaultSettings, Environment environment) {
|
||||||
|
this.vaultSettings = vaultSettings;
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(Volume caller) {
|
||||||
|
//Disable if useExperimentalFuse is required (Win + Fuse), but set to false
|
||||||
|
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Path> chooseMountPoint(Volume caller) {
|
||||||
|
//VaultSettings#getCustomMountPath already checks whether the saved custom mountpoint should be used
|
||||||
|
return this.vaultSettings.getCustomMountPath().map(Paths::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
|
||||||
|
switch (caller.getMountPointRequirement()) {
|
||||||
|
case PARENT_NO_MOUNT_POINT -> prepareParentNoMountPoint(mountPoint);
|
||||||
|
case EMPTY_MOUNT_POINT -> prepareEmptyMountPoint(mountPoint);
|
||||||
|
case NONE -> {
|
||||||
|
//Requirement "NONE" doesn't make any sense here.
|
||||||
|
//No need to prepare/verify a Mountpoint without requiring one...
|
||||||
|
throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement"));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
//Currently the case for "PARENT_OPT_MOUNT_POINT"
|
||||||
|
throw new InvalidMountPointException(new IllegalStateException("Not implemented"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareParentNoMountPoint(Path mountPoint) throws InvalidMountPointException {
|
||||||
|
//This the case on Windows when using FUSE
|
||||||
|
//See https://github.com/billziss-gh/winfsp/issues/320
|
||||||
|
Path parent = mountPoint.getParent();
|
||||||
|
if (!Files.isDirectory(parent)) {
|
||||||
|
throw new InvalidMountPointException(new NotDirectoryException(parent.toString()));
|
||||||
|
}
|
||||||
|
//We must use #notExists() here because notExists =/= !exists (see docs)
|
||||||
|
if (!Files.notExists(mountPoint, LinkOption.NOFOLLOW_LINKS)) {
|
||||||
|
//File exists OR can't be determined
|
||||||
|
throw new InvalidMountPointException(new FileAlreadyExistsException(mountPoint.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareEmptyMountPoint(Path mountPoint) throws InvalidMountPointException {
|
||||||
|
//This is the case for Windows when using Dokany and for Linux and Mac
|
||||||
|
if (!Files.isDirectory(mountPoint)) {
|
||||||
|
throw new InvalidMountPointException(new NotDirectoryException(mountPoint.toString()));
|
||||||
|
}
|
||||||
|
try (DirectoryStream<Path> ds = Files.newDirectoryStream(mountPoint)) {
|
||||||
|
if (ds.iterator().hasNext()) {
|
||||||
|
throw new InvalidMountPointException(new DirectoryNotEmptyException(mountPoint.toString()));
|
||||||
|
}
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new InvalidMountPointException("IOException while checking folder content", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
public class InvalidMountPointException extends Exception {
|
||||||
|
|
||||||
|
public InvalidMountPointException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidMountPointException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidMountPointException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
|
||||||
|
public class IrregularUnmountCleaner {
|
||||||
|
|
||||||
|
public static Logger LOG = LoggerFactory.getLogger(IrregularUnmountCleaner.class);
|
||||||
|
|
||||||
|
public static void removeIrregularUnmountDebris(Path dirContainingMountPoints) {
|
||||||
|
IOException cleanupFailed = new IOException("Cleanup failed");
|
||||||
|
|
||||||
|
try {
|
||||||
|
LOG.debug("Performing cleanup of mountpoint dir {}.", dirContainingMountPoints);
|
||||||
|
for (Path p : Files.newDirectoryStream(dirContainingMountPoints)) {
|
||||||
|
try {
|
||||||
|
var attr = Files.readAttributes(p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
||||||
|
if (attr.isOther() && attr.isDirectory()) { // yes, this is possible with windows junction points -.-
|
||||||
|
Files.delete(p);
|
||||||
|
} else if (attr.isDirectory()) {
|
||||||
|
deleteEmptyDir(p);
|
||||||
|
} else if (attr.isSymbolicLink()) {
|
||||||
|
deleteDeadLink(p);
|
||||||
|
} else {
|
||||||
|
LOG.debug("Found non-directory element in mountpoint dir: {}", p);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
cleanupFailed.addSuppressed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanupFailed.getSuppressed().length > 0) {
|
||||||
|
throw cleanupFailed;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Unable to perform cleanup of mountpoint dir {}.", dirContainingMountPoints, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteEmptyDir(Path dir) throws IOException {
|
||||||
|
assert Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS);
|
||||||
|
try {
|
||||||
|
Files.delete(dir); // attempt to delete dir non-recursively (will fail, if there are contents)
|
||||||
|
} catch (DirectoryNotEmptyException e) {
|
||||||
|
LOG.info("Found non-empty directory in mountpoint dir: {}", dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteDeadLink(Path symlink) throws IOException {
|
||||||
|
assert Files.isSymbolicLink(symlink);
|
||||||
|
if (Files.notExists(symlink)) { // following link: target does not exist
|
||||||
|
Files.delete(symlink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for the Mountpoint-Choosing-Operation that results in the choice and
|
||||||
|
* preparation of a mountpoint or an exception otherwise.<br>
|
||||||
|
* <p>All <i>MountPointChoosers (MPCs)</i> need to implement this class and must be added to
|
||||||
|
* the pool of possible MPCs by the {@link MountPointChooserModule MountPointChooserModule.}
|
||||||
|
* The MountPointChooserModule will sort them according to their {@link #getPriority() priority.}
|
||||||
|
* The priority must be defined by the developer to reflect a useful execution order.<br>
|
||||||
|
* A specific priority <b>must not</b> be assigned to more than one MPC at a time;
|
||||||
|
* the result of having two MPCs with equal priority is undefined.
|
||||||
|
*
|
||||||
|
* <p>MPCs are executed by a {@link Volume} in ascending order of their priority
|
||||||
|
* (smaller priorities are tried first) to find and prepare a suitable mountpoint for the volume.
|
||||||
|
* The volume has access to a {@link SortedSet} of MPCs in this specific order,
|
||||||
|
* that is provided by the Module. The Set contains all available Choosers, even if they
|
||||||
|
* are not {@link #isApplicable(Volume) applicable} for the Vault/Volume. The Volume must
|
||||||
|
* check whether a MPC is applicable by invoking {@code #isApplicable(Volume)} on it
|
||||||
|
* <i>before</i> calling {@code #chooseMountPoint(Volume)}.
|
||||||
|
*
|
||||||
|
* <p>At execution of a MPC {@link #chooseMountPoint(Volume)} is called to choose a mountpoint
|
||||||
|
* according to the MPC's <i>strategy.</i> The <i>strategy</i> can involve reading configs,
|
||||||
|
* searching the filesystem, processing user-input or similar operations.
|
||||||
|
* If {@code #chooseMountPoint(Volume)} returns a non-null path (everything but
|
||||||
|
* {@linkplain Optional#empty()}) the MPC's {@link #prepare(Volume, Path)} method is called and the
|
||||||
|
* MountPoint is verified and/or prepared. In this case <i>no other MPC's will be called for
|
||||||
|
* this volume, even if {@code #prepare(Volume, Path)} fails.</i>
|
||||||
|
*
|
||||||
|
* <p>If {@code #chooseMountPoint(Volume)} yields no result, the next MPC is executed
|
||||||
|
* <i>without</i> first calling the {@code #prepare(Volume, Path)} method of the current MPC.
|
||||||
|
* This is repeated until<br>
|
||||||
|
* <ul>
|
||||||
|
* <li><b>either</b> a mountpoint is returned by {@code #chooseMountPoint(Volume)}
|
||||||
|
* and {@code #prepare(Volume, Path)} succeeds or fails, ending the entire operation</li>
|
||||||
|
* <li><b>or</b> no MPC remains and an {@link InvalidMountPointException} is thrown.</li>
|
||||||
|
* </ul>
|
||||||
|
* If the {@code #prepare(Volume, Path)} method of a MPC fails, the entire
|
||||||
|
* Mountpoint-Choosing-Operation is aborted and the method should do all necessary cleanup
|
||||||
|
* before throwing the exception.
|
||||||
|
* If the preparation succeeds {@link #cleanup(Volume, Path)} can be used after unmount to do any
|
||||||
|
* remaining cleanup.
|
||||||
|
*/
|
||||||
|
public interface MountPointChooser extends Comparable<MountPointChooser> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link Volume} to determine whether this MountPointChooser is
|
||||||
|
* applicable for mounting the Vault/Volume, especially with regard to the
|
||||||
|
* current system configuration and particularities of the Volume type.
|
||||||
|
*
|
||||||
|
* <p>Developers should override this method to check for system configurations
|
||||||
|
* that are unsuitable for this MPC.
|
||||||
|
*
|
||||||
|
* @param caller The Volume that is calling the method to determine applicability of the MPC
|
||||||
|
* @return a boolean flag; true if applicable, else false.
|
||||||
|
* @see #chooseMountPoint(Volume)
|
||||||
|
*/
|
||||||
|
boolean isApplicable(Volume caller);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a {@link Volume} to choose a mountpoint according to the
|
||||||
|
* MountPointChoosers strategy.
|
||||||
|
*
|
||||||
|
* <p>This method must only be called for MPCs that were deemed
|
||||||
|
* {@link #isApplicable(Volume) applicable} by the {@link Volume Volume.}
|
||||||
|
* Developers should override this method to find or extract a mountpoint for
|
||||||
|
* the volume <b>without</b> preparing it. Preparation should be done by
|
||||||
|
* {@link #prepare(Volume, Path)} instead.
|
||||||
|
* Exceptions in this method should be handled gracefully and result in returning
|
||||||
|
* {@link Optional#empty()} instead of throwing an exception.
|
||||||
|
*
|
||||||
|
* @param caller The Volume that is calling the method to choose a mountpoint
|
||||||
|
* @return the chosen path or {@link Optional#empty()} if an exception occurred
|
||||||
|
* or no mountpoint could be found.
|
||||||
|
* @see #isApplicable(Volume)
|
||||||
|
* @see #prepare(Volume, Path)
|
||||||
|
*/
|
||||||
|
Optional<Path> chooseMountPoint(Volume caller);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a {@link Volume} to prepare and/or verify the chosen mountpoint.<br>
|
||||||
|
* This method is only called if the {@link #chooseMountPoint(Volume)} method
|
||||||
|
* of the same MountPointChooser returned a path.
|
||||||
|
*
|
||||||
|
* <p>Developers should override this method to prepare the mountpoint for
|
||||||
|
* the volume and check for any obstacles that could hinder the mount operation.
|
||||||
|
* The mountpoint is deemed "prepared" if it can be used to mount a volume
|
||||||
|
* without any further filesystem actions or user interaction. If this is not possible,
|
||||||
|
* this method should fail. In other words: This method should not return without
|
||||||
|
* either failing or finalizing the preparation of the mountpoint.
|
||||||
|
* Generally speaking exceptions should be wrapped as
|
||||||
|
* {@link InvalidMountPointException} to allow efficient handling by the caller.
|
||||||
|
*
|
||||||
|
* <p>Often the preparation of a mountpoint involves creating files or others
|
||||||
|
* actions that require cleaning up after the volume is unmounted.
|
||||||
|
* In this case developers should override the {@link #cleanup(Volume, Path)}
|
||||||
|
* method and return {@code true} to the volume to indicate that the
|
||||||
|
* {@code #cleanup} method of this MPC should be called after unmount.
|
||||||
|
*
|
||||||
|
* <p><b>Please note:</b> If this method fails the entire
|
||||||
|
* Mountpoint-Choosing-Operation is aborted without calling
|
||||||
|
* {@link #cleanup(Volume, Path)} or any other MPCs. Therefore this method should
|
||||||
|
* do all necessary cleanup before throwing the exception.
|
||||||
|
*
|
||||||
|
* @param caller The Volume that is calling the method to prepare a mountpoint
|
||||||
|
* @param mountPoint the mountpoint chosen by {@link #chooseMountPoint(Volume)}
|
||||||
|
* @return a boolean flag; true if cleanup is needed, false otherwise
|
||||||
|
* @throws InvalidMountPointException if the preparation fails
|
||||||
|
* @see #chooseMountPoint(Volume)
|
||||||
|
* @see #cleanup(Volume, Path)
|
||||||
|
*/
|
||||||
|
default boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
|
||||||
|
return false; //NO-OP
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a {@link Volume} to do any cleanup needed after unmount.
|
||||||
|
*
|
||||||
|
* <p>This method is only called if the {@link #prepare(Volume, Path)} method
|
||||||
|
* of the same MountPointChooser returned {@code true}. Typically developers want to
|
||||||
|
* delete any files created prior to mount or do similar tasks.<br>
|
||||||
|
* Exceptions in this method should be handled gracefully.
|
||||||
|
*
|
||||||
|
* @param caller The Volume that is calling the method to cleanup the prepared mountpoint
|
||||||
|
* @param mountPoint the mountpoint that was prepared by {@link #prepare(Volume, Path)}
|
||||||
|
* @see #prepare(Volume, Path)
|
||||||
|
*/
|
||||||
|
default void cleanup(Volume caller, Path mountPoint) {
|
||||||
|
//NO-OP
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link MountPointChooserModule} to sort the available MPCs
|
||||||
|
* and determine their execution order.
|
||||||
|
* The priority must be defined by the developer to reflect a useful execution order.
|
||||||
|
* MPCs with lower priorities will be placed at lower indices in the resulting
|
||||||
|
* {@link SortedSet} and will be executed with higher probability.<br>
|
||||||
|
* A specific priority <b>must not</b> be assigned to more than one MPC at a time;
|
||||||
|
* the result of having two MPCs with equal priority is undefined.
|
||||||
|
*
|
||||||
|
* @return the priority of this MPC.
|
||||||
|
*/
|
||||||
|
int getPriority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the {@link Volume} to determine the execution order of the registered MPCs.
|
||||||
|
* <b>Implementations usually may not override this method.</b> This default implementation
|
||||||
|
* sorts the MPCs in ascending order of their {@link #getPriority() priority.}<br>
|
||||||
|
* <br>
|
||||||
|
* <b>Original description:</b>
|
||||||
|
* <p>{@inheritDoc}
|
||||||
|
*
|
||||||
|
* @implNote This default implementation sorts the MPCs in ascending order
|
||||||
|
* of their {@link #getPriority() priority.}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
default int compareTo(MountPointChooser other) {
|
||||||
|
Preconditions.checkNotNull(other, "Other must not be null!");
|
||||||
|
|
||||||
|
//Sort by priority (ascending order)
|
||||||
|
return Integer.compare(this.getPriority(), other.getPriority());
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import dagger.multibindings.IntoSet;
|
||||||
|
import org.cryptomator.common.vaults.PerVault;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dagger-Module for {@link MountPointChooser MountPointChoosers.}<br>
|
||||||
|
* See there for additional information.
|
||||||
|
*
|
||||||
|
* @see MountPointChooser
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
public abstract class MountPointChooserModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
@PerVault
|
||||||
|
public abstract MountPointChooser bindCustomMountPointChooser(CustomMountPointChooser chooser);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
@PerVault
|
||||||
|
public abstract MountPointChooser bindCustomDriveLetterChooser(CustomDriveLetterChooser chooser);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
@PerVault
|
||||||
|
public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
@PerVault
|
||||||
|
public abstract MountPointChooser bindTemporaryMountPointChooser(TemporaryMountPointChooser chooser);
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@PerVault
|
||||||
|
@Named("orderedMountPointChoosers")
|
||||||
|
public static SortedSet<MountPointChooser> provideOrderedMountPointChoosers(Set<MountPointChooser> choosers) {
|
||||||
|
//Sort by natural order. The natural order is defined by MountPointChooser#compareTo
|
||||||
|
return ImmutableSortedSet.copyOf(choosers);
|
||||||
|
}
|
||||||
|
}
|
||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
package org.cryptomator.common.mountpoint;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class TemporaryMountPointChooser implements MountPointChooser {
|
||||||
|
|
||||||
|
public static final int PRIORITY = 300;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TemporaryMountPointChooser.class);
|
||||||
|
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
|
||||||
|
|
||||||
|
private final VaultSettings vaultSettings;
|
||||||
|
private final Environment environment;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment) {
|
||||||
|
this.vaultSettings = vaultSettings;
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(Volume caller) {
|
||||||
|
if (this.environment.getMountPointsDir().isEmpty()) {
|
||||||
|
LOG.warn("\"cryptomator.mountPointsDir\" is not set to a valid path!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Path> chooseMountPoint(Volume caller) {
|
||||||
|
return this.environment.getMountPointsDir().map(this::choose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path choose(Path parent) {
|
||||||
|
String basename = this.vaultSettings.mountName().get();
|
||||||
|
//regular
|
||||||
|
Path mountPoint = parent.resolve(basename);
|
||||||
|
if (Files.notExists(mountPoint)) {
|
||||||
|
return mountPoint;
|
||||||
|
}
|
||||||
|
//with id
|
||||||
|
mountPoint = parent.resolve(basename + " (" +vaultSettings.getId() + ")");
|
||||||
|
if (Files.notExists(mountPoint)) {
|
||||||
|
return mountPoint;
|
||||||
|
}
|
||||||
|
//with id and count
|
||||||
|
for (int i = 1; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
|
||||||
|
mountPoint = parent.resolve(basename + "_(" +vaultSettings.getId() + ")_"+i);
|
||||||
|
if (Files.notExists(mountPoint)) {
|
||||||
|
return mountPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.error("Failed to find feasible mountpoint at {}{}{}_x. Giving up after {} attempts.", parent, File.separator, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
|
||||||
|
// https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
|
||||||
|
// In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
|
||||||
|
// starting with version 3.5.0, FUSE will create non-existent mount points automatically.
|
||||||
|
if (SystemUtils.IS_OS_MAC && mountPoint.getParent().equals(Paths.get("/Volumes"))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (caller.getMountPointRequirement()) {
|
||||||
|
case PARENT_NO_MOUNT_POINT -> {
|
||||||
|
Files.createDirectories(mountPoint.getParent());
|
||||||
|
LOG.debug("Successfully created folder for mount point: {}", mountPoint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case EMPTY_MOUNT_POINT -> {
|
||||||
|
Files.createDirectories(mountPoint);
|
||||||
|
LOG.debug("Successfully created mount point: {}", mountPoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case NONE -> {
|
||||||
|
//Requirement "NONE" doesn't make any sense here.
|
||||||
|
//No need to prepare/verify a Mountpoint without requiring one...
|
||||||
|
throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement"));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
//Currently the case for "PARENT_OPT_MOUNT_POINT"
|
||||||
|
throw new InvalidMountPointException(new IllegalStateException("Not implemented"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new InvalidMountPointException("IOException while preparing mountpoint", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup(Volume caller, Path mountPoint) {
|
||||||
|
try {
|
||||||
|
Files.delete(mountPoint);
|
||||||
|
LOG.debug("Successfully deleted mount point: {}", mountPoint);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Could not delete mount point: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
public enum KeychainBackend {
|
||||||
|
GNOME("org.cryptomator.linux.keychain.SecretServiceKeychainAccess"),
|
||||||
|
KDE("org.cryptomator.linux.keychain.KDEWalletKeychainAccess"),
|
||||||
|
MAC_SYSTEM_KEYCHAIN("org.cryptomator.macos.keychain.MacSystemKeychainAccess"),
|
||||||
|
WIN_SYSTEM_KEYCHAIN("org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess");
|
||||||
|
|
||||||
|
private final String providerClass;
|
||||||
|
|
||||||
|
KeychainBackend(String providerClass) {
|
||||||
|
this.providerClass = providerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProviderClass() {
|
||||||
|
return providerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
@@ -15,10 +17,11 @@ import javafx.beans.property.ObjectProperty;
|
|||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.NodeOrientation;
|
import javafx.geometry.NodeOrientation;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
@@ -32,9 +35,11 @@ public class Settings {
|
|||||||
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||||
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
|
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
|
||||||
public static final boolean DEFAULT_DEBUG_MODE = false;
|
public static final boolean DEFAULT_DEBUG_MODE = false;
|
||||||
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = System.getProperty("os.name").toLowerCase().contains("windows") ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
|
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
|
||||||
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
||||||
|
public static final KeychainBackend DEFAULT_KEYCHAIN_BACKEND = SystemUtils.IS_OS_WINDOWS ? KeychainBackend.WIN_SYSTEM_KEYCHAIN : SystemUtils.IS_OS_MAC ? KeychainBackend.MAC_SYSTEM_KEYCHAIN : KeychainBackend.GNOME;
|
||||||
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
|
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
|
||||||
|
private static final String DEFAULT_LICENSE_KEY = "";
|
||||||
|
|
||||||
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
|
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||||
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
|
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
|
||||||
@@ -46,7 +51,9 @@ public class Settings {
|
|||||||
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
|
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
|
||||||
private final ObjectProperty<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
|
private final ObjectProperty<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
|
||||||
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
|
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
|
||||||
|
private final ObjectProperty<KeychainBackend> keychainBackend = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_BACKEND);
|
||||||
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
|
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
|
||||||
|
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
|
||||||
|
|
||||||
private Consumer<Settings> saveCmd;
|
private Consumer<Settings> saveCmd;
|
||||||
|
|
||||||
@@ -64,13 +71,15 @@ public class Settings {
|
|||||||
debugMode.addListener(this::somethingChanged);
|
debugMode.addListener(this::somethingChanged);
|
||||||
preferredVolumeImpl.addListener(this::somethingChanged);
|
preferredVolumeImpl.addListener(this::somethingChanged);
|
||||||
theme.addListener(this::somethingChanged);
|
theme.addListener(this::somethingChanged);
|
||||||
|
keychainBackend.addListener(this::somethingChanged);
|
||||||
userInterfaceOrientation.addListener(this::somethingChanged);
|
userInterfaceOrientation.addListener(this::somethingChanged);
|
||||||
|
licenseKey.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||||
this.saveCmd = saveCmd;
|
this.saveCmd = saveCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
|
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
@@ -94,7 +103,7 @@ public class Settings {
|
|||||||
public BooleanProperty checkForUpdates() {
|
public BooleanProperty checkForUpdates() {
|
||||||
return checkForUpdates;
|
return checkForUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty startHidden() {
|
public BooleanProperty startHidden() {
|
||||||
return startHidden;
|
return startHidden;
|
||||||
}
|
}
|
||||||
@@ -123,7 +132,13 @@ public class Settings {
|
|||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<KeychainBackend> keychainBackend() { return keychainBackend; }
|
||||||
|
|
||||||
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
|
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
|
||||||
return userInterfaceOrientation;
|
return userInterfaceOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StringProperty licenseKey() {
|
||||||
|
return licenseKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-36
@@ -9,10 +9,10 @@ import com.google.gson.TypeAdapter;
|
|||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonToken;
|
import com.google.gson.stream.JsonToken;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import javafx.geometry.NodeOrientation;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javafx.geometry.NodeOrientation;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,6 +38,8 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
|
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
|
||||||
out.name("theme").value(value.theme().get().name());
|
out.name("theme").value(value.theme().get().name());
|
||||||
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
|
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
|
||||||
|
out.name("keychainBackend").value(value.keychainBackend().get().name());
|
||||||
|
out.name("licenseKey").value(value.licenseKey().get());
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,43 +59,23 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
String name = in.nextName();
|
String name = in.nextName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "directories":
|
case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||||
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
|
||||||
break;
|
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
|
||||||
case "askedForUpdateCheck":
|
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
|
||||||
settings.askedForUpdateCheck().set(in.nextBoolean());
|
case "port" -> settings.port().set(in.nextInt());
|
||||||
break;
|
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
|
||||||
case "checkForUpdatesEnabled":
|
case "preferredGvfsScheme" -> settings.preferredGvfsScheme().set(parseWebDavUrlSchemePrefix(in.nextString()));
|
||||||
settings.checkForUpdates().set(in.nextBoolean());
|
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
|
||||||
break;
|
case "preferredVolumeImpl" -> settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
|
||||||
case "startHidden":
|
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
|
||||||
settings.startHidden().set(in.nextBoolean());
|
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
|
||||||
break;
|
case "keychainBackend" -> settings.keychainBackend().set(parseKeychainBackend(in.nextString()));
|
||||||
case "port":
|
case "licenseKey" -> settings.licenseKey().set(in.nextString());
|
||||||
settings.port().set(in.nextInt());
|
default -> {
|
||||||
break;
|
|
||||||
case "numTrayNotifications":
|
|
||||||
settings.numTrayNotifications().set(in.nextInt());
|
|
||||||
break;
|
|
||||||
case "preferredGvfsScheme":
|
|
||||||
settings.preferredGvfsScheme().set(parseWebDavUrlSchemePrefix(in.nextString()));
|
|
||||||
break;
|
|
||||||
case "debugMode":
|
|
||||||
settings.debugMode().set(in.nextBoolean());
|
|
||||||
break;
|
|
||||||
case "preferredVolumeImpl":
|
|
||||||
settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
|
|
||||||
break;
|
|
||||||
case "theme":
|
|
||||||
settings.theme().set(parseUiTheme(in.nextString()));
|
|
||||||
break;
|
|
||||||
case "uiOrientation":
|
|
||||||
settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||||
in.skipValue();
|
in.skipValue();
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
@@ -128,6 +110,15 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private KeychainBackend parseKeychainBackend(String backendName) {
|
||||||
|
try {
|
||||||
|
return KeychainBackend.valueOf(backendName.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.warn("Invalid keychain backend {}. Defaulting to {}.", backendName, Settings.DEFAULT_KEYCHAIN_BACKEND);
|
||||||
|
return Settings.DEFAULT_KEYCHAIN_BACKEND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private NodeOrientation parseUiOrientation(String uiOrientationName) {
|
private NodeOrientation parseUiOrientation(String uiOrientationName) {
|
||||||
try {
|
try {
|
||||||
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
|
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ package org.cryptomator.common.settings;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.LazyInitializer;
|
import org.cryptomator.common.LazyInitializer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -32,7 +34,6 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -46,16 +47,17 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
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;
|
||||||
|
|
||||||
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
||||||
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
||||||
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
|
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SettingsProvider(Environment env) {
|
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
this.scheduler = scheduler;
|
||||||
this.gson = new GsonBuilder() //
|
this.gson = new GsonBuilder() //
|
||||||
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
||||||
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
||||||
@@ -77,12 +79,15 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
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)) {
|
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||||
Settings settings = gson.fromJson(reader, Settings.class);
|
JsonElement json = JsonParser.parseReader(reader);
|
||||||
if (settings == null) {
|
if (json.isJsonObject()) {
|
||||||
throw new IOException("Unexpected EOF");
|
Settings settings = gson.fromJson(json, Settings.class);
|
||||||
|
LOG.info("Settings loaded from {}", path);
|
||||||
|
return Stream.of(settings);
|
||||||
|
} else {
|
||||||
|
LOG.warn("Invalid json file {}", path);
|
||||||
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
LOG.info("Settings loaded from {}", path);
|
|
||||||
return Stream.of(settings);
|
|
||||||
} catch (NoSuchFileException e) {
|
} catch (NoSuchFileException e) {
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -98,7 +103,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
||||||
settingsPath.ifPresent(path -> {
|
settingsPath.ifPresent(path -> {
|
||||||
Runnable saveCommand = () -> this.save(settings, path);
|
Runnable saveCommand = () -> this.save(settings, path);
|
||||||
ScheduledFuture<?> scheduledTask = saveScheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||||
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
||||||
if (previouslyScheduledTask != null) {
|
if (previouslyScheduledTask != null) {
|
||||||
previouslyScheduledTask.cancel(false);
|
previouslyScheduledTask.cancel(false);
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
public enum UiTheme {
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
LIGHT("Light"),
|
|
||||||
DARK("Dark");
|
|
||||||
// CUSTOM("Custom (%s)");
|
|
||||||
|
|
||||||
private String displayName;
|
public enum UiTheme {
|
||||||
|
LIGHT("preferences.general.theme.light"), //
|
||||||
|
DARK("preferences.general.theme.dark"), //
|
||||||
|
AUTOMATIC("preferences.general.theme.automatic");
|
||||||
|
|
||||||
|
public static UiTheme[] applicableValues() {
|
||||||
|
if (SystemUtils.IS_OS_MAC) {
|
||||||
|
return values();
|
||||||
|
} else {
|
||||||
|
return new UiTheme[]{LIGHT, DARK};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
UiTheme(String displayName) {
|
UiTheme(String displayName) {
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
|
|||||||
@@ -5,31 +5,30 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.google.common.base.CharMatcher;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
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.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;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The settings specific to a single vault.
|
* The settings specific to a single vault.
|
||||||
* TODO: Change the name of individualMountPath and its derivatives to customMountPath
|
|
||||||
*/
|
*/
|
||||||
public class VaultSettings {
|
public class VaultSettings {
|
||||||
|
|
||||||
@@ -38,34 +37,33 @@ public class VaultSettings {
|
|||||||
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
|
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
|
||||||
public static final boolean DEFAULT_USES_READONLY_MODE = false;
|
public static final boolean DEFAULT_USES_READONLY_MODE = false;
|
||||||
public static final String DEFAULT_MOUNT_FLAGS = "";
|
public static final String DEFAULT_MOUNT_FLAGS = "";
|
||||||
|
public static final int DEFAULT_FILENAME_LENGTH_LIMIT = -1;
|
||||||
private static final Random RNG = new Random();
|
public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
||||||
|
|
||||||
|
private static final Random RNG = new Random();
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final ObjectProperty<Path> path = new SimpleObjectProperty();
|
private final ObjectProperty<Path> path = new SimpleObjectProperty();
|
||||||
private final StringProperty mountName = new SimpleStringProperty();
|
private final StringProperty displayName = new SimpleStringProperty();
|
||||||
private final StringProperty winDriveLetter = new SimpleStringProperty();
|
private final StringProperty winDriveLetter = new SimpleStringProperty();
|
||||||
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
|
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
|
||||||
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
|
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REAVEAL_AFTER_MOUNT);
|
||||||
private final BooleanProperty usesIndividualMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
|
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
|
||||||
private final StringProperty individualMountPath = new SimpleStringProperty();
|
private final StringProperty customMountPath = new SimpleStringProperty();
|
||||||
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
|
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
|
||||||
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
|
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
|
||||||
|
private final IntegerProperty filenameLengthLimit = new SimpleIntegerProperty(DEFAULT_FILENAME_LENGTH_LIMIT);
|
||||||
|
private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
|
||||||
|
|
||||||
|
private final StringBinding mountName;
|
||||||
|
|
||||||
public VaultSettings(String id) {
|
public VaultSettings(String id) {
|
||||||
this.id = Objects.requireNonNull(id);
|
this.id = Objects.requireNonNull(id);
|
||||||
|
this.mountName = Bindings.createStringBinding(this::normalizeDisplayName, displayName);
|
||||||
EasyBind.subscribe(path, this::deriveMountNameFromPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, revealAfterMount, usesIndividualMountPath, individualMountPath, usesReadOnlyMode, mountFlags};
|
return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, filenameLengthLimit, actionAfterUnlock};
|
||||||
}
|
|
||||||
|
|
||||||
private void deriveMountNameFromPath(Path path) {
|
|
||||||
if (path != null && StringUtils.isBlank(mountName.get())) {
|
|
||||||
mountName.set(normalizeMountName(path.getFileName().toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VaultSettings withRandomId() {
|
public static VaultSettings withRandomId() {
|
||||||
@@ -78,23 +76,18 @@ public class VaultSettings {
|
|||||||
return BaseEncoding.base64Url().encode(randomBytes);
|
return BaseEncoding.base64Url().encode(randomBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String normalizeMountName(String mountName) {
|
//visible for testing
|
||||||
String normalizedMountName = StringUtils.stripAccents(mountName);
|
String normalizeDisplayName() {
|
||||||
StringBuilder builder = new StringBuilder();
|
var original = displayName.getValueSafe();
|
||||||
for (char c : normalizedMountName.toCharArray()) {
|
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
||||||
if (Character.isWhitespace(c)) {
|
return "_";
|
||||||
if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') {
|
|
||||||
builder.append('_');
|
|
||||||
}
|
|
||||||
} else if (c < 127 && Character.isLetterOrDigit(c)) {
|
|
||||||
builder.append(c);
|
|
||||||
} else {
|
|
||||||
if (builder.length() == 0 || builder.charAt(builder.length() - 1) != '_') {
|
|
||||||
builder.append('_');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return builder.toString();
|
|
||||||
|
// replace whitespaces (tabs, linebreaks, ...) by simple space (0x20)
|
||||||
|
var withoutFancyWhitespaces = CharMatcher.whitespace().collapseFrom(original, ' ');
|
||||||
|
|
||||||
|
// replace control chars as well as chars that aren't allowed in file names on standard file systems by underscore
|
||||||
|
return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
@@ -107,7 +100,11 @@ public class VaultSettings {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringProperty mountName() {
|
public StringProperty displayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBinding mountName() {
|
||||||
return mountName;
|
return mountName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +112,14 @@ public class VaultSettings {
|
|||||||
return winDriveLetter;
|
return winDriveLetter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getWinDriveLetter() {
|
||||||
|
String current = this.winDriveLetter.get();
|
||||||
|
if (!Strings.isNullOrEmpty(current)) {
|
||||||
|
return Optional.of(current);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
public BooleanProperty unlockAfterStartup() {
|
public BooleanProperty unlockAfterStartup() {
|
||||||
return unlockAfterStartup;
|
return unlockAfterStartup;
|
||||||
}
|
}
|
||||||
@@ -123,17 +128,17 @@ public class VaultSettings {
|
|||||||
return revealAfterMount;
|
return revealAfterMount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty usesIndividualMountPath() {
|
public BooleanProperty useCustomMountPath() {
|
||||||
return usesIndividualMountPath;
|
return useCustomMountPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringProperty individualMountPath() {
|
public StringProperty customMountPath() {
|
||||||
return individualMountPath;
|
return customMountPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getIndividualMountPath() {
|
public Optional<String> getCustomMountPath() {
|
||||||
if (usesIndividualMountPath.get()) {
|
if (useCustomMountPath.get()) {
|
||||||
return Optional.ofNullable(Strings.emptyToNull(individualMountPath.get()));
|
return Optional.ofNullable(Strings.emptyToNull(customMountPath.get()));
|
||||||
} else {
|
} else {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -147,6 +152,18 @@ public class VaultSettings {
|
|||||||
return mountFlags;
|
return mountFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntegerProperty filenameLengthLimit() {
|
||||||
|
return filenameLengthLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<WhenUnlocked> actionAfterUnlock() {
|
||||||
|
return actionAfterUnlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WhenUnlocked getActionAfterUnlock() {
|
||||||
|
return actionAfterUnlock.get();
|
||||||
|
}
|
||||||
|
|
||||||
/* Hashcode/Equals */
|
/* Hashcode/Equals */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+44
-41
@@ -21,82 +21,85 @@ class VaultSettingsJsonAdapter {
|
|||||||
out.beginObject();
|
out.beginObject();
|
||||||
out.name("id").value(value.getId());
|
out.name("id").value(value.getId());
|
||||||
out.name("path").value(value.path().get().toString());
|
out.name("path").value(value.path().get().toString());
|
||||||
out.name("mountName").value(value.mountName().get());
|
out.name("displayName").value(value.displayName().get());
|
||||||
out.name("winDriveLetter").value(value.winDriveLetter().get());
|
out.name("winDriveLetter").value(value.winDriveLetter().get());
|
||||||
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
|
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
|
||||||
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
||||||
out.name("usesIndividualMountPath").value(value.usesIndividualMountPath().get());
|
out.name("useCustomMountPath").value(value.useCustomMountPath().get());
|
||||||
out.name("individualMountPath").value(value.individualMountPath().get());
|
out.name("customMountPath").value(value.customMountPath().get());
|
||||||
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
|
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
|
||||||
out.name("mountFlags").value(value.mountFlags().get());
|
out.name("mountFlags").value(value.mountFlags().get());
|
||||||
|
out.name("filenameLengthLimit").value(value.filenameLengthLimit().get());
|
||||||
|
out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VaultSettings read(JsonReader in) throws IOException {
|
public VaultSettings read(JsonReader in) throws IOException {
|
||||||
String id = null;
|
String id = null;
|
||||||
String path = null;
|
String path = null;
|
||||||
String mountName = null;
|
String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
|
||||||
String individualMountPath = null;
|
String displayName = null;
|
||||||
|
String customMountPath = null;
|
||||||
String winDriveLetter = null;
|
String winDriveLetter = null;
|
||||||
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
||||||
boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
|
boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
|
||||||
boolean usesIndividualMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
|
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
|
||||||
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
||||||
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
|
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
|
||||||
|
int filenameLengthLimit = VaultSettings.DEFAULT_FILENAME_LENGTH_LIMIT;
|
||||||
|
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
|
||||||
|
|
||||||
in.beginObject();
|
in.beginObject();
|
||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
String name = in.nextName();
|
String name = in.nextName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "id":
|
case "id" -> id = in.nextString();
|
||||||
id = in.nextString();
|
case "path" -> path = in.nextString();
|
||||||
break;
|
case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
|
||||||
case "path":
|
case "displayName" -> displayName = in.nextString();
|
||||||
path = in.nextString();
|
case "winDriveLetter" -> winDriveLetter = in.nextString();
|
||||||
break;
|
case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
|
||||||
case "mountName":
|
case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
|
||||||
mountName = in.nextString();
|
case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
|
||||||
break;
|
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
|
||||||
case "winDriveLetter":
|
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
|
||||||
winDriveLetter = in.nextString();
|
case "mountFlags" -> mountFlags = in.nextString();
|
||||||
break;
|
case "filenameLengthLimit" -> filenameLengthLimit = in.nextInt();
|
||||||
case "unlockAfterStartup":
|
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
|
||||||
unlockAfterStartup = in.nextBoolean();
|
default -> {
|
||||||
break;
|
|
||||||
case "revealAfterMount":
|
|
||||||
revealAfterMount = in.nextBoolean();
|
|
||||||
break;
|
|
||||||
case "usesIndividualMountPath":
|
|
||||||
usesIndividualMountPath = in.nextBoolean();
|
|
||||||
break;
|
|
||||||
case "individualMountPath":
|
|
||||||
individualMountPath = in.nextString();
|
|
||||||
break;
|
|
||||||
case "usesReadOnlyMode":
|
|
||||||
usesReadOnlyMode = in.nextBoolean();
|
|
||||||
break;
|
|
||||||
case "mountFlags":
|
|
||||||
mountFlags = in.nextString();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||||
in.skipValue();
|
in.skipValue();
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
|
|
||||||
VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
|
VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
|
||||||
vaultSettings.mountName().set(mountName);
|
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.path().set(Paths.get(path));
|
||||||
vaultSettings.winDriveLetter().set(winDriveLetter);
|
vaultSettings.winDriveLetter().set(winDriveLetter);
|
||||||
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
|
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
|
||||||
vaultSettings.revealAfterMount().set(revealAfterMount);
|
vaultSettings.revealAfterMount().set(revealAfterMount);
|
||||||
vaultSettings.usesIndividualMountPath().set(usesIndividualMountPath);
|
vaultSettings.useCustomMountPath().set(useCustomMountPath);
|
||||||
vaultSettings.individualMountPath().set(individualMountPath);
|
vaultSettings.customMountPath().set(customMountPath);
|
||||||
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
|
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
|
||||||
vaultSettings.mountFlags().set(mountFlags);
|
vaultSettings.mountFlags().set(mountFlags);
|
||||||
|
vaultSettings.filenameLengthLimit().set(filenameLengthLimit);
|
||||||
|
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
|
||||||
return vaultSettings;
|
return vaultSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,7 +1,5 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public enum VolumeImpl {
|
public enum VolumeImpl {
|
||||||
WEBDAV("WebDAV"),
|
WEBDAV("WebDAV"),
|
||||||
FUSE("FUSE"),
|
FUSE("FUSE"),
|
||||||
@@ -17,18 +15,4 @@ public enum VolumeImpl {
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a VolumeImpl by display name.
|
|
||||||
*
|
|
||||||
* @param displayName Display name of the VolumeImpl
|
|
||||||
* @return VolumeImpl with the given <code>displayName</code>.
|
|
||||||
* @throws IllegalArgumentException if not volumeImpl with the given <code>displayName</code> was found.
|
|
||||||
*/
|
|
||||||
public static VolumeImpl forDisplayName(String displayName) throws IllegalArgumentException {
|
|
||||||
return Arrays.stream(values()) //
|
|
||||||
.filter(impl -> impl.displayName.equals(displayName)) //
|
|
||||||
.findAny() //
|
|
||||||
.orElseThrow(IllegalArgumentException::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public enum WebDavUrlScheme {
|
public enum WebDavUrlScheme {
|
||||||
DAV("dav", "dav:// (Gnome, Nautilus, ...)"),
|
DAV("dav", "dav:// (Gnome, Nautilus, ...)"),
|
||||||
WEBDAV("webdav", "webdav:// (KDE, Dolphin, ...)");
|
WEBDAV("webdav", "webdav:// (KDE, Dolphin, ...)");
|
||||||
@@ -9,7 +7,8 @@ public enum WebDavUrlScheme {
|
|||||||
private final String prefix;
|
private final String prefix;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
|
||||||
WebDavUrlScheme(String prefix, String displayName) {this.prefix = prefix;
|
WebDavUrlScheme(String prefix, String displayName) {
|
||||||
|
this.prefix = prefix;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,18 +19,4 @@ public enum WebDavUrlScheme {
|
|||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a WebDavUrlScheme by prefix.
|
|
||||||
*
|
|
||||||
* @param prefix Prefix of the WebDavUrlScheme
|
|
||||||
* @return WebDavUrlScheme with the given <code>prefix</code>.
|
|
||||||
* @throws IllegalArgumentException if not WebDavUrlScheme with the given <code>prefix</code> was found.
|
|
||||||
*/
|
|
||||||
public static WebDavUrlScheme forPrefix(String prefix) throws IllegalArgumentException {
|
|
||||||
return Arrays.stream(values()) //
|
|
||||||
.filter(impl -> impl.prefix.equals(prefix)) //
|
|
||||||
.findAny() //
|
|
||||||
.orElseThrow(IllegalArgumentException::new);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
public enum WhenUnlocked {
|
||||||
|
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
|
||||||
|
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
|
||||||
|
ASK("vaultOptions.general.actionAfterUnlock.ask");
|
||||||
|
|
||||||
|
private String displayName;
|
||||||
|
|
||||||
|
WhenUnlocked(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||||
|
import org.cryptomator.common.mountpoint.MountPointChooser;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public abstract class AbstractVolume implements Volume {
|
||||||
|
|
||||||
|
private final SortedSet<MountPointChooser> choosers;
|
||||||
|
|
||||||
|
protected Path mountPoint;
|
||||||
|
|
||||||
|
//Cleanup
|
||||||
|
private boolean cleanupRequired;
|
||||||
|
private MountPointChooser usedChooser;
|
||||||
|
|
||||||
|
public AbstractVolume(SortedSet<MountPointChooser> choosers) {
|
||||||
|
this.choosers = choosers;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path determineMountPoint() throws InvalidMountPointException {
|
||||||
|
SortedSet<MountPointChooser> checkedChoosers = new TreeSet<>(); //Natural order
|
||||||
|
for (MountPointChooser chooser : this.choosers) {
|
||||||
|
if (!chooser.isApplicable(this)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Path> chosenPath = chooser.chooseMountPoint(this);
|
||||||
|
checkedChoosers.add(chooser); //Consider a chooser checked if it's #chooseMountPoint() method was called
|
||||||
|
if (chosenPath.isEmpty()) {
|
||||||
|
//Chooser was applicable, but couldn't find a feasible mountpoint
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.cleanupRequired = chooser.prepare(this, chosenPath.get()); //Fail entirely if an Exception occurs
|
||||||
|
this.usedChooser = chooser;
|
||||||
|
return chosenPath.get();
|
||||||
|
}
|
||||||
|
//SortedSet#stream() should return a sorted stream (that's what it's docs and the docs of #spliterator() say, even if they are not 100% clear for me.)
|
||||||
|
//We want to keep that order, that's why we use ImmutableSet#toImmutableSet() to collect (even if it doesn't implement SortedSet, it's docs promise use encounter ordering.)
|
||||||
|
String checked = Joiner.on(", ").join(checkedChoosers.stream().map((mpc) -> mpc.getClass().getTypeName()).collect(ImmutableSet.toImmutableSet()));
|
||||||
|
throw new InvalidMountPointException(String.format("No feasible MountPoint found! Checked %s", checked.isBlank() ? "<No applicable MPC>" : checked));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cleanupMountPoint() {
|
||||||
|
if (this.cleanupRequired) {
|
||||||
|
this.usedChooser.cleanup(this, this.mountPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Path> getMountPoint() {
|
||||||
|
return Optional.ofNullable(mountPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,4 +10,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
@Documented
|
@Documented
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface DefaultMountFlags {
|
public @interface DefaultMountFlags {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||||
|
import org.cryptomator.common.mountpoint.MountPointChooser;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.frontend.dokany.Mount;
|
import org.cryptomator.frontend.dokany.Mount;
|
||||||
import org.cryptomator.frontend.dokany.MountFactory;
|
import org.cryptomator.frontend.dokany.MountFactory;
|
||||||
@@ -10,84 +12,47 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import javax.inject.Named;
|
||||||
import java.nio.file.DirectoryNotEmptyException;
|
import java.util.SortedSet;
|
||||||
import java.nio.file.DirectoryStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NotDirectoryException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
public class DokanyVolume implements Volume {
|
public class DokanyVolume extends AbstractVolume {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(DokanyVolume.class);
|
private static final Logger LOG = LoggerFactory.getLogger(DokanyVolume.class);
|
||||||
|
|
||||||
private static final String FS_TYPE_NAME = "Cryptomator File System";
|
private static final String FS_TYPE_NAME = "CryptomatorFS";
|
||||||
|
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
private final MountFactory mountFactory;
|
private final MountFactory mountFactory;
|
||||||
private final WindowsDriveLetters windowsDriveLetters;
|
|
||||||
private Mount mount;
|
private Mount mount;
|
||||||
private Path mountPoint;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, WindowsDriveLetters windowsDriveLetters) {
|
public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
|
||||||
|
super(choosers);
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.mountFactory = new MountFactory(executorService);
|
this.mountFactory = new MountFactory(executorService);
|
||||||
this.windowsDriveLetters = windowsDriveLetters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public VolumeImpl getImplementationType() {
|
||||||
return DokanyVolume.isSupportedStatic();
|
return VolumeImpl.DOKANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException, IOException {
|
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
|
||||||
this.mountPoint = determineMountPoint();
|
this.mountPoint = determineMountPoint();
|
||||||
String mountName = vaultSettings.mountName().get();
|
String mountName = vaultSettings.mountName().get();
|
||||||
try {
|
try {
|
||||||
this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, mountName, FS_TYPE_NAME, mountFlags.strip());
|
this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip());
|
||||||
} catch (MountFailedException e) {
|
} catch (MountFailedException e) {
|
||||||
if (vaultSettings.getIndividualMountPath().isPresent()) {
|
if (vaultSettings.getCustomMountPath().isPresent()) {
|
||||||
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
|
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
|
||||||
}
|
}
|
||||||
throw new VolumeException("Unable to mount Filesystem", e);
|
throw new VolumeException("Unable to mount Filesystem", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path determineMountPoint() throws VolumeException, IOException {
|
|
||||||
Optional<String> optionalCustomMountPoint = vaultSettings.getIndividualMountPath();
|
|
||||||
if (optionalCustomMountPoint.isPresent()) {
|
|
||||||
Path customMountPoint = Paths.get(optionalCustomMountPoint.get());
|
|
||||||
checkProvidedMountPoint(customMountPoint);
|
|
||||||
return customMountPoint;
|
|
||||||
} else if (!Strings.isNullOrEmpty(vaultSettings.winDriveLetter().get())) {
|
|
||||||
return Path.of(vaultSettings.winDriveLetter().get().charAt(0) + ":\\");
|
|
||||||
} else {
|
|
||||||
//auto assign drive letter
|
|
||||||
if (!windowsDriveLetters.getAvailableDriveLetters().isEmpty()) {
|
|
||||||
return Path.of(windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\");
|
|
||||||
} else {
|
|
||||||
//TODO: Error Handling
|
|
||||||
throw new VolumeException("No free drive letter available.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkProvidedMountPoint(Path mountPoint) throws IOException {
|
|
||||||
if (!Files.isDirectory(mountPoint)) {
|
|
||||||
throw new NotDirectoryException(mountPoint.toString());
|
|
||||||
}
|
|
||||||
try (DirectoryStream<Path> ds = Files.newDirectoryStream(mountPoint)) {
|
|
||||||
if (ds.iterator().hasNext()) {
|
|
||||||
throw new DirectoryNotEmptyException(mountPoint.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reveal() throws VolumeException {
|
public void reveal() throws VolumeException {
|
||||||
boolean success = mount.reveal();
|
boolean success = mount.reveal();
|
||||||
@@ -99,11 +64,17 @@ public class DokanyVolume implements Volume {
|
|||||||
@Override
|
@Override
|
||||||
public void unmount() {
|
public void unmount() {
|
||||||
mount.close();
|
mount.close();
|
||||||
|
cleanupMountPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Path> getMountPoint() {
|
public boolean isSupported() {
|
||||||
return Optional.ofNullable(mountPoint);
|
return DokanyVolume.isSupportedStatic();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MountPointRequirement getMountPointRequirement() {
|
||||||
|
return MountPointRequirement.EMPTY_MOUNT_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSupportedStatic() {
|
public static boolean isSupportedStatic() {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package org.cryptomator.common.vaults;
|
|||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.mountpoint.MountPointChooser;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.frontend.fuse.mount.CommandFailedException;
|
import org.cryptomator.frontend.fuse.mount.CommandFailedException;
|
||||||
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
||||||
@@ -15,95 +16,36 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import javax.inject.Named;
|
||||||
import java.nio.file.DirectoryNotEmptyException;
|
|
||||||
import java.nio.file.DirectoryStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NotDirectoryException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.util.SortedSet;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class FuseVolume implements Volume {
|
public class FuseVolume extends AbstractVolume {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
|
||||||
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
|
|
||||||
private static final boolean IS_MAC = System.getProperty("os.name").toLowerCase().contains("mac");
|
|
||||||
|
|
||||||
private final VaultSettings vaultSettings;
|
private Mount mount;
|
||||||
private final Environment environment;
|
|
||||||
|
|
||||||
private Mount fuseMnt;
|
|
||||||
private Path mountPoint;
|
|
||||||
private boolean createdTemporaryMountPoint;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FuseVolume(VaultSettings vaultSettings, Environment environment) {
|
public FuseVolume(@Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
|
||||||
this.vaultSettings = vaultSettings;
|
super(choosers);
|
||||||
this.environment = environment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mount(CryptoFileSystem fs, String mountFlags) throws IOException, FuseNotSupportedException, VolumeException {
|
public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
|
||||||
Optional<String> optionalCustomMountPoint = vaultSettings.getIndividualMountPath();
|
this.mountPoint = determineMountPoint();
|
||||||
if (optionalCustomMountPoint.isPresent()) {
|
|
||||||
Path customMountPoint = Paths.get(optionalCustomMountPoint.get());
|
|
||||||
checkProvidedMountPoint(customMountPoint);
|
|
||||||
this.mountPoint = customMountPoint;
|
|
||||||
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
|
|
||||||
} else {
|
|
||||||
this.mountPoint = prepareTemporaryMountPoint();
|
|
||||||
LOG.debug("Successfully created mount point: {}", mountPoint);
|
|
||||||
}
|
|
||||||
mount(fs.getPath("/"), mountFlags);
|
mount(fs.getPath("/"), mountFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkProvidedMountPoint(Path mountPoint) throws IOException {
|
|
||||||
if (!Files.isDirectory(mountPoint)) {
|
|
||||||
throw new NotDirectoryException(mountPoint.toString());
|
|
||||||
}
|
|
||||||
try (DirectoryStream<Path> ds = Files.newDirectoryStream(mountPoint)) {
|
|
||||||
if (ds.iterator().hasNext()) {
|
|
||||||
throw new DirectoryNotEmptyException(mountPoint.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path prepareTemporaryMountPoint() throws IOException, VolumeException {
|
|
||||||
Path mountPoint = chooseNonExistingTemporaryMountPoint();
|
|
||||||
// https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
|
|
||||||
// In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
|
|
||||||
// starting with version 3.5.0, FUSE will create non-existent mount points automatically.
|
|
||||||
if (IS_MAC && mountPoint.getParent().equals(Paths.get("/Volumes"))) {
|
|
||||||
return mountPoint;
|
|
||||||
} else {
|
|
||||||
Files.createDirectories(mountPoint);
|
|
||||||
this.createdTemporaryMountPoint = true;
|
|
||||||
return mountPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path chooseNonExistingTemporaryMountPoint() throws VolumeException {
|
|
||||||
Path parent = environment.getMountPointsDir().orElseThrow();
|
|
||||||
String basename = vaultSettings.getId();
|
|
||||||
for (int i = 0; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
|
|
||||||
Path mountPoint = parent.resolve(basename + "_" + i);
|
|
||||||
if (Files.notExists(mountPoint)) {
|
|
||||||
return mountPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG.error("Failed to find feasible mountpoint at {}/{}_x. Giving up after {} attempts.", parent, basename, MAX_TMPMOUNTPOINT_CREATION_RETRIES);
|
|
||||||
throw new VolumeException("Did not find feasible mount point.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mount(Path root, String mountFlags) throws VolumeException {
|
private void mount(Path root, String mountFlags) throws VolumeException {
|
||||||
try {
|
try {
|
||||||
Mounter mounter = FuseMountFactory.getMounter();
|
Mounter mounter = FuseMountFactory.getMounter();
|
||||||
EnvironmentVariables envVars = EnvironmentVariables.create() //
|
EnvironmentVariables envVars = EnvironmentVariables.create() //
|
||||||
.withFlags(splitFlags(mountFlags)).withMountPoint(mountPoint) //
|
.withFlags(splitFlags(mountFlags)).withMountPoint(mountPoint) //
|
||||||
.build();
|
.build();
|
||||||
this.fuseMnt = mounter.mount(root, envVars);
|
this.mount = mounter.mount(root, envVars);
|
||||||
} catch (CommandFailedException e) {
|
} catch (CommandFailedException | FuseNotSupportedException e) {
|
||||||
throw new VolumeException("Unable to mount Filesystem", e);
|
throw new VolumeException("Unable to mount Filesystem", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +57,7 @@ public class FuseVolume implements Volume {
|
|||||||
@Override
|
@Override
|
||||||
public void reveal() throws VolumeException {
|
public void reveal() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
fuseMnt.revealInFileManager();
|
mount.revealInFileManager();
|
||||||
} catch (CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
LOG.debug("Revealing the vault in file manger failed: " + e.getMessage());
|
LOG.debug("Revealing the vault in file manger failed: " + e.getMessage());
|
||||||
throw new VolumeException(e);
|
throw new VolumeException(e);
|
||||||
@@ -130,34 +72,23 @@ public class FuseVolume implements Volume {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void unmountForced() throws VolumeException {
|
public synchronized void unmountForced() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
fuseMnt.unmountForced();
|
mount.unmountForced();
|
||||||
fuseMnt.close();
|
mount.close();
|
||||||
} catch (CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
throw new VolumeException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
cleanupTemporaryMountPoint();
|
cleanupMountPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmount() throws VolumeException {
|
public synchronized void unmount() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
fuseMnt.unmount();
|
mount.unmount();
|
||||||
fuseMnt.close();
|
mount.close();
|
||||||
} catch (CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
throw new VolumeException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
cleanupTemporaryMountPoint();
|
cleanupMountPoint();
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanupTemporaryMountPoint() {
|
|
||||||
if (createdTemporaryMountPoint) {
|
|
||||||
try {
|
|
||||||
Files.delete(mountPoint);
|
|
||||||
LOG.debug("Successfully deleted mount point: {}", mountPoint);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.warn("Could not delete mount point: {}", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -166,12 +97,17 @@ public class FuseVolume implements Volume {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Path> getMountPoint() {
|
public VolumeImpl getImplementationType() {
|
||||||
return Optional.ofNullable(mountPoint);
|
return VolumeImpl.FUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MountPointRequirement getMountPointRequirement() {
|
||||||
|
return SystemUtils.IS_OS_WINDOWS ? MountPointRequirement.PARENT_NO_MOUNT_POINT : MountPointRequirement.EMPTY_MOUNT_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSupportedStatic() {
|
public static boolean isSupportedStatic() {
|
||||||
return (SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX) && FuseMountFactory.isFuseSupported();
|
return FuseMountFactory.isFuseSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration used to indicate the requirements for mounting a vault
|
||||||
|
* using a specific {@link Volume VolumeProvider}, e.g. {@link FuseVolume}.
|
||||||
|
*/
|
||||||
|
public enum MountPointRequirement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No Mountpoint on the local filesystem required. (e.g. WebDAV)
|
||||||
|
*/
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parent folder is required, but the actual Mountpoint must not exist.
|
||||||
|
*/
|
||||||
|
PARENT_NO_MOUNT_POINT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parent folder is required, but the actual Mountpoint may exist.
|
||||||
|
*/
|
||||||
|
PARENT_OPT_MOUNT_POINT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual Mountpoint must exist and must be empty.
|
||||||
|
*/
|
||||||
|
EMPTY_MOUNT_POINT;
|
||||||
|
}
|
||||||
@@ -8,6 +8,6 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
@Scope
|
@Scope
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@interface PerVault {
|
public @interface PerVault {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,50 +9,48 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.beans.binding.Binding;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.binding.ObjectBinding;
|
|
||||||
import javafx.beans.binding.StringBinding;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.LazyInitializer;
|
import org.cryptomator.common.LazyInitializer;
|
||||||
|
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.vaults.Volume.VolumeException;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
|
import org.cryptomator.cryptofs.common.Constants;
|
||||||
|
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||||
import org.cryptomator.cryptolib.api.CryptoException;
|
import org.cryptomator.cryptolib.api.CryptoException;
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
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.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.NotDirectoryException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||||
|
|
||||||
@PerVault
|
@PerVault
|
||||||
public class Vault {
|
public class Vault {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
|
||||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
|
||||||
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
|
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
|
||||||
|
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
@@ -60,32 +58,42 @@ public class Vault {
|
|||||||
private final StringBinding defaultMountFlags;
|
private final StringBinding defaultMountFlags;
|
||||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
||||||
private final ObjectProperty<VaultState> state;
|
private final ObjectProperty<VaultState> state;
|
||||||
|
private final ObjectProperty<Exception> lastKnownException;
|
||||||
private final VaultStats stats;
|
private final VaultStats stats;
|
||||||
private final StringBinding displayableName;
|
private final StringBinding displayName;
|
||||||
private final StringBinding displayablePath;
|
private final StringBinding displayablePath;
|
||||||
private final BooleanBinding locked;
|
private final BooleanBinding locked;
|
||||||
private final BooleanBinding processing;
|
private final BooleanBinding processing;
|
||||||
private final BooleanBinding unlocked;
|
private final BooleanBinding unlocked;
|
||||||
|
private final BooleanBinding missing;
|
||||||
private final BooleanBinding needsMigration;
|
private final BooleanBinding needsMigration;
|
||||||
private final ObjectBinding<Path> accessPoint;
|
private final BooleanBinding unknownError;
|
||||||
|
private final StringBinding accessPoint;
|
||||||
|
private final BooleanBinding accessPointPresent;
|
||||||
|
private final BooleanProperty showingStats;
|
||||||
|
|
||||||
private volatile Volume volume;
|
private volatile Volume volume;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, VaultStats stats) {
|
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.volumeProvider = volumeProvider;
|
this.volumeProvider = volumeProvider;
|
||||||
this.defaultMountFlags = defaultMountFlags;
|
this.defaultMountFlags = defaultMountFlags;
|
||||||
this.cryptoFileSystem = cryptoFileSystem;
|
this.cryptoFileSystem = cryptoFileSystem;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.lastKnownException = lastKnownException;
|
||||||
this.stats = stats;
|
this.stats = stats;
|
||||||
this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path());
|
this.displayName = Bindings.createStringBinding(this::getDisplayName, vaultSettings.displayName());
|
||||||
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);
|
||||||
|
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
|
||||||
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
||||||
this.accessPoint = Bindings.createObjectBinding(this::getAccessPoint, state);
|
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||||
|
this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state);
|
||||||
|
this.accessPointPresent = this.accessPoint.isNotEmpty();
|
||||||
|
this.showingStats = new SimpleBooleanProperty(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
@@ -101,24 +109,30 @@ public class Vault {
|
|||||||
if (vaultSettings.usesReadOnlyMode().get()) {
|
if (vaultSettings.usesReadOnlyMode().get()) {
|
||||||
flags.add(FileSystemFlags.READONLY);
|
flags.add(FileSystemFlags.READONLY);
|
||||||
}
|
}
|
||||||
|
if (vaultSettings.filenameLengthLimit().get() == -1) {
|
||||||
|
LOG.debug("Determining file name length limitations...");
|
||||||
|
int limit = new FileSystemCapabilityChecker().determineSupportedFileNameLength(getPath());
|
||||||
|
vaultSettings.filenameLengthLimit().set(limit);
|
||||||
|
LOG.info("Storing file name length limit of {}", limit);
|
||||||
|
}
|
||||||
|
assert vaultSettings.filenameLengthLimit().get() > 0;
|
||||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||||
.withPassphrase(passphrase) //
|
.withPassphrase(passphrase) //
|
||||||
.withFlags(flags) //
|
.withFlags(flags) //
|
||||||
.withMasterkeyFilename(MASTERKEY_FILENAME) //
|
.withMasterkeyFilename(MASTERKEY_FILENAME) //
|
||||||
|
.withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) //
|
||||||
|
.withMaxNameLength(vaultSettings.filenameLengthLimit().get()) //
|
||||||
.build();
|
.build();
|
||||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, Volume.VolumeException {
|
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
|
||||||
if (vaultSettings.usesIndividualMountPath().get() && Strings.isNullOrEmpty(vaultSettings.individualMountPath().get())) {
|
|
||||||
throw new NotDirectoryException("");
|
|
||||||
}
|
|
||||||
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
|
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
|
||||||
volume = volumeProvider.get();
|
volume = volumeProvider.get();
|
||||||
volume.mount(fs, getEffectiveMountFlags());
|
volume.mount(fs, getEffectiveMountFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void lock(boolean forced) throws Volume.VolumeException {
|
public synchronized void lock(boolean forced) throws VolumeException {
|
||||||
if (forced && volume.supportsForcedUnmount()) {
|
if (forced && volume.supportsForcedUnmount()) {
|
||||||
volume.unmountForced();
|
volume.unmountForced();
|
||||||
} else {
|
} else {
|
||||||
@@ -134,7 +148,7 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reveal() throws Volume.VolumeException {
|
public void reveal() throws VolumeException {
|
||||||
volume.reveal();
|
volume.reveal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +168,18 @@ public class Vault {
|
|||||||
state.setValue(value);
|
state.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<Exception> lastKnownExceptionProperty() {
|
||||||
|
return lastKnownException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exception getLastKnownException() {
|
||||||
|
return lastKnownException.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastKnownException(Exception e) {
|
||||||
|
lastKnownException.setValue(e);
|
||||||
|
}
|
||||||
|
|
||||||
public BooleanBinding lockedProperty() {
|
public BooleanBinding lockedProperty() {
|
||||||
return locked;
|
return locked;
|
||||||
}
|
}
|
||||||
@@ -177,37 +203,60 @@ public class Vault {
|
|||||||
public boolean isUnlocked() {
|
public boolean isUnlocked() {
|
||||||
return state.get() == VaultState.UNLOCKED;
|
return state.get() == VaultState.UNLOCKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanBinding missingProperty() {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMissing() {
|
||||||
|
return state.get() == VaultState.MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
public BooleanBinding needsMigrationProperty() {
|
public BooleanBinding needsMigrationProperty() {
|
||||||
return needsMigration;
|
return needsMigration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNeedsMigration() {
|
public boolean isNeedsMigration() {
|
||||||
return state.get() == VaultState.NEEDS_MIGRATION;
|
return state.get() == VaultState.NEEDS_MIGRATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringBinding displayableNameProperty() {
|
public BooleanBinding unknownErrorProperty() {
|
||||||
return displayableName;
|
return unknownError;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayableName() {
|
public boolean isUnknownError() {
|
||||||
Path p = vaultSettings.path().get();
|
return state.get() == VaultState.ERROR;
|
||||||
return p.getFileName().toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectBinding<Path> accessPointProperty() {
|
public StringBinding displayNameProperty() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return vaultSettings.displayName().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBinding accessPointProperty() {
|
||||||
return accessPoint;
|
return accessPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getAccessPoint() {
|
public String getAccessPoint() {
|
||||||
if (state.get() == VaultState.UNLOCKED) {
|
if (state.get() == VaultState.UNLOCKED) {
|
||||||
assert volume != null;
|
assert volume != null;
|
||||||
return volume.getMountPoint().orElse(Path.of(""));
|
return volume.getMountPoint().orElse(Path.of("")).toString();
|
||||||
} else {
|
} else {
|
||||||
return Path.of("");
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanBinding accessPointPresentProperty() {
|
||||||
|
return accessPointPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAccessPointPresent() {
|
||||||
|
return accessPointPresent.get();
|
||||||
|
}
|
||||||
|
|
||||||
public StringBinding displayablePathProperty() {
|
public StringBinding displayablePathProperty() {
|
||||||
return displayablePath;
|
return displayablePath;
|
||||||
}
|
}
|
||||||
@@ -223,6 +272,15 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanProperty showingStatsProperty() {
|
||||||
|
return showingStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShowingStats() {
|
||||||
|
return accessPointPresent.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
// Getter/Setter
|
// Getter/Setter
|
||||||
// *******************************************************************************/
|
// *******************************************************************************/
|
||||||
@@ -272,6 +330,10 @@ public class Vault {
|
|||||||
return vaultSettings.getId();
|
return vaultSettings.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Volume> getVolume() {
|
||||||
|
return Optional.ofNullable(this.volume);
|
||||||
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
// Hashcode / Equals
|
// Hashcode / Equals
|
||||||
// *******************************************************************************/
|
// *******************************************************************************/
|
||||||
|
|||||||
@@ -6,12 +6,15 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import dagger.BindsInstance;
|
import dagger.BindsInstance;
|
||||||
|
import dagger.Subcomponent;
|
||||||
|
import org.cryptomator.common.mountpoint.MountPointChooserModule;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
|
||||||
import dagger.Subcomponent;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
@PerVault
|
@PerVault
|
||||||
@Subcomponent(modules = {VaultModule.class})
|
@Subcomponent(modules = {VaultModule.class, MountPointChooserModule.class})
|
||||||
public interface VaultComponent {
|
public interface VaultComponent {
|
||||||
|
|
||||||
Vault vault();
|
Vault vault();
|
||||||
@@ -25,6 +28,9 @@ public interface VaultComponent {
|
|||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder initialVaultState(VaultState vaultState);
|
Builder initialVaultState(VaultState vaultState);
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||||
|
|
||||||
VaultComponent build();
|
VaultComponent build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ class VaultListChangeListener implements ListChangeListener<Vault> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(Change<? extends Vault> c) {
|
public void onChanged(Change<? extends Vault> c) {
|
||||||
while(c.next()) {
|
while (c.next()) {
|
||||||
if (c.wasAdded()) {
|
if (c.wasAdded()) {
|
||||||
List<VaultSettings> addedSettings = c.getAddedSubList().stream().map(Vault::getVaultSettings).collect(Collectors.toList());
|
List<VaultSettings> addedSettings = c.getAddedSubList().stream().map(Vault::getVaultSettings).collect(Collectors.toList());
|
||||||
vaultSettingsList.addAll(c.getFrom(), addedSettings);
|
vaultSettingsList.addAll(c.getFrom(), addedSettings);
|
||||||
|
|||||||
@@ -8,35 +8,42 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
import org.cryptomator.cryptofs.migration.Migrators;
|
import org.cryptomator.cryptofs.migration.Migrators;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class VaultListManager {
|
public class VaultListManager {
|
||||||
|
|
||||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
||||||
|
|
||||||
private final VaultComponent.Builder vaultComponentBuilder;
|
private final VaultComponent.Builder vaultComponentBuilder;
|
||||||
private final ObservableList<Vault> vaultList;
|
private final ObservableList<Vault> vaultList;
|
||||||
|
private final String defaultVaultName;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultListManager(VaultComponent.Builder vaultComponentBuilder, Settings settings) {
|
public VaultListManager(VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
|
||||||
this.vaultComponentBuilder = vaultComponentBuilder;
|
this.vaultComponentBuilder = vaultComponentBuilder;
|
||||||
|
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
||||||
this.vaultList = FXCollections.observableArrayList(Vault::observables);
|
this.vaultList = FXCollections.observableArrayList(Vault::observables);
|
||||||
|
|
||||||
addAll(settings.getDirectories());
|
addAll(settings.getDirectories());
|
||||||
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
|
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
|
||||||
}
|
}
|
||||||
@@ -46,47 +53,83 @@ public class VaultListManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Vault add(Path pathToVault) throws NoSuchFileException {
|
public Vault add(Path pathToVault) throws NoSuchFileException {
|
||||||
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
|
||||||
throw new NoSuchFileException(pathToVault.toString(), null, "Not a vault directory");
|
if (!CryptoFileSystemProvider.containsVault(normalizedPathToVault, MASTERKEY_FILENAME)) {
|
||||||
|
throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory");
|
||||||
}
|
}
|
||||||
Optional<Vault> alreadyExistingVault = get(pathToVault);
|
Optional<Vault> alreadyExistingVault = get(normalizedPathToVault);
|
||||||
if (alreadyExistingVault.isPresent()) {
|
if (alreadyExistingVault.isPresent()) {
|
||||||
return alreadyExistingVault.get();
|
return alreadyExistingVault.get();
|
||||||
} else {
|
} else {
|
||||||
VaultSettings vaultSettings = VaultSettings.withRandomId();
|
Vault newVault = create(newVaultSettings(normalizedPathToVault));
|
||||||
vaultSettings.path().set(pathToVault);
|
|
||||||
Vault newVault = create(vaultSettings);
|
|
||||||
vaultList.add(newVault);
|
vaultList.add(newVault);
|
||||||
return newVault;
|
return newVault;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VaultSettings newVaultSettings(Path path) {
|
||||||
|
VaultSettings vaultSettings = VaultSettings.withRandomId();
|
||||||
|
vaultSettings.path().set(path);
|
||||||
|
if (path.getFileName() != null) {
|
||||||
|
vaultSettings.displayName().set(path.getFileName().toString());
|
||||||
|
} else {
|
||||||
|
vaultSettings.displayName().set(defaultVaultName);
|
||||||
|
}
|
||||||
|
return vaultSettings;
|
||||||
|
}
|
||||||
|
|
||||||
private void addAll(Collection<VaultSettings> vaultSettings) {
|
private void addAll(Collection<VaultSettings> vaultSettings) {
|
||||||
Collection<Vault> vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList());
|
Collection<Vault> vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList());
|
||||||
vaultList.addAll(vaults);
|
vaultList.addAll(vaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Vault> get(Path vaultPath) {
|
private Optional<Vault> get(Path vaultPath) {
|
||||||
return vaultList.stream().filter(v -> v.getPath().equals(vaultPath)).findAny();
|
assert vaultPath.isAbsolute();
|
||||||
|
assert vaultPath.normalize().equals(vaultPath);
|
||||||
|
return vaultList.stream() //
|
||||||
|
.filter(v -> vaultPath.equals(v.getPath())) //
|
||||||
|
.findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vault create(VaultSettings vaultSettings) {
|
private Vault create(VaultSettings vaultSettings) {
|
||||||
VaultState vaultState = determineVaultState(vaultSettings.path().get());
|
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
|
||||||
VaultComponent comp = vaultComponentBuilder.vaultSettings(vaultSettings).initialVaultState(vaultState).build();
|
try {
|
||||||
return comp.vault();
|
VaultState vaultState = determineVaultState(vaultSettings.path().get());
|
||||||
|
compBuilder.initialVaultState(vaultState);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
|
||||||
|
compBuilder.initialVaultState(VaultState.ERROR);
|
||||||
|
compBuilder.initialErrorCause(e);
|
||||||
|
}
|
||||||
|
return compBuilder.build().vault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private VaultState determineVaultState(Path pathToVault) {
|
public static VaultState redetermineVaultState(Vault vault) {
|
||||||
try {
|
VaultState previousState = vault.getState();
|
||||||
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
return switch (previousState) {
|
||||||
return VaultState.MISSING;
|
case LOCKED, NEEDS_MIGRATION, MISSING -> {
|
||||||
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
|
try {
|
||||||
return VaultState.NEEDS_MIGRATION;
|
VaultState determinedState = determineVaultState(vault.getPath());
|
||||||
} else {
|
vault.setState(determinedState);
|
||||||
return VaultState.LOCKED;
|
yield determinedState;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
|
||||||
|
vault.setState(VaultState.ERROR);
|
||||||
|
vault.setLastKnownException(e);
|
||||||
|
yield VaultState.ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
case ERROR, UNLOCKED, PROCESSING -> previousState;
|
||||||
return VaultState.ERROR;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VaultState determineVaultState(Path pathToVault) throws IOException {
|
||||||
|
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
||||||
|
return VaultState.MISSING;
|
||||||
|
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
|
||||||
|
return VaultState.NEEDS_MIGRATION;
|
||||||
|
} else {
|
||||||
|
return VaultState.LOCKED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,6 @@ package org.cryptomator.common.vaults;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.StringBinding;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
@@ -23,6 +15,14 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -46,6 +46,14 @@ public class VaultModule {
|
|||||||
return new SimpleObjectProperty<>(initialState);
|
return new SimpleObjectProperty<>(initialState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("lastKnownException")
|
||||||
|
@PerVault
|
||||||
|
public ObjectProperty<Exception> provideLastKnownException(@Named("lastKnownException") @Nullable Exception initialErrorCause) {
|
||||||
|
return new SimpleObjectProperty<>(initialErrorCause);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
|
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
|
||||||
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
||||||
@@ -67,7 +75,7 @@ public class VaultModule {
|
|||||||
@DefaultMountFlags
|
@DefaultMountFlags
|
||||||
public StringBinding provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
public StringBinding provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
||||||
ObjectProperty<VolumeImpl> preferredVolumeImpl = settings.preferredVolumeImpl();
|
ObjectProperty<VolumeImpl> preferredVolumeImpl = settings.preferredVolumeImpl();
|
||||||
StringProperty mountName = vaultSettings.mountName();
|
StringBinding mountName = vaultSettings.mountName();
|
||||||
BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
|
BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
|
||||||
|
|
||||||
return Bindings.createStringBinding(() -> {
|
return Bindings.createStringBinding(() -> {
|
||||||
@@ -76,6 +84,8 @@ public class VaultModule {
|
|||||||
return getMacFuseDefaultMountFlags(mountName, readOnly);
|
return getMacFuseDefaultMountFlags(mountName, readOnly);
|
||||||
} else if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_LINUX) {
|
} else if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_LINUX) {
|
||||||
return getLinuxFuseDefaultMountFlags(readOnly);
|
return getLinuxFuseDefaultMountFlags(readOnly);
|
||||||
|
} else if (v == VolumeImpl.FUSE && SystemUtils.IS_OS_WINDOWS) {
|
||||||
|
return getWindowsFuseDefaultMountFlags(mountName, readOnly);
|
||||||
} else if (v == VolumeImpl.DOKANY && SystemUtils.IS_OS_WINDOWS) {
|
} else if (v == VolumeImpl.DOKANY && SystemUtils.IS_OS_WINDOWS) {
|
||||||
return getDokanyDefaultMountFlags(readOnly);
|
return getDokanyDefaultMountFlags(readOnly);
|
||||||
} else {
|
} else {
|
||||||
@@ -85,7 +95,7 @@ public class VaultModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
|
// see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
|
||||||
private String getMacFuseDefaultMountFlags(ReadOnlyStringProperty mountName, ReadOnlyBooleanProperty readOnly) {
|
private String getMacFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
|
||||||
assert SystemUtils.IS_OS_MAC_OSX;
|
assert SystemUtils.IS_OS_MAC_OSX;
|
||||||
StringBuilder flags = new StringBuilder();
|
StringBuilder flags = new StringBuilder();
|
||||||
if (readOnly.get()) {
|
if (readOnly.get()) {
|
||||||
@@ -134,6 +144,28 @@ public class VaultModule {
|
|||||||
return flags.toString().strip();
|
return flags.toString().strip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse_main.c#L53-L62 for syntax guide
|
||||||
|
// see https://github.com/billziss-gh/winfsp/blob/5d0b10d0b643652c00ebb4704dc2bb28e7244973/src/dll/fuse/fuse.c#L295-L319 for options (-o <...>)
|
||||||
|
// see https://github.com/billziss-gh/winfsp/wiki/Frequently-Asked-Questions/5ba00e4be4f5e938eaae6ef1500b331de12dee77 (FUSE 4.) on why the given defaults were choosen
|
||||||
|
private String getWindowsFuseDefaultMountFlags(StringBinding mountName, ReadOnlyBooleanProperty readOnly) {
|
||||||
|
assert SystemUtils.IS_OS_WINDOWS;
|
||||||
|
StringBuilder flags = new StringBuilder();
|
||||||
|
|
||||||
|
//WinFSP has no explicit "readonly"-option, nut not setting the group/user-id has the same effect, tho.
|
||||||
|
//So for the time being not setting them is the way to go...
|
||||||
|
//See: https://github.com/billziss-gh/winfsp/issues/319
|
||||||
|
if (!readOnly.get()) {
|
||||||
|
flags.append(" -ouid=-1");
|
||||||
|
flags.append(" -ogid=-1");
|
||||||
|
}
|
||||||
|
flags.append(" -ovolname=").append(mountName.get());
|
||||||
|
//Dokany requires this option to be set, WinFSP doesn't seem to share this peculiarity,
|
||||||
|
//but the option exists. Let's keep this here in case we need it.
|
||||||
|
// flags.append(" -oThreadCount=").append(5);
|
||||||
|
|
||||||
|
return flags.toString().strip();
|
||||||
|
}
|
||||||
|
|
||||||
// see https://github.com/cryptomator/dokany-nio-adapter/blob/develop/src/main/java/org/cryptomator/frontend/dokany/MountUtil.java#L30-L34
|
// see https://github.com/cryptomator/dokany-nio-adapter/blob/develop/src/main/java/org/cryptomator/frontend/dokany/MountUtil.java#L30-L34
|
||||||
private String getDokanyDefaultMountFlags(ReadOnlyBooleanProperty readOnly) {
|
private String getDokanyDefaultMountFlags(ReadOnlyBooleanProperty readOnly) {
|
||||||
assert SystemUtils.IS_OS_WINDOWS;
|
assert SystemUtils.IS_OS_WINDOWS;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.cryptomator.common.vaults;
|
|||||||
|
|
||||||
public enum VaultState {
|
public enum VaultState {
|
||||||
/**
|
/**
|
||||||
* No vault found at the provided path
|
* No vault found at the provided path
|
||||||
*/
|
*/
|
||||||
MISSING,
|
MISSING,
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.beans.property.LongProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleLongProperty;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemStats;
|
import org.cryptomator.cryptofs.CryptoFileSystemStats;
|
||||||
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 javafx.application.Platform;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.LongProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
import javafx.concurrent.ScheduledService;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.util.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
@@ -28,6 +30,15 @@ public class VaultStats {
|
|||||||
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
|
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
|
||||||
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
|
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
|
||||||
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
|
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
|
||||||
|
private final LongProperty bytesPerSecondEncrypted = new SimpleLongProperty();
|
||||||
|
private final LongProperty bytesPerSecondDecrypted = new SimpleLongProperty();
|
||||||
|
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
|
||||||
|
private final LongProperty toalBytesRead = new SimpleLongProperty();
|
||||||
|
private final LongProperty toalBytesWritten = new SimpleLongProperty();
|
||||||
|
private final LongProperty totalBytesEncrypted = new SimpleLongProperty();
|
||||||
|
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
|
||||||
|
private final LongProperty filesRead = new SimpleLongProperty();
|
||||||
|
private final LongProperty filesWritten = new SimpleLongProperty();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
|
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
|
||||||
@@ -41,27 +52,48 @@ public class VaultStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
|
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
switch (state.get()) {
|
if (VaultState.UNLOCKED.equals(state.get())) {
|
||||||
case UNLOCKED:
|
assert fs.get() != null;
|
||||||
assert fs.get() != null;
|
LOG.debug("start recording stats");
|
||||||
LOG.debug("start recording stats");
|
updateService.restart();
|
||||||
updateService.restart();
|
} else {
|
||||||
break;
|
LOG.debug("stop recording stats");
|
||||||
default:
|
updateService.cancel();
|
||||||
LOG.debug("stop recording stats");
|
|
||||||
updateService.cancel();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStats(Optional<CryptoFileSystemStats> stats) {
|
private void updateStats(Optional<CryptoFileSystemStats> stats) {
|
||||||
assert Platform.isFxApplicationThread();
|
assert Platform.isFxApplicationThread();
|
||||||
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0l));
|
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0L));
|
||||||
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0l));
|
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0L));
|
||||||
|
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
|
||||||
|
bytesPerSecondDecrypted.set(stats.map(CryptoFileSystemStats::pollBytesDecrypted).orElse(0L));
|
||||||
|
bytesPerSecondEncrypted.set(stats.map(CryptoFileSystemStats::pollBytesEncrypted).orElse(0L));
|
||||||
|
toalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
|
||||||
|
toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
|
||||||
|
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
|
||||||
|
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
|
||||||
|
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
|
||||||
|
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getCacheHitRate(CryptoFileSystemStats stats) {
|
||||||
|
long accesses = stats.pollChunkCacheAccesses();
|
||||||
|
long hits = stats.pollChunkCacheHits();
|
||||||
|
if (accesses == 0) {
|
||||||
|
return 0.0;
|
||||||
|
} else {
|
||||||
|
return hits / (double) accesses;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateStatsService extends ScheduledService<Optional<CryptoFileSystemStats>> {
|
private class UpdateStatsService extends ScheduledService<Optional<CryptoFileSystemStats>> {
|
||||||
|
|
||||||
|
private UpdateStatsService() {
|
||||||
|
setOnFailed(event -> LOG.error("Error in UpdateStateService.", getException()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Task<Optional<CryptoFileSystemStats>> createTask() {
|
protected Task<Optional<CryptoFileSystemStats>> createTask() {
|
||||||
return new Task<>() {
|
return new Task<>() {
|
||||||
@@ -97,4 +129,50 @@ public class VaultStats {
|
|||||||
public long getBytesPerSecondWritten() {
|
public long getBytesPerSecondWritten() {
|
||||||
return bytesPerSecondWritten.get();
|
return bytesPerSecondWritten.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LongProperty bytesPerSecondEncryptedProperty() {
|
||||||
|
return bytesPerSecondEncrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytesPerSecondEnrypted() {
|
||||||
|
return bytesPerSecondEncrypted.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongProperty bytesPerSecondDecryptedProperty() {
|
||||||
|
return bytesPerSecondDecrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytesPerSecondDecrypted() {
|
||||||
|
return bytesPerSecondDecrypted.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleProperty cacheHitRateProperty() { return cacheHitRate; }
|
||||||
|
|
||||||
|
public double getCacheHitRate() {
|
||||||
|
return cacheHitRate.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongProperty toalBytesReadProperty() {return toalBytesRead;}
|
||||||
|
|
||||||
|
public long getTotalBytesRead() { return toalBytesRead.get();}
|
||||||
|
|
||||||
|
public LongProperty toalBytesWrittenProperty() {return toalBytesWritten;}
|
||||||
|
|
||||||
|
public long getTotalBytesWritten() { return toalBytesWritten.get();}
|
||||||
|
|
||||||
|
public LongProperty totalBytesEncryptedProperty() {return totalBytesEncrypted;}
|
||||||
|
|
||||||
|
public long getTotalBytesEncrypted() { return totalBytesEncrypted.get();}
|
||||||
|
|
||||||
|
public LongProperty totalBytesDecryptedProperty() {return totalBytesDecrypted;}
|
||||||
|
|
||||||
|
public long getTotalBytesDecrypted() { return totalBytesDecrypted.get();}
|
||||||
|
|
||||||
|
public LongProperty filesRead() { return filesRead;}
|
||||||
|
|
||||||
|
public long getFilesRead() { return filesRead.get();}
|
||||||
|
|
||||||
|
public LongProperty filesWritten() {return filesWritten;}
|
||||||
|
|
||||||
|
public long getFilesWritten() {return filesWritten.get();}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import org.cryptomator.common.mountpoint.InvalidMountPointException;
|
||||||
import org.cryptomator.common.settings.VolumeImpl;
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
|
|
||||||
@@ -20,11 +21,18 @@ public interface Volume {
|
|||||||
*/
|
*/
|
||||||
boolean isSupported();
|
boolean isSupported();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the coresponding enum type of the {@link VolumeImpl volume implementation ("VolumeImpl")} that is implemented by this Volume.
|
||||||
|
*
|
||||||
|
* @return the type of implementation as defined by the {@link VolumeImpl VolumeImpl enum}
|
||||||
|
*/
|
||||||
|
VolumeImpl getImplementationType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fs
|
* @param fs
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException;
|
void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException, InvalidMountPointException;
|
||||||
|
|
||||||
void reveal() throws VolumeException;
|
void reveal() throws VolumeException;
|
||||||
|
|
||||||
@@ -32,6 +40,8 @@ public interface Volume {
|
|||||||
|
|
||||||
Optional<Path> getMountPoint();
|
Optional<Path> getMountPoint();
|
||||||
|
|
||||||
|
MountPointRequirement getMountPointRequirement();
|
||||||
|
|
||||||
// optional forced unmounting:
|
// optional forced unmounting:
|
||||||
|
|
||||||
default boolean supportsForcedUnmount() {
|
default boolean supportsForcedUnmount() {
|
||||||
@@ -43,17 +53,10 @@ public interface Volume {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VolumeImpl[] getCurrentSupportedAdapters() {
|
static VolumeImpl[] getCurrentSupportedAdapters() {
|
||||||
return Stream.of(VolumeImpl.values()).filter(impl -> {
|
return Stream.of(VolumeImpl.values()).filter(impl -> switch (impl) {
|
||||||
switch (impl) {
|
case WEBDAV -> WebDavVolume.isSupportedStatic();
|
||||||
case WEBDAV:
|
case DOKANY -> DokanyVolume.isSupportedStatic();
|
||||||
return WebDavVolume.isSupportedStatic();
|
case FUSE -> FuseVolume.isSupportedStatic();
|
||||||
case DOKANY:
|
|
||||||
return DokanyVolume.isSupportedStatic();
|
|
||||||
case FUSE:
|
|
||||||
return FuseVolume.isSupportedStatic();
|
|
||||||
default:
|
|
||||||
return false;//throw new IllegalStateException("Adapter not implemented.");
|
|
||||||
}
|
|
||||||
}).toArray(VolumeImpl[]::new);
|
}).toArray(VolumeImpl[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.common.base.CharMatcher;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||||
import org.cryptomator.frontend.webdav.mount.MountParams;
|
import org.cryptomator.frontend.webdav.mount.MountParams;
|
||||||
@@ -44,7 +46,9 @@ public class WebDavVolume implements Volume {
|
|||||||
if (!server.isRunning()) {
|
if (!server.isRunning()) {
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
|
CharMatcher acceptable = CharMatcher.inRange('0', '9').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('a', 'z'));
|
||||||
|
String urlConformMountName = acceptable.negate().collapseFrom(vaultSettings.mountName().get(), '_');
|
||||||
|
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + urlConformMountName);
|
||||||
servlet.start();
|
servlet.start();
|
||||||
mount();
|
mount();
|
||||||
}
|
}
|
||||||
@@ -98,7 +102,12 @@ public class WebDavVolume implements Volume {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Path> getMountPoint() {
|
public Optional<Path> getMountPoint() {
|
||||||
return Optional.ofNullable(mountPoint);
|
return Optional.ofNullable(mountPoint); //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MountPointRequirement getMountPointRequirement() {
|
||||||
|
return MountPointRequirement.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLocalhostAliasOrNull() {
|
private String getLocalhostAliasOrNull() {
|
||||||
@@ -126,6 +135,11 @@ public class WebDavVolume implements Volume {
|
|||||||
return WebDavVolume.isSupportedStatic();
|
return WebDavVolume.isSupportedStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VolumeImpl getImplementationType() {
|
||||||
|
return VolumeImpl.WEBDAV;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsForcedUnmount() {
|
public boolean supportsForcedUnmount() {
|
||||||
return mount != null && mount.forced().isPresent();
|
return mount != null && mount.forced().isPresent();
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
@@ -22,12 +22,11 @@ import java.util.stream.StreamSupport;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public final class WindowsDriveLetters {
|
public final class WindowsDriveLetters {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(WindowsDriveLetters.class);
|
private static final Set<String> C_TO_Z;
|
||||||
private static final Set<String> A_TO_Z;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try (IntStream stream = IntStream.rangeClosed('A', 'Z')) {
|
try (IntStream stream = IntStream.rangeClosed('C', 'Z')) {
|
||||||
A_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(Collectors.toSet());
|
C_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(ImmutableSet.toImmutableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,21 +35,31 @@ public final class WindowsDriveLetters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAllDriveLetters() {
|
public Set<String> getAllDriveLetters() {
|
||||||
return A_TO_Z;
|
return C_TO_Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getOccupiedDriveLetters() {
|
public Set<String> getOccupiedDriveLetters() {
|
||||||
if (!SystemUtils.IS_OS_WINDOWS) {
|
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||||
LOG.warn("Attempted to get occupied drive letters on non-Windows machine.");
|
|
||||||
return Set.of();
|
return Set.of();
|
||||||
} else {
|
} else {
|
||||||
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
||||||
return StreamSupport.stream(rootDirs.spliterator(), false).map(p -> p.toString().substring(0,1)).collect(Collectors.toSet());
|
return StreamSupport.stream(rootDirs.spliterator(), false).map(p -> p.toString().substring(0, 1)).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAvailableDriveLetters() {
|
public Set<String> getAvailableDriveLetters() {
|
||||||
return Sets.difference(A_TO_Z, getOccupiedDriveLetters());
|
return Sets.difference(getAllDriveLetters(), getOccupiedDriveLetters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getAvailableDriveLetter() {
|
||||||
|
return getAvailableDriveLetters().stream().findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Path> getAvailableDriveLetterPath() {
|
||||||
|
return getAvailableDriveLetter().map(this::toPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path toPath(String driveLetter) {
|
||||||
|
return Path.of(driveLetter + ":\\");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class EnvironmentTest {
|
|||||||
|
|
||||||
List<Path> result = env.getSettingsPath().collect(Collectors.toList());
|
List<Path> result = env.getSettingsPath().collect(Collectors.toList());
|
||||||
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
||||||
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/settings.json"),
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/settings.json"), //
|
||||||
Paths.get("/home/testuser/.Cryptomator/settings.json")));
|
Paths.get("/home/testuser/.Cryptomator/settings.json")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ class EnvironmentTest {
|
|||||||
|
|
||||||
List<Path> result = env.getIpcPortPath().collect(Collectors.toList());
|
List<Path> result = env.getIpcPortPath().collect(Collectors.toList());
|
||||||
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
||||||
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/ipcPort.bin"),
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/ipcPort.bin"), //
|
||||||
Paths.get("/home/testuser/.Cryptomator/ipcPort.bin")));
|
Paths.get("/home/testuser/.Cryptomator/ipcPort.bin")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +123,8 @@ class EnvironmentTest {
|
|||||||
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
||||||
|
|
||||||
MatcherAssert.assertThat(result, Matchers.hasSize(3));
|
MatcherAssert.assertThat(result, Matchers.hasSize(3));
|
||||||
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/test"),
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/test"), //
|
||||||
Paths.get("/home/testuser/test2"),
|
Paths.get("/home/testuser/test2"), //
|
||||||
Paths.get("/foo/bar/test")));
|
Paths.get("/foo/bar/test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class LicenseCheckerTest {
|
||||||
|
|
||||||
|
private static final String PUBLIC_KEY = "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ" //
|
||||||
|
+ "PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47" //
|
||||||
|
+ "6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM" //
|
||||||
|
+ "Al8G7CqwoJOsW7Kddns=";
|
||||||
|
|
||||||
|
private LicenseChecker licenseChecker;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
licenseChecker = new LicenseChecker(PUBLIC_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckValidLicense() {
|
||||||
|
String license = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6InhaRGZacHJ5NFA5dlpQWnlHMmZOQlJqLTdMejVvbVZkbTd0SG9DZ1NOZlkifQ.eyJzdWIiOiJjcnlwdG9ib3RAZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.AQaBIKQdNCxmRJi2wLOcbagTgi39WhdWwgdpKTYSPicg-aPr_tst_RjmnqMemx3cBe0Blr4nEbj_lAtSKHz_i61fAUyI1xCIAZYbK9Q3ICHIHQl3AiuCpBwFl-k81OB4QDYiKpEc9gLN5dhW_VymJMsgOvyiC0UjC91f2AM7s46byDNj";
|
||||||
|
|
||||||
|
Optional<DecodedJWT> decoded = licenseChecker.check(license);
|
||||||
|
|
||||||
|
Assertions.assertTrue(decoded.isPresent());
|
||||||
|
Assertions.assertEquals("cryptobot@example.com", decoded.get().getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckInvalidLicenseHeader() {
|
||||||
|
String license = "EyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6InhaRGZacHJ5NFA5dlpQWnlHMmZOQlJqLTdMejVvbVZkbTd0SG9DZ1NOZlkifQ.eyJzdWIiOiJjcnlwdG9ib3RAZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.AQaBIKQdNCxmRJi2wLOcbagTgi39WhdWwgdpKTYSPicg-aPr_tst_RjmnqMemx3cBe0Blr4nEbj_lAtSKHz_i61fAUyI1xCIAZYbK9Q3ICHIHQl3AiuCpBwFl-k81OB4QDYiKpEc9gLN5dhW_VymJMsgOvyiC0UjC91f2AM7s46byDNj";
|
||||||
|
|
||||||
|
Optional<DecodedJWT> decoded = licenseChecker.check(license);
|
||||||
|
|
||||||
|
Assertions.assertFalse(decoded.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckInvalidLicensePayload() {
|
||||||
|
String license = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6InhaRGZacHJ5NFA5dlpQWnlHMmZOQlJqLTdMejVvbVZkbTd0SG9DZ1NOZlkifQ.EyJzdWIiOiJjcnlwdG9ib3RAZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.AQaBIKQdNCxmRJi2wLOcbagTgi39WhdWwgdpKTYSPicg-aPr_tst_RjmnqMemx3cBe0Blr4nEbj_lAtSKHz_i61fAUyI1xCIAZYbK9Q3ICHIHQl3AiuCpBwFl-k81OB4QDYiKpEc9gLN5dhW_VymJMsgOvyiC0UjC91f2AM7s46byDNj";
|
||||||
|
|
||||||
|
Optional<DecodedJWT> decoded = licenseChecker.check(license);
|
||||||
|
|
||||||
|
Assertions.assertFalse(decoded.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckInvalidLicenseSignature() {
|
||||||
|
String license = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6InhaRGZacHJ5NFA5dlpQWnlHMmZOQlJqLTdMejVvbVZkbTd0SG9DZ1NOZlkifQ.eyJzdWIiOiJjcnlwdG9ib3RAZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.aQaBIKQdNCxmRJi2wLOcbagTgi39WhdWwgdpKTYSPicg-aPr_tst_RjmnqMemx3cBe0Blr4nEbj_lAtSKHz_i61fAUyI1xCIAZYbK9Q3ICHIHQl3AiuCpBwFl-k81OB4QDYiKpEc9gLN5dhW_VymJMsgOvyiC0UjC91f2AM7s46byDNj";
|
||||||
|
|
||||||
|
Optional<DecodedJWT> decoded = licenseChecker.check(license);
|
||||||
|
|
||||||
|
Assertions.assertFalse(decoded.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
|
||||||
|
public class KeychainManagerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStoreAndLoad() throws KeychainAccessException {
|
||||||
|
KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess()));
|
||||||
|
keychainManager.storePassphrase("test", "asd");
|
||||||
|
Assertions.assertArrayEquals("asd".toCharArray(), keychainManager.loadPassphrase("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
public static class WhenObservingProperties {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void startup() throws InterruptedException {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Platform.startup(latch::countDown);
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException {
|
||||||
|
KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess()));
|
||||||
|
ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test");
|
||||||
|
Assertions.assertEquals(false, property.get());
|
||||||
|
|
||||||
|
keychainManager.storePassphrase("test", "bar");
|
||||||
|
|
||||||
|
AtomicBoolean result = new AtomicBoolean(false);
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
result.set(property.get());
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
latch.await(1, TimeUnit.SECONDS);
|
||||||
|
Assertions.assertEquals(true, result.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
-2
@@ -3,12 +3,14 @@
|
|||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the accompanying LICENSE file.
|
* are made available under the terms of the accompanying LICENSE file.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.keychain;
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
class MapKeychainAccess implements KeychainAccessStrategy {
|
class MapKeychainAccess implements KeychainAccessProvider {
|
||||||
|
|
||||||
private final Map<String, char[]> map = new HashMap<>();
|
private final Map<String, char[]> map = new HashMap<>();
|
||||||
|
|
||||||
@@ -31,6 +33,12 @@ class MapKeychainAccess implements KeychainAccessStrategy {
|
|||||||
map.remove(key);
|
map.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changePassphrase(String key, CharSequence passphrase) {
|
||||||
|
map.get(key);
|
||||||
|
storePassphrase(key, passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return true;
|
return true;
|
||||||
+14
@@ -7,6 +7,8 @@ package org.cryptomator.common.settings;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -34,4 +36,16 @@ public class SettingsJsonAdapterTest {
|
|||||||
Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get());
|
Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "fromJson() should throw IOException for input: {0}")
|
||||||
|
@ValueSource(strings = { //
|
||||||
|
"", //
|
||||||
|
"<html>", //
|
||||||
|
"{invalidjson}" //
|
||||||
|
})
|
||||||
|
public void testDeserializeMalformed(String input) {
|
||||||
|
Assertions.assertThrows(IOException.class, () -> {
|
||||||
|
adapter.fromJson(input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class SettingsTest {
|
|||||||
Mockito.verify(changeListener, Mockito.times(2)).accept(settings);
|
Mockito.verify(changeListener, Mockito.times(2)).accept(settings);
|
||||||
|
|
||||||
// third change (to property of list item):
|
// third change (to property of list item):
|
||||||
vaultSettings.mountName().set("asd");
|
vaultSettings.displayName().set("asd");
|
||||||
Mockito.verify(changeListener, Mockito.times(3)).accept(settings);
|
Mockito.verify(changeListener, Mockito.times(3)).accept(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -16,7 +16,6 @@ import java.io.IOException;
|
|||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class VaultSettingsJsonAdapterTest {
|
public class VaultSettingsJsonAdapterTest {
|
||||||
|
|
||||||
@@ -24,15 +23,15 @@ public class VaultSettingsJsonAdapterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeserialize() throws IOException {
|
public void testDeserialize() throws IOException {
|
||||||
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\", \"mountFlags\":\"--foo --bar\"}";
|
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"displayName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\", \"mountFlags\":\"--foo --bar\"}";
|
||||||
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
||||||
|
|
||||||
VaultSettings vaultSettings = adapter.read(jsonReader);
|
VaultSettings vaultSettings = adapter.read(jsonReader);
|
||||||
Assertions.assertEquals("foo", vaultSettings.getId());
|
Assertions.assertEquals("foo", vaultSettings.getId());
|
||||||
Assertions.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
|
Assertions.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
|
||||||
Assertions.assertEquals("test", vaultSettings.mountName().get());
|
Assertions.assertEquals("test", vaultSettings.displayName().get());
|
||||||
Assertions.assertEquals("X", vaultSettings.winDriveLetter().get());
|
Assertions.assertEquals("X", vaultSettings.winDriveLetter().get());
|
||||||
Assertions.assertEquals("/home/test/crypto", vaultSettings.individualMountPath().get());
|
Assertions.assertEquals("/home/test/crypto", vaultSettings.customMountPath().get());
|
||||||
Assertions.assertEquals("--foo --bar", vaultSettings.mountFlags().get());
|
Assertions.assertEquals("--foo --bar", vaultSettings.mountFlags().get());
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ public class VaultSettingsJsonAdapterTest {
|
|||||||
public void testSerialize() throws IOException {
|
public void testSerialize() throws IOException {
|
||||||
VaultSettings vaultSettings = new VaultSettings("test");
|
VaultSettings vaultSettings = new VaultSettings("test");
|
||||||
vaultSettings.path().set(Paths.get("/foo/bar"));
|
vaultSettings.path().set(Paths.get("/foo/bar"));
|
||||||
vaultSettings.mountName().set("mountyMcMountFace");
|
vaultSettings.displayName().set("mountyMcMountFace");
|
||||||
vaultSettings.mountFlags().set("--foo --bar");
|
vaultSettings.mountFlags().set("--foo --bar");
|
||||||
|
|
||||||
StringWriter buf = new StringWriter();
|
StringWriter buf = new StringWriter();
|
||||||
@@ -51,12 +50,12 @@ public class VaultSettingsJsonAdapterTest {
|
|||||||
String result = buf.toString();
|
String result = buf.toString();
|
||||||
|
|
||||||
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"id\":\"test\""));
|
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"id\":\"test\""));
|
||||||
if(System.getProperty("os.name").contains("Windows")){
|
if (System.getProperty("os.name").contains("Windows")) {
|
||||||
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"path\":\"\\\\foo\\\\bar\""));
|
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"path\":\"\\\\foo\\\\bar\""));
|
||||||
} else {
|
} else {
|
||||||
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"path\":\"/foo/bar\""));
|
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"path\":\"/foo/bar\""));
|
||||||
}
|
}
|
||||||
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"mountName\":\"mountyMcMountFace\""));
|
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"displayName\":\"mountyMcMountFace\""));
|
||||||
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"mountFlags\":\"--foo --bar\""));
|
MatcherAssert.assertThat(result, CoreMatchers.containsString("\"mountFlags\":\"--foo --bar\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,19 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class VaultSettingsTest {
|
public class VaultSettingsTest {
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testNormalize() throws Exception {
|
@CsvSource({"a\u000Fa,a_a", ": \\,_ _", "汉语,汉语", "..,_", "a\ta,a\u0020a", "\t\n\r,_"})
|
||||||
assertEquals("_", VaultSettings.normalizeMountName(" "));
|
public void testNormalize(String test, String expected) {
|
||||||
assertEquals("a", VaultSettings.normalizeMountName("ä"));
|
VaultSettings settings = new VaultSettings("id");
|
||||||
assertEquals("C", VaultSettings.normalizeMountName("Ĉ"));
|
settings.displayName().setValue(test);
|
||||||
assertEquals("_", VaultSettings.normalizeMountName(":"));
|
assertEquals(expected, settings.normalizeDisplayName());
|
||||||
assertEquals("_", VaultSettings.normalizeMountName("汉语"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import javafx.beans.binding.StringBinding;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.common.settings.VolumeImpl;
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
@@ -17,6 +13,11 @@ import org.junit.jupiter.api.condition.OS;
|
|||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class VaultModuleTest {
|
public class VaultModuleTest {
|
||||||
@@ -28,8 +29,9 @@ public class VaultModuleTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup(@TempDir Path tmpDir) {
|
public void setup(@TempDir Path tmpDir) {
|
||||||
Mockito.when(vaultSettings.mountName()).thenReturn(new SimpleStringProperty("TEST"));
|
Mockito.when(vaultSettings.mountName()).thenReturn(Bindings.createStringBinding(() -> "TEST"));
|
||||||
Mockito.when(vaultSettings.usesReadOnlyMode()).thenReturn(new SimpleBooleanProperty(true));
|
Mockito.when(vaultSettings.usesReadOnlyMode()).thenReturn(new SimpleBooleanProperty(true));
|
||||||
|
Mockito.when(vaultSettings.displayName()).thenReturn(new SimpleStringProperty("Vault"));
|
||||||
System.setProperty("user.home", tmpDir.toString());
|
System.setProperty("user.home", tmpDir.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>main</artifactId>
|
|
||||||
<version>1.5.0-beta1</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>keychain</artifactId>
|
|
||||||
<name>System Keychain Access</name>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>commons</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-lang3</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Google -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- DI -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.dagger</groupId>
|
|
||||||
<artifactId>dagger</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- secret-service lib -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>de.swiesend</groupId>
|
|
||||||
<artifactId>secret-service</artifactId>
|
|
||||||
<version>1.0.0-RC.3</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Logging -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
@@ -1,31 +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.keychain;
|
|
||||||
|
|
||||||
public interface KeychainAccess {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associates a passphrase with a given key.
|
|
||||||
*
|
|
||||||
* @param key Key used to retrieve the passphrase via {@link #loadPassphrase(String)}.
|
|
||||||
* @param passphrase The secret to store in this keychain.
|
|
||||||
*/
|
|
||||||
void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}.
|
|
||||||
* @return The stored passphrase for the given key or <code>null</code> if no value for the given key could be found.
|
|
||||||
*/
|
|
||||||
char[] loadPassphrase(String key) throws KeychainAccessException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a passphrase with a given key.
|
|
||||||
*
|
|
||||||
* @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}.
|
|
||||||
*/
|
|
||||||
void deletePassphrase(String key) throws KeychainAccessException;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.cryptomator.keychain;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates an error during communication with the operating system's keychain.
|
|
||||||
*/
|
|
||||||
public class KeychainAccessException extends Exception {
|
|
||||||
|
|
||||||
KeychainAccessException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,17 +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.keychain;
|
|
||||||
|
|
||||||
interface KeychainAccessStrategy extends KeychainAccess {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return <code>true</code> if this KeychainAccessStrategy works on the current machine.
|
|
||||||
* @implNote This method must not throw any exceptions and should fail fast
|
|
||||||
* returning <code>false</code> if it can't determine availability of the checked strategy
|
|
||||||
*/
|
|
||||||
boolean isSupported();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +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.keychain;
|
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import dagger.multibindings.ElementsIntoSet;
|
|
||||||
import org.cryptomator.common.JniModule;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Module(includes = {JniModule.class})
|
|
||||||
public class KeychainModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@ElementsIntoSet
|
|
||||||
Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain, LinuxSecretServiceKeychainAccess linKeychain) {
|
|
||||||
return Sets.newHashSet(macKeychain, winKeychain, linKeychain);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
public Optional<KeychainAccess> provideSupportedKeychain(Set<KeychainAccessStrategy> keychainAccessStrategies) {
|
|
||||||
return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).map(KeychainAccess.class::cast).findFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
package org.cryptomator.keychain;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A facade to LinuxSecretServiceKeychainAccessImpl that doesn't depend on libraries that are unavailable on Mac and Windows.
|
|
||||||
*/
|
|
||||||
public class LinuxSecretServiceKeychainAccess implements KeychainAccessStrategy {
|
|
||||||
|
|
||||||
// the actual implementation is hidden in this delegate object which is loaded via reflection,
|
|
||||||
// as it depends on libraries that aren't necessarily available:
|
|
||||||
private final Optional<KeychainAccessStrategy> delegate;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public LinuxSecretServiceKeychainAccess() {
|
|
||||||
this.delegate = constructGnomeKeyringKeychainAccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Optional<KeychainAccessStrategy> constructGnomeKeyringKeychainAccess() {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl");
|
|
||||||
KeychainAccessStrategy instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
|
|
||||||
return Optional.of(instance);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSupported() {
|
|
||||||
return SystemUtils.IS_OS_LINUX && delegate.map(KeychainAccessStrategy::isSupported).orElse(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
|
||||||
delegate.orElseThrow(IllegalStateException::new).storePassphrase(key, passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] loadPassphrase(String key) throws KeychainAccessException {
|
|
||||||
return delegate.orElseThrow(IllegalStateException::new).loadPassphrase(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deletePassphrase(String key) throws KeychainAccessException {
|
|
||||||
delegate.orElseThrow(IllegalStateException::new).deletePassphrase(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-65
@@ -1,65 +0,0 @@
|
|||||||
package org.cryptomator.keychain;
|
|
||||||
|
|
||||||
import org.freedesktop.secret.simple.SimpleCollection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
class LinuxSecretServiceKeychainAccessImpl implements KeychainAccessStrategy {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSupported() {
|
|
||||||
try (@SuppressWarnings("unused") SimpleCollection keyring = new SimpleCollection()) {
|
|
||||||
// seems like we're able to access the keyring.
|
|
||||||
return true;
|
|
||||||
} catch (IOException | RuntimeException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
|
|
||||||
try (SimpleCollection keyring = new SimpleCollection()) {
|
|
||||||
List<String> list = keyring.getItems(createAttributes(key));
|
|
||||||
if (list == null) {
|
|
||||||
keyring.createItem("Cryptomator", passphrase, createAttributes(key));
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KeychainAccessException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] loadPassphrase(String key) throws KeychainAccessException {
|
|
||||||
try (SimpleCollection keyring = new SimpleCollection()) {
|
|
||||||
List<String> list = keyring.getItems(createAttributes(key));
|
|
||||||
if (list != null) {
|
|
||||||
return keyring.getSecret(list.get(0));
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KeychainAccessException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deletePassphrase(String key) throws KeychainAccessException {
|
|
||||||
try (SimpleCollection keyring = new SimpleCollection()) {
|
|
||||||
List<String> list = keyring.getItems(createAttributes(key));
|
|
||||||
if (list != null) {
|
|
||||||
keyring.deleteItem(list.get(0));
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KeychainAccessException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> createAttributes(String key) {
|
|
||||||
Map<String, String> attributes = new HashMap();
|
|
||||||
attributes.put("Vault", key);
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +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.keychain;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.jni.MacFunctions;
|
|
||||||
import org.cryptomator.jni.MacKeychainAccess;
|
|
||||||
|
|
||||||
class MacSystemKeychainAccess implements KeychainAccessStrategy {
|
|
||||||
|
|
||||||
private final Optional<MacFunctions> macFunctions;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public MacSystemKeychainAccess(Optional<MacFunctions> macFunctions) {
|
|
||||||
this.macFunctions = macFunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MacKeychainAccess keychain() {
|
|
||||||
return macFunctions.orElseThrow(IllegalStateException::new).keychainAccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePassphrase(String key, CharSequence passphrase) {
|
|
||||||
keychain().storePassword(key, passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char[] loadPassphrase(String key) {
|
|
||||||
return keychain().loadPassword(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSupported() {
|
|
||||||
return SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deletePassphrase(String key) {
|
|
||||||
keychain().deletePassword(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user