mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-25 06:01:31 +00:00
Compare commits
366 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0037c12326 | ||
|
|
5908249325 | ||
|
|
debdf92cfc | ||
|
|
5944086510 | ||
|
|
a551663127 | ||
|
|
1cc0b0728d | ||
|
|
7e7d5e46d3 | ||
|
|
5625525b21 | ||
|
|
b8fa226163 | ||
|
|
2b44f0ee9f | ||
|
|
b1ea21809b | ||
|
|
8def0b2060 | ||
|
|
2f61964758 | ||
|
|
2668299131 | ||
|
|
a7de849800 | ||
|
|
c66a8d0cfe | ||
|
|
efa7f78ffd | ||
|
|
57c858351d | ||
|
|
c3e48934b2 | ||
|
|
06b8c7cdf4 | ||
|
|
cbf677a51c | ||
|
|
923e58ba18 | ||
|
|
65c12d7ae1 | ||
|
|
8e324ef0eb | ||
|
|
29a0336bf4 | ||
|
|
44fc6761e3 | ||
|
|
a4ef082bc4 | ||
|
|
1272279b96 | ||
|
|
6aafa7bb5c | ||
|
|
43f2110f68 | ||
|
|
0b8f8e53af | ||
|
|
336d67195d | ||
|
|
6677079623 | ||
|
|
0974a57671 | ||
|
|
ab77673fed | ||
|
|
a70401596f | ||
|
|
ab198271a1 | ||
|
|
d62edcda73 | ||
|
|
8cba58075d | ||
|
|
426f36ce04 | ||
|
|
dd3c969f0f | ||
|
|
6a270ceccd | ||
|
|
a3474e05eb | ||
|
|
dd190b5a16 | ||
|
|
fe722629be | ||
|
|
f8e5d8aefb | ||
|
|
3e3a4ceefc | ||
|
|
a09edad165 | ||
|
|
129e9c63f8 | ||
|
|
9af58b8e6e | ||
|
|
1048ff5728 | ||
|
|
6adb591c9a | ||
|
|
d06720838e | ||
|
|
cf020e5b96 | ||
|
|
4bfd1e6433 | ||
|
|
deded33da8 | ||
|
|
be5fce0ee9 | ||
|
|
39f9da16f9 | ||
|
|
53b0d5cb9f | ||
|
|
b9a120b51b | ||
|
|
debcab47e2 | ||
|
|
8814372c68 | ||
|
|
98e5c3ff88 | ||
|
|
f1c332f455 | ||
|
|
79306ea498 | ||
|
|
d7dda7d249 | ||
|
|
1e80f4bba4 | ||
|
|
ffd3981f36 | ||
|
|
aa23635744 | ||
|
|
0317e7c21d | ||
|
|
ec5e8bba30 | ||
|
|
0caa9988d3 | ||
|
|
f16c3d5110 | ||
|
|
e1930505d1 | ||
|
|
757549919c | ||
|
|
0257802bb0 | ||
|
|
5cb4b403cd | ||
|
|
8831df9242 | ||
|
|
2229a56831 | ||
|
|
c3370a8388 | ||
|
|
1175a114ec | ||
|
|
3374dbf9a5 | ||
|
|
26aee9e42c | ||
|
|
ab82874013 | ||
|
|
39f1da105e | ||
|
|
207bfee6e5 | ||
|
|
d08f3d03d0 | ||
|
|
a88bd81347 | ||
|
|
8e2d2b899e | ||
|
|
3b4870a98a | ||
|
|
ef5eabdb79 | ||
|
|
f52b2f323a | ||
|
|
7e60e5606c | ||
|
|
8e2fa082cc | ||
|
|
d8ef402607 | ||
|
|
748f1be0c5 | ||
|
|
608d54a8f2 | ||
|
|
50b167e28f | ||
|
|
b7d06783dd | ||
|
|
5ef3d23970 | ||
|
|
200a195f3b | ||
|
|
db0aceefdf | ||
|
|
492e986608 | ||
|
|
98cab7e4d8 | ||
|
|
384c9de7aa | ||
|
|
528005a623 | ||
|
|
c78d4c2d0e | ||
|
|
c8e9201692 | ||
|
|
536da2621a | ||
|
|
6167eeecb4 | ||
|
|
9cc873a344 | ||
|
|
b539590d7a | ||
|
|
5209bef1a9 | ||
|
|
ee5505362c | ||
|
|
82f388d420 | ||
|
|
70e733f341 | ||
|
|
3af5d5f267 | ||
|
|
28bf9e2ab1 | ||
|
|
2f3a576de9 | ||
|
|
bbe1ef3dbc | ||
|
|
078a127182 | ||
|
|
240bf122dd | ||
|
|
f2f8f9b28c | ||
|
|
af03e0d73d | ||
|
|
7844078203 | ||
|
|
da62a22faf | ||
|
|
c36a1a4aef | ||
|
|
86d8599d07 | ||
|
|
f760347d9d | ||
|
|
8fc647eb2b | ||
|
|
69b1bf5a26 | ||
|
|
6951edac96 | ||
|
|
76c84b34e9 | ||
|
|
1362720011 | ||
|
|
3f15352ebf | ||
|
|
fdedee49d3 | ||
|
|
b506493c13 | ||
|
|
bc09f28120 | ||
|
|
eba0935900 | ||
|
|
744225cf7a | ||
|
|
54f2667e45 | ||
|
|
050a6e6a57 | ||
|
|
dbcb407a7c | ||
|
|
b1acdf9e61 | ||
|
|
9c6a0b3e0c | ||
|
|
f6df442948 | ||
|
|
29038a679f | ||
|
|
2e042423d0 | ||
|
|
e08c2b7dc3 | ||
|
|
d5d0589dec | ||
|
|
a760edcab9 | ||
|
|
a759455473 | ||
|
|
50d2f16f71 | ||
|
|
8afa09007b | ||
|
|
fccd02a7e8 | ||
|
|
7e46957bcb | ||
|
|
194f373c08 | ||
|
|
5f11e1aa8b | ||
|
|
0ac1b406b7 | ||
|
|
ade2db7cc0 | ||
|
|
eb911aeb6c | ||
|
|
a58259d4f0 | ||
|
|
963a731202 | ||
|
|
5ea0fc4b6f | ||
|
|
4becdf7e1e | ||
|
|
8ab8798381 | ||
|
|
c3f8fa1662 | ||
|
|
59d019f34a | ||
|
|
ee99e9994e | ||
|
|
019b7ac643 | ||
|
|
7392a99da5 | ||
|
|
e909269ae0 | ||
|
|
45ad496f41 | ||
|
|
a272bf2614 | ||
|
|
119e0724d5 | ||
|
|
76a9cb9a06 | ||
|
|
acb8d4cd0c | ||
|
|
ffe8887114 | ||
|
|
d8c8c2380a | ||
|
|
fe5ce79802 | ||
|
|
bf7a8686a6 | ||
|
|
143070d02d | ||
|
|
06827a7466 | ||
|
|
5add862ce8 | ||
|
|
2b8d7c6c3b | ||
|
|
f5da13d3b4 | ||
|
|
03dfd3e887 | ||
|
|
8241559362 | ||
|
|
c289040624 | ||
|
|
767acbd922 | ||
|
|
ccd4cedb08 | ||
|
|
0ddd6d767d | ||
|
|
6a5a1e5bae | ||
|
|
b3d76bb394 | ||
|
|
1924a7dec9 | ||
|
|
b65da30899 | ||
|
|
7de8b3da02 | ||
|
|
48ac8da1a7 | ||
|
|
86ae35c7eb | ||
|
|
8421a8fc7b | ||
|
|
b579e03bc8 | ||
|
|
9217b11e61 | ||
|
|
e16bd7373c | ||
|
|
ef53561bf0 | ||
|
|
3165c4ba86 | ||
|
|
f1bf157cac | ||
|
|
9c75dd48dd | ||
|
|
74d4b4ea47 | ||
|
|
6f66f4cbf1 | ||
|
|
874c5506a7 | ||
|
|
aed35c17c8 | ||
|
|
873e438759 | ||
|
|
5b45893c7b | ||
|
|
5515258af1 | ||
|
|
dd5d52d25a | ||
|
|
24236f3844 | ||
|
|
458866f7d6 | ||
|
|
525b0a7982 | ||
|
|
d53af61b58 | ||
|
|
b0ab46b7b6 | ||
|
|
9107d296c3 | ||
|
|
6be95963a1 | ||
|
|
09c9361e94 | ||
|
|
5e7cea216d | ||
|
|
6d91992102 | ||
|
|
5a23ee0be6 | ||
|
|
31e186dd15 | ||
|
|
c7beb4a93c | ||
|
|
6aff94a8f8 | ||
|
|
273f6907c9 | ||
|
|
95df963913 | ||
|
|
13c5e2470b | ||
|
|
2341440ed9 | ||
|
|
558f4be945 | ||
|
|
d63735df1a | ||
|
|
8be9cbbab7 | ||
|
|
191d7561f0 | ||
|
|
49a6fe9571 | ||
|
|
8b85fa5928 | ||
|
|
2695979ae2 | ||
|
|
f2212169be | ||
|
|
5898a4d584 | ||
|
|
42b5b2663e | ||
|
|
a0a867474b | ||
|
|
c98f7f8574 | ||
|
|
fae2e19b96 | ||
|
|
45548a0023 | ||
|
|
8edd5326fa | ||
|
|
1d178674ce | ||
|
|
7593d5cd40 | ||
|
|
9e8f88f000 | ||
|
|
e1eafbc838 | ||
|
|
f2107954b3 | ||
|
|
db3dabe3c5 | ||
|
|
76f69dec34 | ||
|
|
1b130ab58e | ||
|
|
a12e2ae6cd | ||
|
|
7441f67b03 | ||
|
|
d02d521856 | ||
|
|
e6dd14d0e5 | ||
|
|
e257d8e497 | ||
|
|
2d6d3084b0 | ||
|
|
4fffc63869 | ||
|
|
ce457ae20e | ||
|
|
5b9efcc16f | ||
|
|
28001710b3 | ||
|
|
32e81796ae | ||
|
|
68445befeb | ||
|
|
da31a9d2a0 | ||
|
|
9b3167c886 | ||
|
|
7c1a0b5fdf | ||
|
|
a666c80ef5 | ||
|
|
2610ef4645 | ||
|
|
357f659c8d | ||
|
|
19225c058d | ||
|
|
f1ee991d07 | ||
|
|
562daf990f | ||
|
|
19cd0d70e7 | ||
|
|
d38b488ee0 | ||
|
|
84d6f11be2 | ||
|
|
b3cf7eee16 | ||
|
|
9e5e14e462 | ||
|
|
b165f41c6b | ||
|
|
41358e6715 | ||
|
|
c8387c7e3c | ||
|
|
8ace293701 | ||
|
|
f5a08f05c1 | ||
|
|
9707d951d6 | ||
|
|
242b1e92a3 | ||
|
|
3bc7df9e83 | ||
|
|
f774829fb1 | ||
|
|
bfd8918e6a | ||
|
|
3989316269 | ||
|
|
d0d83c6833 | ||
|
|
c957f93ce6 | ||
|
|
b069a16f0c | ||
|
|
bf637f8361 | ||
|
|
a2f6a85334 | ||
|
|
8a359704ca | ||
|
|
e3b9601721 | ||
|
|
d170e87c1b | ||
|
|
39d1d9c561 | ||
|
|
2914af5f7b | ||
|
|
fa10a92fa4 | ||
|
|
1e7478a89f | ||
|
|
8ec2b10a4d | ||
|
|
2001e768d4 | ||
|
|
79fd17250b | ||
|
|
b8ee19b395 | ||
|
|
08cfcffa72 | ||
|
|
9e82739c16 | ||
|
|
6534242275 | ||
|
|
cc40d05e44 | ||
|
|
5fcbe7eff1 | ||
|
|
4be842aff5 | ||
|
|
e808db51a5 | ||
|
|
8e1bb121bb | ||
|
|
355c1934f9 | ||
|
|
b47ced1173 | ||
|
|
a66e08ee9b | ||
|
|
ad27094d63 | ||
|
|
0be97b309b | ||
|
|
b8de68bf3b | ||
|
|
f9dba09003 | ||
|
|
a428e62891 | ||
|
|
424ea8429c | ||
|
|
a52adc1871 | ||
|
|
08d5335b74 | ||
|
|
8741c64b86 | ||
|
|
b64f7cc7a8 | ||
|
|
f84760746f | ||
|
|
296848b41e | ||
|
|
51e6ee5be5 | ||
|
|
9d5609b129 | ||
|
|
e18d62d57f | ||
|
|
9ff710ddf5 | ||
|
|
02f3f5ad53 | ||
|
|
40caba847b | ||
|
|
467e242df4 | ||
|
|
a7c42c3d59 | ||
|
|
ecdd3bc5ec | ||
|
|
46cab2ef9a | ||
|
|
a7b036d441 | ||
|
|
17bbd9161f | ||
|
|
a850e8b816 | ||
|
|
95b8240a21 | ||
|
|
69d379d03b | ||
|
|
1b9951e90e | ||
|
|
e62e9a1dff | ||
|
|
6d1989bbfe | ||
|
|
c41973a8d6 | ||
|
|
f84bb4710f | ||
|
|
d1a9233557 | ||
|
|
1a73f84d83 | ||
|
|
36cf3c2283 | ||
|
|
091fab90ee | ||
|
|
d81bf2e301 | ||
|
|
cd3cf3da0e | ||
|
|
3e961dabdd | ||
|
|
dde7d263b9 | ||
|
|
ff373b9a3f | ||
|
|
0a7d102981 | ||
|
|
10867e87c9 | ||
|
|
7900f0ea8c | ||
|
|
f0d364a6c5 | ||
|
|
5ed50c50c7 |
15
CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
15
CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
@@ -4,20 +4,9 @@
|
|||||||
|
|
||||||
- Ensure you're running the latest version of Cryptomator.
|
- Ensure you're running the latest version of Cryptomator.
|
||||||
- Ensure the bug is related to the desktop version of Cryptomator. Bugs concerning the Cryptomator iOS and Android app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/cryptomator-ios/issues) and [Cryptomator for Android issues list](https://github.com/cryptomator/cryptomator-android/issues) respectively.
|
- Ensure the bug is related to the desktop version of Cryptomator. Bugs concerning the Cryptomator iOS and Android app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/cryptomator-ios/issues) and [Cryptomator for Android issues list](https://github.com/cryptomator/cryptomator-android/issues) respectively.
|
||||||
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [knowledge base](https://cryptomator.freshdesk.com/support/solutions) and our [Wiki](https://github.com/cryptomator/cryptomator/wiki).
|
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [FAQ](https://community.cryptomator.org/c/kb/faq).
|
||||||
- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptomator/issues/new).
|
- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptomator/issues/new).
|
||||||
|
|
||||||
## Do you have questions?
|
|
||||||
|
|
||||||
- Ask questions by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new).
|
|
||||||
- [Contact us](https://cryptomator.org/contact/) directly by writing an email. Wir sprechen auch Deutsch!
|
|
||||||
- Have a chat with us on [Gitter](https://gitter.im/cryptomator/cryptomator).
|
|
||||||
|
|
||||||
## Do you miss a feature?
|
|
||||||
|
|
||||||
- Ensure the feature was not [already requested](https://github.com/cryptomator/cryptomator/issues).
|
|
||||||
- You're welcome to suggest a feature by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new).
|
|
||||||
|
|
||||||
## Did you write a patch that fixes a bug?
|
## Did you write a patch that fixes a bug?
|
||||||
|
|
||||||
- Open a new pull request with the patch.
|
- Open a new pull request with the patch.
|
||||||
@@ -29,7 +18,7 @@
|
|||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
|
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
## Above all, thank you for your contributions
|
## Above all, thank you for your contributions
|
||||||
|
|
||||||
31
.github/ISSUE_TEMPLATE.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
## Issue Checklist
|
||||||
|
|
||||||
|
Before creating a new issue make sure that you
|
||||||
|
- searched existing (and closed) issues: https://github.com/cryptomator/cryptomator/issues
|
||||||
|
- searched the knowledge base: https://community.cryptomator.org/c/kb
|
||||||
|
- have read the support guide: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
|
||||||
|
- have read the contribution guide: https://github.com/cryptomator/cryptomator/blob/develop/.github/CONTRIBUTING.md
|
||||||
|
- have read the code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
|
## Basic Info
|
||||||
|
|
||||||
|
I'm using Windows / macOS / Linux / … in version: …
|
||||||
|
|
||||||
|
I'm running Cryptomator in version: …
|
||||||
|
(You can check the version in the Cryptomator settings.)
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
…
|
||||||
|
(Please describe in detail what you did, what you expected, and what really happened.)
|
||||||
|
|
||||||
|
## Attachments (optional)
|
||||||
|
|
||||||
|
If you want to add the log file or screenshots, please add them as attachments. If your log file seems empty and doesn't show any errors, you may enable the debug mode first. Here is how to do that: https://community.cryptomator.org/t/how-do-i-enable-debug-mode/36
|
||||||
|
|
||||||
|
Then reproduce the problem to ensure all important information is contained in there. You may use test data or redact sensitive information from the log file.
|
||||||
|
|
||||||
|
You can find the log file
|
||||||
|
- on Windows: %appdata%/Cryptomator/cryptomator.log
|
||||||
|
- on macOS: ~/Library/Logs/Cryptomator/cryptomator.log
|
||||||
|
- on Linux: ~/.Cryptomator/cryptomator.log
|
||||||
15
.github/ISSUE_TEMPLATE/1.4.0-beta-testing.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/1.4.0-beta-testing.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
## 1.4.0 Beta Issue Checklist
|
||||||
|
- Existing 1.4.0 Beta Issues: https://github.com/cryptomator/cryptomator/milestone/27
|
||||||
|
- Contribution Guide: https://github.com/cryptomator/cryptomator/blob/develop/.github/CONTRIBUTING.md
|
||||||
|
- Code of Conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
|
## Software Used During the Test
|
||||||
|
- Cryptomator 1.4.0 Beta 1
|
||||||
|
- Ubuntu 16.04 / macOS 10.11.6 / etc
|
||||||
|
- Linux Kernel x.y.z
|
||||||
|
- Gnome x.y.z
|
||||||
|
- OpenOffice x.y.z
|
||||||
|
- ...
|
||||||
|
|
||||||
|
## Description
|
||||||
|
What doesn't work? What did you do? How can the bug be reproduced?
|
||||||
13
.github/no-response.yml
vendored
Normal file
13
.github/no-response.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Configuration for probot-no-response - https://github.com/probot/no-response
|
||||||
|
|
||||||
|
# Number of days of inactivity before an Issue is closed for lack of response
|
||||||
|
daysUntilClose: 14
|
||||||
|
# Label requiring a response
|
||||||
|
responseRequiredLabel: state:awaiting-response
|
||||||
|
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
|
||||||
|
closeComment: >
|
||||||
|
This issue has been automatically closed because there has been no response
|
||||||
|
to our request for more information from the original author. With only the
|
||||||
|
information that is currently in the issue, we don't have enough information
|
||||||
|
to take action. Please reach out if you have or find the answers we need so
|
||||||
|
that we can investigate further.
|
||||||
19
.github/stale.yml
vendored
Normal file
19
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 60
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 7
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- type:security-issue # never close automatically
|
||||||
|
- state:awaiting-response # handled by different bot
|
||||||
|
# Set to true to ignore issues in a milestone (defaults to false)
|
||||||
|
exemptMilestones: true
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: state:stale
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -9,11 +9,13 @@
|
|||||||
.settings
|
.settings
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
target/
|
|
||||||
test-output/
|
|
||||||
|
|
||||||
# IntelliJ Settings Files #
|
# Maven #
|
||||||
.idea/
|
target/
|
||||||
out/
|
|
||||||
.idea_modules/
|
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
|
||||||
*.iws
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
.idea/**/libraries/
|
||||||
|
*.iml
|
||||||
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Cryptomator
|
||||||
51
.idea/codeStyles/Project.xml
generated
Normal file
51
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="OTHER_INDENT_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
|
<option name="RIGHT_MARGIN" value="220" />
|
||||||
|
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
||||||
|
<JavaCodeStyleSettings>
|
||||||
|
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="30" />
|
||||||
|
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="10" />
|
||||||
|
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||||
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
|
</JavaCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="Groovy">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="HTML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JAVA">
|
||||||
|
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||||
|
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||||
|
<option name="KEEP_SIMPLE_BLOCKS_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" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JSON">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
36
.idea/compiler.xml
generated
Normal file
36
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?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.22.1/dagger-compiler-2.22.1.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.22.1/dagger-2.22.1.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.22.1/dagger-producers-2.22.1.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/25.0-jre/guava-25.0-jre.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.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/errorprone/error_prone_annotations/2.1.3/error_prone_annotations-2.1.3.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.14/animal-sniffer-annotations-1.14.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.22.1/dagger-spi-2.22.1.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" />
|
||||||
|
</processorPath>
|
||||||
|
<module name="keychain" />
|
||||||
|
<module name="commons" />
|
||||||
|
<module name="ui" />
|
||||||
|
<module name="launcher" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
<bytecodeTargetLevel>
|
||||||
|
<module name="buildkit" target="11" />
|
||||||
|
</bytecodeTargetLevel>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
.idea/encodings.xml
generated
Normal file
11
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?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>
|
||||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/main/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/modules.xml
generated
Normal file
9
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Desktop.iml" filepath="$PROJECT_DIR$/.idea/Desktop.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/main/buildkit/buildkit.iml" filepath="$PROJECT_DIR$/main/buildkit/buildkit.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/runConfigurations/Cryptomator_Linux.xml
generated
Normal file
10
.idea/runConfigurations/Cryptomator_Linux.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator Linux" 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/settings.json" -Dcryptomator.ipcPortPath="~/.config/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/.local/share/Cryptomator/logs" -Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" -Xss20m -Xmx512m" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
10
.idea/runConfigurations/Cryptomator_Windows.xml
generated
Normal file
10
.idea/runConfigurations/Cryptomator_Windows.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator Windows" 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/settings.json" -Dcryptomator.ipcPortPath="~/AppData/Roaming/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator/keychain.json" -Xss2m -Xmx512m" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
11
.idea/runConfigurations/Cryptomator_macOS.xml
generated
Normal file
11
.idea/runConfigurations/Cryptomator_macOS.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Cryptomator macOS" 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="~/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator" -Dcryptomator.mountPointsDir="/Volumes/" -Xss2m -Xmx512m" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
19
.travis-deploy-release.tmpl.json
Normal file
19
.travis-deploy-release.tmpl.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
15
.travis-deploy-snapshot.json
Normal file
15
.travis-deploy-snapshot.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
69
.travis.yml
69
.travis.yml
@@ -1,50 +1,55 @@
|
|||||||
|
dist: xenial
|
||||||
language: java
|
language: java
|
||||||
sudo: required
|
sudo: false
|
||||||
dist: trusty
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- openjdk11
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.m2
|
- $HOME/.m2
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: "IfYURwZaDWuBDvyn47n0k1Zod/IQw1FF+CS5nnV08Q+NfC3vGGJMwV8m59XnbfwnWGxwvCaAbk4qP6s6+ijgZNKkvgfFMo3rfTok5zt43bIqgaFOANYV+OC/1c59gYD6ZUxhW5iNgMgU3qdsRtJuwSmfkVv/jKyLGfAbS4kN8BA=" # COVERITY_SCAN_TOKEN
|
- secure: "HftEaabMmWn5GwKFKksUkOcelc3Mn7xazwAEy+4d4gL1+F8VhID/6DCK7nas+afUymWnxTano8Rv4Ci5MWryNkNkTH+FUPWmF3xWezc3hajSyS7RB92IZ8VPetl4Fo8UI1WwM5apDEaugalPxkIf8a7N+lpG5X/Gpumwzo3Be3w=" # BINTRAY_API_KEY
|
||||||
- secure: "lV9OwUbHMrMpLUH1CY+Z4puLDdFXytudyPlG1eGRsesdpuG6KM3uQVz6uAtf6lrU8DRbMM/T7ML+PmvQ4UoPPYLdLxESLLBat2qUPOIVBOhTSlCc7I0DmGy04CSvkeMy8dPaQC0ukgNiR7zwoNzfcpGRN/U9S8tziDruuHoZSrg=" # BINTRAY_API_KEY
|
|
||||||
- secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN
|
- secure: "oWFgRTVP6lyTa7qVxlvkpm20MtVc3BtmsNXQJS6bfg2A0o/iCQMNx7OD59BaafCLGRKvCcJVESiC8FlSylVMS7CDSyYu0gg70NUiIuHp4NBM5inFWYCy/PdQsCTzr5uvNG+rMFQpMFRaCV0FrfM3tLondcVkhsHL68l93Xoexx4=" # CODACY_PROJECT_TOKEN
|
||||||
|
- secure: "zJxgytA2Ks5Xzv+7kUaUq+EBFNQw9Qec63lcMJVuXVWczjL16nKW1EzzV515ag+OWL46z3lEPForDhufw0VtFnNmaX68jkO0mp01eLrHApc1llN2Y/U8GBXfNNazN4+Kom4H+z/AO+wJr8EsKMMUczCdQ3APgd9uVI0hzXw/Z3M=" # GITHUB_API_KEY
|
||||||
addons:
|
addons:
|
||||||
coverity_scan:
|
apt:
|
||||||
project:
|
packages:
|
||||||
name: "cryptomator/cryptomator"
|
- haveged
|
||||||
notification_email: sebastian.stenzel@cryptomator.org
|
|
||||||
build_command: "mvn -fmain/pom.xml clean test -DskipTests"
|
|
||||||
branch_pattern: release.*
|
|
||||||
install:
|
install:
|
||||||
# "clean" needed until https://bugs.openjdk.java.net/browse/JDK-8067747 is resolved.
|
- curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/e60ee486e34ee0c79f89f947abe2c83b4290c6bb/settings.xml
|
||||||
- mvn -fmain/pom.xml clean package -DskipTests dependency:go-offline -Pcoverage
|
- mvn -fmain/pom.xml clean install -DskipTests 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
|
||||||
- mvn -fmain/pom.xml clean package -DskipTests dependency:go-offline -Prelease
|
|
||||||
script:
|
script:
|
||||||
- mvn --update-snapshots -fmain/pom.xml clean test jacoco:report verify -Pcoverage
|
- 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/4.0.2/codacy-coverage-reporter-4.0.2-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:
|
before_deploy:
|
||||||
- mvn -fmain/pom.xml -Prelease clean package -DskipTests
|
- |
|
||||||
|
if [[ -n "$TRAVIS_TAG" ]]; then
|
||||||
|
mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=$TRAVIS_TAG
|
||||||
|
elif [[ $TRAVIS_BRANCH == "develop" ]] && [[ $TRAVIS_PULL_REQUEST == "false" ]]; then
|
||||||
|
mvn -fmain/pom.xml org.codehaus.mojo:versions-maven-plugin:set -DnewVersion=SNAPSHOT-$(echo $TRAVIS_COMMIT | head -c7)
|
||||||
|
fi
|
||||||
|
- mvn -fmain/pom.xml clean package -Prelease -DskipTests
|
||||||
|
- export TODAY=`date +'%Y-%m-%d'`; envsubst '$TRAVIS_TAG $TODAY' < .travis-deploy-release.tmpl.json > .travis-deploy-release.json
|
||||||
deploy:
|
deploy:
|
||||||
- provider: releases
|
- provider: bintray # SNAPSHOTS
|
||||||
prerelease: false
|
file: .travis-deploy-snapshot.json
|
||||||
api_key:
|
user: cryptobot
|
||||||
secure: "ZjE1j93v3qbPIe2YbmhS319aCbMdLQw0HuymmluTurxXsZtn9D4t2+eTr99vBVxGRuB5lzzGezPR5zjk5W7iHF7xhwrawXrFzr2rPJWzWFt0aM+Ry2njU1ROTGGXGTbv4anWeBlgMxLEInTAy/9ytOGNJlec83yc0THpOY2wxnk="
|
key: $BINTRAY_API_KEY
|
||||||
file:
|
|
||||||
- "main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar"
|
|
||||||
- "main/ant-kit/target/antkit.tar.gz"
|
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
repo: cryptomator/cryptomator
|
repo: cryptomator/cryptomator
|
||||||
tags: true
|
branch: develop
|
||||||
- provider: script
|
- provider: bintray # RELEASES
|
||||||
script: "curl -X POST -u cryptobot:${BINTRAY_API_KEY} -H 'Content-Type: application/json' -d '{\"name\": \"${TRAVIS_TAG}\", \"vcs_tag\": \"${TRAVIS_TAG}\"}' https://api.bintray.com/packages/cryptomator/cryptomator/cryptomator-win/versions"
|
file: .travis-deploy-release.json
|
||||||
|
user: cryptobot
|
||||||
|
key: $BINTRAY_API_KEY
|
||||||
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
repo: cryptomator/cryptomator
|
repo: cryptomator/cryptomator
|
||||||
tags: true
|
tags: true
|
||||||
- provider: script
|
|
||||||
script: "curl -X POST -u cryptobot:${BINTRAY_API_KEY} -H 'Content-Type: application/json' -d '{\"name\": \"${TRAVIS_TAG}\", \"vcs_tag\": \"${TRAVIS_TAG}\"}' https://api.bintray.com/packages/cryptomator/cryptomator/cryptomator-osx/versions"
|
|
||||||
on:
|
|
||||||
repo: cryptomator/cryptomator
|
|
||||||
tags: true
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
To tick a checkbox replace [ ] with [x]. Make sure to replace placeholders (…) accordingly.
|
|
||||||
|
|
||||||
## Issue Checklist
|
|
||||||
|
|
||||||
Before creating a new issue make sure that you
|
|
||||||
- [ ] searched [existing (and closed) issues](https://github.com/cryptomator/cryptomator/issues).
|
|
||||||
- [ ] searched the [knowledge base](https://cryptomator.freshdesk.com/support/solutions).
|
|
||||||
- [ ] have read the [contribution guide](https://github.com/cryptomator/cryptomator/blob/master/CONTRIBUTING.md).
|
|
||||||
- [ ] have read the [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
|
|
||||||
|
|
||||||
## Basic Info
|
|
||||||
|
|
||||||
This is a
|
|
||||||
- [ ] bug report.
|
|
||||||
- [ ] feature request.
|
|
||||||
- [ ] question or something else.
|
|
||||||
|
|
||||||
I'm using
|
|
||||||
- [ ] Windows in version: …
|
|
||||||
- [ ] macOS in version: …
|
|
||||||
- [ ] Linux in version: …
|
|
||||||
|
|
||||||
I'm running Cryptomator in version: …
|
|
||||||
(You can check the version in the Cryptomator settings.)
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
…
|
|
||||||
(Please describe in detail what you did, what you expected, and what really happened.)
|
|
||||||
|
|
||||||
## Attachments (optional)
|
|
||||||
|
|
||||||
If you want to add the log file or screenshots, please add them as attachments. If your log file seems empty and doesn't show any errors, you may enable the [debug mode](https://cryptomator.freshdesk.com/support/solutions/articles/16000046480) first and reproduce the problem to ensure all important information is contained in there. You may use test data or redact sensitive information from the log file.
|
|
||||||
|
|
||||||
You can find the log file
|
|
||||||
- on Windows: %appdata%/Cryptomator/cryptomator.log
|
|
||||||
- on macOS: ~/Library/Logs/Cryptomator/cryptomator.log
|
|
||||||
- on Linux: ~/.Cryptomator/cryptomator.log
|
|
||||||
17
README.md
17
README.md
@@ -1,11 +1,12 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://travis-ci.org/cryptomator/cryptomator)
|
[](https://travis-ci.org/cryptomator/cryptomator)
|
||||||
[](https://scan.coverity.com/projects/cryptomator-cryptomator)
|
[](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)
|
||||||
[](https://poeditor.com/join/project/bHwbvJmx0E)
|
[](https://poeditor.com/join/project/bHwbvJmx0E)
|
||||||
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
||||||
|
[](https://community.cryptomator.org)
|
||||||
|
|
||||||
Multi-platform transparent client-side encryption of your files in the cloud.
|
Multi-platform transparent client-side encryption of your files in the cloud.
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Works with Dropbox, Google Drive, OneDrive, Nextcloud and any other cloud storage service which synchronizes with a local directory
|
- Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
|
||||||
- Open Source means: No backdoors, control is better than trust
|
- Open Source means: No backdoors, control is better than trust
|
||||||
- Client-side: No accounts, no data shared with any online service
|
- Client-side: No accounts, no data shared with any online service
|
||||||
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
|
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
|
||||||
@@ -21,6 +22,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:
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
|
|
||||||
@@ -44,8 +46,7 @@ For more information on the security details visit [cryptomator.org](https://cry
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* Java 8 (min. 8u51, we recommend to use the current version)
|
* Java 10 (min. 10.0.1, we recommend to use the current version)
|
||||||
* [JCE unlimited strength policy files](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) (needed for 256-bit keys)
|
|
||||||
* 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))
|
||||||
|
|
||||||
@@ -58,14 +59,6 @@ mvn clean install -Prelease
|
|||||||
|
|
||||||
An executable jar file will be created inside `main/uber-jar/target`.
|
An executable jar file will be created inside `main/uber-jar/target`.
|
||||||
|
|
||||||
## Contributing to Cryptomator
|
|
||||||
|
|
||||||
Please read our [contribution guide](https://github.com/cryptomator/cryptomator/blob/master/CONTRIBUTING.md), if you would like to report a bug, ask a question or help us with coding.
|
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/master/CODE_OF_CONDUCT.md).
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is dual-licensed under the GPLv3 for FOSS projects as well as a commercial license for independent software vendors and resellers. If you want to modify this application under different conditions, feel free to contact our support team.
|
This project is dual-licensed under the GPLv3 for FOSS projects as well as a commercial license for independent software vendors and resellers. If you want to modify this application under different conditions, feel free to contact our support team.
|
||||||
|
|||||||
10
SUPPORT.md
Normal file
10
SUPPORT.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Support for Cryptomator
|
||||||
|
|
||||||
|
For development-related topics, GitHub is the right place.
|
||||||
|
|
||||||
|
For _everything else_, please visit our official [Cryptomator Community](https://community.cryptomator.org) (we are there, too :wink:). Amongst others, you will find:
|
||||||
|
|
||||||
|
- Installation manuals
|
||||||
|
- Usage guides
|
||||||
|
- Help with problems
|
||||||
|
- Tips & tricks
|
||||||
1
main/ant-kit/.gitignore
vendored
1
main/ant-kit/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
/target/
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
|
||||||
<id>tarball</id>
|
|
||||||
<includeBaseDirectory>false</includeBaseDirectory>
|
|
||||||
<formats>
|
|
||||||
<format>tar.gz</format>
|
|
||||||
</formats>
|
|
||||||
<fileSets>
|
|
||||||
<fileSet>
|
|
||||||
<directory>target/libs</directory>
|
|
||||||
<includes>
|
|
||||||
<include>*.jar</include>
|
|
||||||
</includes>
|
|
||||||
<outputDirectory>libs</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
|
||||||
<directory>target/fixed-binaries</directory>
|
|
||||||
<filtered>false</filtered>
|
|
||||||
<outputDirectory>fixed-binaries</outputDirectory>
|
|
||||||
<fileMode>755</fileMode>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
|
||||||
<directory>target/package</directory>
|
|
||||||
<filtered>false</filtered>
|
|
||||||
<outputDirectory>package</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
<fileSet>
|
|
||||||
<directory>target</directory>
|
|
||||||
<includes>
|
|
||||||
<include>build.xml</include>
|
|
||||||
</includes>
|
|
||||||
<filtered>false</filtered>
|
|
||||||
<outputDirectory>.</outputDirectory>
|
|
||||||
</fileSet>
|
|
||||||
</fileSets>
|
|
||||||
</assembly>
|
|
||||||
@@ -1,99 +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.3.0</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>ant-kit</artifactId>
|
|
||||||
<packaging>pom</packaging>
|
|
||||||
<name>Cryptomator Ant Build Kit</name>
|
|
||||||
<description>Builds a package that can be built with Ant locally</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>launcher</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- copy libraries to target/libs/: -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-libs</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-dependencies</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- copy resources to target/: -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>3.0.2</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-resources</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-resources</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
|
||||||
<escapeString>\</escapeString>
|
|
||||||
<encoding>UTF-8</encoding>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
<filtering>true</filtering>
|
|
||||||
<excludes>
|
|
||||||
<exclude>fixed-binaries/**</exclude>
|
|
||||||
</excludes>
|
|
||||||
</resource>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
<filtering>false</filtering>
|
|
||||||
<includes>
|
|
||||||
<include>fixed-binaries/**</include>
|
|
||||||
</includes>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- create antkit.tar.gz: -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-assembly</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<descriptors>
|
|
||||||
<descriptor>assembly.xml</descriptor>
|
|
||||||
</descriptors>
|
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
|
||||||
<finalName>antkit</finalName>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project name="Cryptomator" default="create-jar" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
|
|
||||||
<taskdef uri="javafx:com.sun.javafx.tools.ant" resource="com/sun/javafx/tools/ant/antlib.xml" classpath="\${java.class.path}:\${java.home}/../lib/ant-javafx.jar:." />
|
|
||||||
|
|
||||||
<!-- Define application to build -->
|
|
||||||
<fx:application id="Cryptomator" name="Cryptomator" version="${project.version}" mainClass="org.cryptomator.launcher.Cryptomator" />
|
|
||||||
|
|
||||||
<!-- Create main application jar -->
|
|
||||||
<target name="create-jar">
|
|
||||||
<fx:jar destfile="antbuild/Cryptomator-${project.version}.jar">
|
|
||||||
<fx:application refid="Cryptomator" />
|
|
||||||
<fx:fileset dir="libs" includes="launcher-${project.version}.jar" />
|
|
||||||
<fx:resources>
|
|
||||||
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="launcher-${project.version}.jar" />
|
|
||||||
</fx:resources>
|
|
||||||
<fx:manifest>
|
|
||||||
<fx:attribute name="Implementation-Vendor" value="cryptomator.org" />
|
|
||||||
<fx:attribute name="Implementation-Title" value="Cryptomator"/>
|
|
||||||
<fx:attribute name="Implementation-Version" value="${project.version}" />
|
|
||||||
</fx:manifest>
|
|
||||||
</fx:jar>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- Create Debian package -->
|
|
||||||
<target name="deb" depends="create-jar">
|
|
||||||
<fx:deploy nativeBundles="deb" outdir="antbuild" outfile="Cryptomator-${project.version}" verbose="true">
|
|
||||||
<fx:application refid="Cryptomator" />
|
|
||||||
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="MIT" category="Utility">
|
|
||||||
<fx:association mimetype="application/x-vnd.cryptomator-vault-metadata" extension="cryptomator" description="Cryptomator Vault Metadata" />
|
|
||||||
</fx:info>
|
|
||||||
<fx:platform j2se="8.0">
|
|
||||||
<fx:property name="logback.configurationFile" value="logback.xml" />
|
|
||||||
<fx:property name="cryptomator.settingsPath" value="~/.Cryptomator/settings.json" />
|
|
||||||
<fx:property name="cryptomator.ipcPortPath" value="~/.Cryptomator/ipcPort.bin" />
|
|
||||||
<fx:jvmarg value="-Xss2m"/>
|
|
||||||
<fx:jvmarg value="-Xmx512m"/>
|
|
||||||
</fx:platform>
|
|
||||||
<fx:resources>
|
|
||||||
<fx:fileset dir="." type="data" includes="logback.xml" />
|
|
||||||
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
|
|
||||||
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="launcher-${project.version}.jar"/>
|
|
||||||
<fx:fileset dir="fixed-binaries" type="data" includes="linux-launcher-*" arch=""/>
|
|
||||||
</fx:resources>
|
|
||||||
<fx:permissions elevated="false" />
|
|
||||||
<fx:preferences install="true" />
|
|
||||||
</fx:deploy>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- Create Red Hat package -->
|
|
||||||
<target name="rpm" depends="create-jar">
|
|
||||||
<fx:deploy nativeBundles="rpm" outdir="antbuild" outfile="Cryptomator-${project.version}" verbose="true">
|
|
||||||
<fx:application refid="Cryptomator" />
|
|
||||||
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="MIT" category="Utility">
|
|
||||||
<fx:association mimetype="application/x-vnd.cryptomator-vault-metadata" extension="cryptomator" description="Cryptomator Vault Metadata" />
|
|
||||||
</fx:info>
|
|
||||||
<fx:platform j2se="8.0">
|
|
||||||
<fx:property name="logback.configurationFile" value="logback.xml" />
|
|
||||||
<fx:property name="cryptomator.settingsPath" value="~/.Cryptomator/settings.json" />
|
|
||||||
<fx:property name="cryptomator.ipcPortPath" value="~/.Cryptomator/ipcPort.bin" />
|
|
||||||
<fx:jvmarg value="-Xss2m"/>
|
|
||||||
<fx:jvmarg value="-Xmx512m"/>
|
|
||||||
</fx:platform>
|
|
||||||
<fx:resources>
|
|
||||||
<fx:fileset dir="." type="data" includes="logback.xml" />
|
|
||||||
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
|
|
||||||
<fx:fileset dir="libs" type="jar" includes="*.jar" excludes="launcher-${project.version}.jar"/>
|
|
||||||
<fx:fileset dir="fixed-binaries" type="data" includes="linux-launcher-*" arch=""/>
|
|
||||||
</fx:resources>
|
|
||||||
<fx:permissions elevated="false" />
|
|
||||||
<fx:preferences install="true" />
|
|
||||||
</fx:deploy>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,44 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE xml>
|
|
||||||
<configuration scan="true" debug="true">
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
|
||||||
<file>${user.home}/.Cryptomator/cryptomator.log</file>
|
|
||||||
<append>false</append>
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
|
||||||
<fileNamePattern>${user.home}/.Cryptomator/cryptomator%i.log</fileNamePattern>
|
|
||||||
<minIndex>0</minIndex>
|
|
||||||
<maxIndex>9</maxIndex>
|
|
||||||
</rollingPolicy>
|
|
||||||
<triggeringPolicy class="org.cryptomator.logging.LaunchBasedTriggeringPolicy" />
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="UPGRADE_FILE" class="ch.qos.logback.core.FileAppender">
|
|
||||||
<file>${user.home}/.Cryptomator/upgrade.log</file>
|
|
||||||
<append>true</append>
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="org.cryptomator" level="INFO" />
|
|
||||||
<logger name="org.eclipse.jetty.server.HttpChannel" level="INFO" />
|
|
||||||
<logger name="org.cryptomator.ui.model" level="INFO">
|
|
||||||
<appender-ref ref="UPGRADE_FILE" />
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
<appender-ref ref="FILE" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</configuration>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
Package: APPLICATION_PACKAGE
|
|
||||||
Version: APPLICATION_VERSION
|
|
||||||
Section: contrib/utils
|
|
||||||
Maintainer: Sebastian Stenzel <sebastian.stenzel@gmail.com>
|
|
||||||
Homepage: https://cryptomator.org
|
|
||||||
Vcs-Git: https://github.com/totalvoidness/cryptomator.git
|
|
||||||
Vcs-Browser: https://github.com/totalvoidness/cryptomator
|
|
||||||
Priority: optional
|
|
||||||
Architecture: APPLICATION_ARCH
|
|
||||||
Provides: APPLICATION_PACKAGE
|
|
||||||
Installed-Size: APPLICATION_INSTALLED_SIZE
|
|
||||||
Depends: gvfs-bin, gvfs-backends, gvfs-fuse
|
|
||||||
Description: Multi-platform client-side encryption of your cloud files.
|
|
||||||
Cryptomator provides free client-side AES encryption for your cloud files.
|
|
||||||
Create encrypted vaults, which get mounted as virtual volumes. Whatever
|
|
||||||
you save on one of these volumes will end up encrypted inside your vault.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Upstream-Name: cryptomator
|
|
||||||
Source: <https://github.com/totalvoidness/cryptomator>
|
|
||||||
|
|
||||||
Copyright: 2015 Sebastian Stenzel <sebastian.stenzel@gmail.com> and contributors.
|
|
||||||
License: MIT
|
|
||||||
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,51 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# postinst script for APPLICATION_NAME
|
|
||||||
#
|
|
||||||
# see: dh_installdeb(1)
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# summary of how this script can be called:
|
|
||||||
# * <postinst> `configure' <most-recently-configured-version>
|
|
||||||
# * <old-postinst> `abort-upgrade' <new version>
|
|
||||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
|
|
||||||
# <new-version>
|
|
||||||
# * <postinst> `abort-remove'
|
|
||||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
|
|
||||||
# <failed-install-package> <version> `removing'
|
|
||||||
# <conflicting-package> <version>
|
|
||||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
|
||||||
# the debian-policy package
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
configure)
|
|
||||||
echo Adding shortcut to the menu
|
|
||||||
SECONDARY_LAUNCHERS_INSTALL
|
|
||||||
APP_CDS_CACHE
|
|
||||||
mkdir -pm 644 /usr/share/desktop-directories
|
|
||||||
xdg-desktop-menu install --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
|
|
||||||
FILE_ASSOCIATION_INSTALL
|
|
||||||
|
|
||||||
rm /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
if [ $(uname -m) = "x86_64" ]; then
|
|
||||||
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x64 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
else
|
|
||||||
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x86 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
abort-upgrade|abort-remove|abort-deconfigure)
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "postinst called with unknown argument \`$1'" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# dh_installdeb will replace this with shell code automatically
|
|
||||||
# generated by other debhelper scripts.
|
|
||||||
|
|
||||||
#DEBHELPER#
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
Summary: APPLICATION_SUMMARY
|
|
||||||
Name: APPLICATION_PACKAGE
|
|
||||||
Version: APPLICATION_VERSION
|
|
||||||
Release: 1
|
|
||||||
License: APPLICATION_LICENSE_TYPE
|
|
||||||
Vendor: APPLICATION_VENDOR
|
|
||||||
Prefix: /opt
|
|
||||||
Provides: APPLICATION_PACKAGE
|
|
||||||
Requires: ld-linux.so.2 libX11.so.6 libXext.so.6 libXi.so.6 libXrender.so.1 libXtst.so.6 libasound.so.2 libc.so.6 libdl.so.2 libgcc_s.so.1 libm.so.6 libpthread.so.0 libthread_db.so.1
|
|
||||||
Autoprov: 0
|
|
||||||
Autoreq: 0
|
|
||||||
|
|
||||||
#avoid ARCH subfolder
|
|
||||||
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
|
|
||||||
|
|
||||||
#comment line below to enable effective jar compression
|
|
||||||
#it could easily get your package size from 40 to 15Mb but
|
|
||||||
#build time will substantially increase and it may require unpack200/system java to install
|
|
||||||
%define __jar_repack %{nil}
|
|
||||||
|
|
||||||
%description
|
|
||||||
APPLICATION_DESCRIPTION
|
|
||||||
|
|
||||||
%prep
|
|
||||||
|
|
||||||
%build
|
|
||||||
|
|
||||||
%install
|
|
||||||
rm -rf %{buildroot}
|
|
||||||
mkdir -p %{buildroot}/opt
|
|
||||||
cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}/opt
|
|
||||||
|
|
||||||
%files
|
|
||||||
APPLICATION_LICENSE_FILE
|
|
||||||
/opt/APPLICATION_FS_NAME
|
|
||||||
|
|
||||||
%post
|
|
||||||
SECONDARY_LAUNCHERS_INSTALL
|
|
||||||
APP_CDS_CACHE
|
|
||||||
xdg-desktop-menu install --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
|
|
||||||
FILE_ASSOCIATION_INSTALL
|
|
||||||
rm /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
if [ $(uname -m) = "x86_64" ]; then
|
|
||||||
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x64 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
else
|
|
||||||
mv /opt/APPLICATION_FS_NAME/app/linux-launcher-x86 /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
%preun
|
|
||||||
SECONDARY_LAUNCHERS_REMOVE
|
|
||||||
xdg-desktop-menu uninstall --novendor /opt/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
|
|
||||||
FILE_ASSOCIATION_REMOVE
|
|
||||||
|
|
||||||
%clean
|
|
||||||
32
main/buildkit/assembly-linux.xml
Normal file
32
main/buildkit/assembly-linux.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?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"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
|
<id>tarball</id>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/</directory>
|
||||||
|
<includes>
|
||||||
|
<include>version.txt</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/linux-libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
||||||
32
main/buildkit/assembly-mac.xml
Normal file
32
main/buildkit/assembly-mac.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?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"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
|
<id>tarball</id>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/</directory>
|
||||||
|
<includes>
|
||||||
|
<include>version.txt</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/mac-libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
||||||
32
main/buildkit/assembly-win.xml
Normal file
32
main/buildkit/assembly-win.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?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"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
|
<id>tarball</id>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
</formats>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/</directory>
|
||||||
|
<includes>
|
||||||
|
<include>version.txt</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>target/win-libs</directory>
|
||||||
|
<includes>
|
||||||
|
<include>*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
</assembly>
|
||||||
157
main/buildkit/pom.xml
Normal file
157
main/buildkit/pom.xml
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?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.4.9</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>buildkit</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Cryptomator Build Kit</name>
|
||||||
|
<description>Builds a package that can be built with Ant locally</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>launcher</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- generate version.txt -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.basedir}/src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>version.txt</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- copy libraries to target/libs/: -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-libs</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<excludeClassifiers>linux,mac,win</excludeClassifiers>
|
||||||
|
</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-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>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
1
main/buildkit/src/main/resources/version.txt
Normal file
1
main/buildkit/src/main/resources/version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
${project.version}
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
<?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"
|
<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">
|
||||||
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.3.0</version>
|
<version>1.4.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
<name>Cryptomator Commons</name>
|
<name>Cryptomator Commons</name>
|
||||||
<description>Shared utilities</description>
|
<description>Shared utilities</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- JavaFx -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-base</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Libs -->
|
<!-- Libs -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
@@ -29,13 +34,13 @@
|
|||||||
<groupId>org.fxmisc.easybind</groupId>
|
<groupId>org.fxmisc.easybind</groupId>
|
||||||
<artifactId>easybind</artifactId>
|
<artifactId>easybind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- DI -->
|
<!-- DI -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.dagger</groupId>
|
<groupId>com.google.dagger</groupId>
|
||||||
<artifactId>dagger</artifactId>
|
<artifactId>dagger</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Logging -->
|
<!-- Logging -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.Spliterators;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class Environment {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
||||||
|
private static final String USER_HOME = System.getProperty("user.home");
|
||||||
|
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
|
||||||
|
private static final Path ABSOLUTE_HOME_DIR = Paths.get(USER_HOME);
|
||||||
|
private static final char PATH_LIST_SEP = ':';
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Environment() {
|
||||||
|
LOG.debug("user.language: {}", System.getProperty("user.language"));
|
||||||
|
LOG.debug("user.region: {}", System.getProperty("user.region"));
|
||||||
|
LOG.debug("logback.configurationFile: {}", System.getProperty("logback.configurationFile"));
|
||||||
|
LOG.debug("cryptomator.settingsPath: {}", System.getProperty("cryptomator.settingsPath"));
|
||||||
|
LOG.debug("cryptomator.ipcPortPath: {}", System.getProperty("cryptomator.ipcPortPath"));
|
||||||
|
LOG.debug("cryptomator.keychainPath: {}", System.getProperty("cryptomator.keychainPath"));
|
||||||
|
LOG.debug("cryptomator.logDir: {}", System.getProperty("cryptomator.logDir"));
|
||||||
|
LOG.debug("cryptomator.mountPointsDir: {}", System.getProperty("cryptomator.mountPointsDir"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useCustomLogbackConfig() {
|
||||||
|
return getPath("logback.configurationFile").map(Files::exists).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getSettingsPath() {
|
||||||
|
return getPaths("cryptomator.settingsPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getIpcPortPath() {
|
||||||
|
return getPaths("cryptomator.ipcPortPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getKeychainPath() {
|
||||||
|
return getPaths("cryptomator.keychainPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Path> getLogDir() {
|
||||||
|
return getPath("cryptomator.logDir").map(this::replaceHomeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Path> getMountPointsDir() {
|
||||||
|
return getPath("cryptomator.mountPointsDir").map(this::replaceHomeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Path> getPath(String propertyName) {
|
||||||
|
String value = System.getProperty(propertyName);
|
||||||
|
return Optional.ofNullable(value).map(Paths::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
Stream<Path> getPaths(String propertyName) {
|
||||||
|
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
|
||||||
|
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path replaceHomeDir(Path path) {
|
||||||
|
if (path.startsWith(RELATIVE_HOME_DIR)) {
|
||||||
|
return ABSOLUTE_HOME_DIR.resolve(RELATIVE_HOME_DIR.relativize(path));
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<String> getRawList(String propertyName, char separator) {
|
||||||
|
String value = System.getProperty(propertyName);
|
||||||
|
if (value == null) {
|
||||||
|
return Stream.empty();
|
||||||
|
} else {
|
||||||
|
Iterable<String> iter = Splitter.on(separator).split(value);
|
||||||
|
Spliterator<String> spliter = Spliterators.spliteratorUnknownSize(iter.iterator(), Spliterator.ORDERED | Spliterator.IMMUTABLE);
|
||||||
|
return StreamSupport.stream(spliter, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import javax.inject.Scope;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Scope
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface FxApplicationScoped {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,41 +2,41 @@
|
|||||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Sebastian Stenzel - initial API and implementation
|
* Sebastian Stenzel - initial API and implementation
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import javafx.beans.property.*;
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.IntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
|
|
||||||
public static final int MIN_PORT = 1024;
|
public static final int MIN_PORT = 1024;
|
||||||
public static final int MAX_PORT = 65535;
|
public static final int MAX_PORT = 65535;
|
||||||
public static final boolean DEFAULT_CHECK_FOR_UDPATES = true;
|
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
|
||||||
|
public static final boolean DEFAULT_CHECK_FOR_UDPATES = false;
|
||||||
public static final int DEFAULT_PORT = 42427;
|
public static final int DEFAULT_PORT = 42427;
|
||||||
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||||
public static final String DEFAULT_GVFS_SCHEME = "dav";
|
public static final String DEFAULT_GVFS_SCHEME = "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;
|
||||||
|
|
||||||
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 checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
|
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UDPATES);
|
||||||
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
|
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
|
||||||
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
|
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
|
||||||
private final StringProperty preferredGvfsScheme = new SimpleStringProperty(DEFAULT_GVFS_SCHEME);
|
private final StringProperty preferredGvfsScheme = new SimpleStringProperty(DEFAULT_GVFS_SCHEME);
|
||||||
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 Consumer<Settings> saveCmd;
|
private Consumer<Settings> saveCmd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,11 +44,13 @@ public class Settings {
|
|||||||
*/
|
*/
|
||||||
Settings() {
|
Settings() {
|
||||||
directories.addListener((ListChangeListener.Change<? extends VaultSettings> change) -> this.save());
|
directories.addListener((ListChangeListener.Change<? extends VaultSettings> change) -> this.save());
|
||||||
|
askedForUpdateCheck.addListener(this::somethingChanged);
|
||||||
checkForUpdates.addListener(this::somethingChanged);
|
checkForUpdates.addListener(this::somethingChanged);
|
||||||
port.addListener(this::somethingChanged);
|
port.addListener(this::somethingChanged);
|
||||||
numTrayNotifications.addListener(this::somethingChanged);
|
numTrayNotifications.addListener(this::somethingChanged);
|
||||||
preferredGvfsScheme.addListener(this::somethingChanged);
|
preferredGvfsScheme.addListener(this::somethingChanged);
|
||||||
debugMode.addListener(this::somethingChanged);
|
debugMode.addListener(this::somethingChanged);
|
||||||
|
preferredVolumeImpl.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||||
@@ -71,6 +73,10 @@ public class Settings {
|
|||||||
return directories;
|
return directories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanProperty askedForUpdateCheck() {
|
||||||
|
return askedForUpdateCheck;
|
||||||
|
}
|
||||||
|
|
||||||
public BooleanProperty checkForUpdates() {
|
public BooleanProperty checkForUpdates() {
|
||||||
return checkForUpdates;
|
return checkForUpdates;
|
||||||
}
|
}
|
||||||
@@ -91,4 +97,8 @@ public class Settings {
|
|||||||
return debugMode;
|
return debugMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<VolumeImpl> preferredVolumeImpl() {
|
||||||
|
return preferredVolumeImpl;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,16 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.gson.TypeAdapter;
|
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 org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
||||||
|
|
||||||
@@ -28,11 +27,13 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
out.beginObject();
|
out.beginObject();
|
||||||
out.name("directories");
|
out.name("directories");
|
||||||
writeVaultSettingsArray(out, value.getDirectories());
|
writeVaultSettingsArray(out, value.getDirectories());
|
||||||
|
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
|
||||||
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
|
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
|
||||||
out.name("port").value(value.port().get());
|
out.name("port").value(value.port().get());
|
||||||
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
|
||||||
out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get());
|
out.name("preferredGvfsScheme").value(value.preferredGvfsScheme().get());
|
||||||
out.name("debugMode").value(value.debugMode().get());
|
out.name("debugMode").value(value.debugMode().get());
|
||||||
|
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,27 +53,34 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
String name = in.nextName();
|
String name = in.nextName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "directories":
|
case "directories":
|
||||||
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||||
break;
|
break;
|
||||||
case "checkForUpdatesEnabled":
|
case "askedForUpdateCheck":
|
||||||
settings.checkForUpdates().set(in.nextBoolean());
|
settings.askedForUpdateCheck().set(in.nextBoolean());
|
||||||
break;
|
break;
|
||||||
case "port":
|
case "checkForUpdatesEnabled":
|
||||||
settings.port().set(in.nextInt());
|
settings.checkForUpdates().set(in.nextBoolean());
|
||||||
break;
|
break;
|
||||||
case "numTrayNotifications":
|
case "port":
|
||||||
settings.numTrayNotifications().set(in.nextInt());
|
settings.port().set(in.nextInt());
|
||||||
break;
|
break;
|
||||||
case "preferredGvfsScheme":
|
case "numTrayNotifications":
|
||||||
settings.preferredGvfsScheme().set(in.nextString());
|
settings.numTrayNotifications().set(in.nextInt());
|
||||||
break;
|
break;
|
||||||
case "debugMode":
|
case "preferredGvfsScheme":
|
||||||
settings.debugMode().set(in.nextBoolean());
|
settings.preferredGvfsScheme().set(in.nextString());
|
||||||
break;
|
break;
|
||||||
default:
|
case "debugMode":
|
||||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
settings.debugMode().set(in.nextBoolean());
|
||||||
in.skipValue();
|
break;
|
||||||
|
case "preferredVolumeImpl":
|
||||||
|
settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||||
|
in.skipValue();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
@@ -80,6 +88,14 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VolumeImpl parsePreferredVolumeImplName(String nioAdapterName) {
|
||||||
|
try {
|
||||||
|
return VolumeImpl.valueOf(nioAdapterName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Settings.DEFAULT_PREFERRED_VOLUME_IMPL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<VaultSettings> readVaultSettingsArray(JsonReader in) throws IOException {
|
private List<VaultSettings> readVaultSettingsArray(JsonReader in) throws IOException {
|
||||||
List<VaultSettings> result = new ArrayList<>();
|
List<VaultSettings> result = new ArrayList<>();
|
||||||
in.beginArray();
|
in.beginArray();
|
||||||
|
|||||||
@@ -8,6 +8,16 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.LazyInitializer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@@ -16,9 +26,8 @@ import java.io.OutputStreamWriter;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.FileSystems;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -27,110 +36,83 @@ 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;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.LazyInitializer;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SettingsProvider implements Provider<Settings> {
|
public class SettingsProvider implements Provider<Settings> {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
||||||
private static final Path DEFAULT_SETTINGS_PATH;
|
|
||||||
private static final long SAVE_DELAY_MS = 1000;
|
private static final long SAVE_DELAY_MS = 1000;
|
||||||
|
|
||||||
static {
|
|
||||||
final FileSystem fs = FileSystems.getDefault();
|
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
|
||||||
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "AppData/Roaming/Cryptomator/settings.json");
|
|
||||||
} else if (SystemUtils.IS_OS_MAC_OSX) {
|
|
||||||
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator/settings.json");
|
|
||||||
} else {
|
|
||||||
DEFAULT_SETTINGS_PATH = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator/settings.json");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
|
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 Gson gson;
|
private final Gson gson;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SettingsProvider() {
|
public SettingsProvider(Environment env) {
|
||||||
|
this.env = env;
|
||||||
this.gson = new GsonBuilder() //
|
this.gson = new GsonBuilder() //
|
||||||
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
||||||
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
||||||
.create();
|
.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getSettingsPath() {
|
|
||||||
final String settingsPathProperty = System.getProperty("cryptomator.settingsPath");
|
|
||||||
return Optional.ofNullable(settingsPathProperty).filter(StringUtils::isNotBlank).map(this::replaceHomeDir).map(FileSystems.getDefault()::getPath).orElse(DEFAULT_SETTINGS_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String replaceHomeDir(String path) {
|
|
||||||
if (path.startsWith("~/")) {
|
|
||||||
return SystemUtils.USER_HOME + path.substring(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Settings get() {
|
public Settings get() {
|
||||||
return LazyInitializer.initializeLazily(settings, this::load);
|
return LazyInitializer.initializeLazily(settings, this::load);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Settings load() {
|
private Settings load() {
|
||||||
Settings settings;
|
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings());
|
||||||
final Path settingsPath = getSettingsPath();
|
settings.setSaveCmd(this::scheduleSave);
|
||||||
try (InputStream in = Files.newInputStream(settingsPath, StandardOpenOption.READ); //
|
return settings;
|
||||||
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
}
|
||||||
settings = gson.fromJson(reader, Settings.class);
|
|
||||||
|
private Stream<Settings> tryLoad(Path path) {
|
||||||
|
LOG.debug("Attempting to load settings from {}", path);
|
||||||
|
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
|
||||||
|
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||||
|
Settings settings = gson.fromJson(reader, Settings.class);
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
throw new IOException("Unexpected EOF");
|
throw new IOException("Unexpected EOF");
|
||||||
}
|
}
|
||||||
LOG.info("Settings loaded from " + settingsPath);
|
LOG.info("Settings loaded from {}", path);
|
||||||
|
return Stream.of(settings);
|
||||||
|
} catch (NoSuchFileException e) {
|
||||||
|
return Stream.empty();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.info("Failed to load settings, creating new one.");
|
LOG.warn("Exception while loading settings from " + path, e);
|
||||||
settings = new Settings();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
settings.setSaveCmd(this::scheduleSave);
|
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleSave(Settings settings) {
|
private void scheduleSave(Settings settings) {
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ScheduledFuture<?> saveCmd = saveScheduler.schedule(() -> {
|
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
||||||
this.save(settings);
|
settingsPath.ifPresent(path -> {
|
||||||
}, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
Runnable saveCommand = () -> this.save(settings, path);
|
||||||
ScheduledFuture<?> previousSaveCmd = scheduledSaveCmd.getAndSet(saveCmd);
|
ScheduledFuture<?> scheduledTask = saveScheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||||
if (previousSaveCmd != null) {
|
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
||||||
previousSaveCmd.cancel(false);
|
if (previouslyScheduledTask != null) {
|
||||||
}
|
previouslyScheduledTask.cancel(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save(Settings settings) {
|
private void save(Settings settings, Path settingsPath) {
|
||||||
assert settings != null : "method should only be invoked by #scheduleSave, which checks for null";
|
assert settings != null : "method should only be invoked by #scheduleSave, which checks for null";
|
||||||
final Path settingsPath = getSettingsPath();
|
LOG.debug("Attempting to save settings to {}", settingsPath);
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(settingsPath.getParent());
|
Files.createDirectories(settingsPath.getParent());
|
||||||
try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
try (OutputStream out = Files.newOutputStream(settingsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
||||||
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
|
||||||
gson.toJson(settings, writer);
|
gson.toJson(settings, writer);
|
||||||
LOG.info("Settings saved to " + settingsPath);
|
LOG.info("Settings saved to {}", settingsPath);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to save settings.", e);
|
LOG.error("Failed to save settings.", e);
|
||||||
|
|||||||
@@ -5,16 +5,7 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import com.google.common.base.Strings;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
@@ -22,20 +13,37 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
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.util.Base64;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The settings specific to a single vault.
|
||||||
|
* TODO: Change the name of individualMountPath and its derivatives to customMountPath
|
||||||
|
*/
|
||||||
public class VaultSettings {
|
public class VaultSettings {
|
||||||
|
|
||||||
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
|
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
|
||||||
public static final boolean DEFAULT_MOUNT_AFTER_UNLOCK = true;
|
|
||||||
public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
|
public static final boolean DEFAULT_REAVEAL_AFTER_MOUNT = true;
|
||||||
|
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
|
||||||
|
public static final boolean DEFAULT_USES_READONLY_MODE = false;
|
||||||
|
|
||||||
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 mountName = 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 mountAfterUnlock = new SimpleBooleanProperty(DEFAULT_MOUNT_AFTER_UNLOCK);
|
|
||||||
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 StringProperty individualMountPath = new SimpleStringProperty();
|
||||||
|
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
|
||||||
|
|
||||||
public VaultSettings(String id) {
|
public VaultSettings(String id) {
|
||||||
this.id = Objects.requireNonNull(id);
|
this.id = Objects.requireNonNull(id);
|
||||||
@@ -44,7 +52,7 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
return new Observable[] {path, mountName, winDriveLetter, unlockAfterStartup, mountAfterUnlock, revealAfterMount};
|
return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, revealAfterMount, usesIndividualMountPath, individualMountPath, usesReadOnlyMode};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deriveMountNameFromPath(Path path) {
|
private void deriveMountNameFromPath(Path path) {
|
||||||
@@ -115,14 +123,30 @@ public class VaultSettings {
|
|||||||
return unlockAfterStartup;
|
return unlockAfterStartup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty mountAfterUnlock() {
|
|
||||||
return mountAfterUnlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty revealAfterMount() {
|
public BooleanProperty revealAfterMount() {
|
||||||
return revealAfterMount;
|
return revealAfterMount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanProperty usesIndividualMountPath() {
|
||||||
|
return usesIndividualMountPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty individualMountPath() {
|
||||||
|
return individualMountPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getIndividualMountPath() {
|
||||||
|
if (usesIndividualMountPath.get()) {
|
||||||
|
return Optional.ofNullable(Strings.emptyToNull(individualMountPath.get()));
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty usesReadOnlyMode() {
|
||||||
|
return usesReadOnlyMode;
|
||||||
|
}
|
||||||
|
|
||||||
/* Hashcode/Equals */
|
/* Hashcode/Equals */
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,14 +5,13 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.google.gson.stream.JsonReader;
|
||||||
import java.nio.file.Paths;
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.gson.stream.JsonReader;
|
import java.io.IOException;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
class VaultSettingsJsonAdapter {
|
class VaultSettingsJsonAdapter {
|
||||||
|
|
||||||
@@ -25,8 +24,10 @@ class VaultSettingsJsonAdapter {
|
|||||||
out.name("mountName").value(value.mountName().get());
|
out.name("mountName").value(value.mountName().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("mountAfterUnlock").value(value.mountAfterUnlock().get());
|
|
||||||
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
||||||
|
out.name("usesIndividualMountPath").value(value.usesIndividualMountPath().get());
|
||||||
|
out.name("individualMountPath").value(value.individualMountPath().get()); //TODO: should this always be written? ( because it could contain metadata, which the user may not want to save!)
|
||||||
|
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,39 +35,48 @@ class VaultSettingsJsonAdapter {
|
|||||||
String id = null;
|
String id = null;
|
||||||
String path = null;
|
String path = null;
|
||||||
String mountName = null;
|
String mountName = null;
|
||||||
|
String individualMountPath = null;
|
||||||
String winDriveLetter = null;
|
String winDriveLetter = null;
|
||||||
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
||||||
boolean mountAfterUnlock = VaultSettings.DEFAULT_MOUNT_AFTER_UNLOCK;
|
|
||||||
boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
|
boolean revealAfterMount = VaultSettings.DEFAULT_REAVEAL_AFTER_MOUNT;
|
||||||
|
boolean usesIndividualMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
|
||||||
|
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
||||||
|
|
||||||
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();
|
||||||
break;
|
break;
|
||||||
case "path":
|
case "path":
|
||||||
path = in.nextString();
|
path = in.nextString();
|
||||||
break;
|
break;
|
||||||
case "mountName":
|
case "mountName":
|
||||||
mountName = in.nextString();
|
mountName = in.nextString();
|
||||||
break;
|
break;
|
||||||
case "winDriveLetter":
|
case "winDriveLetter":
|
||||||
winDriveLetter = in.nextString();
|
winDriveLetter = in.nextString();
|
||||||
break;
|
break;
|
||||||
case "unlockAfterStartup":
|
case "unlockAfterStartup":
|
||||||
unlockAfterStartup = in.nextBoolean();
|
unlockAfterStartup = in.nextBoolean();
|
||||||
break;
|
break;
|
||||||
case "mountAfterUnlock":
|
case "revealAfterMount":
|
||||||
mountAfterUnlock = in.nextBoolean();
|
revealAfterMount = in.nextBoolean();
|
||||||
break;
|
break;
|
||||||
case "revealAfterMount":
|
case "usesIndividualMountPath":
|
||||||
revealAfterMount = in.nextBoolean();
|
usesIndividualMountPath = in.nextBoolean();
|
||||||
break;
|
break;
|
||||||
default:
|
case "individualMountPath":
|
||||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
individualMountPath = in.nextString();
|
||||||
in.skipValue();
|
break;
|
||||||
|
case "usesReadOnlyMode":
|
||||||
|
usesReadOnlyMode = in.nextBoolean();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||||
|
in.skipValue();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
@@ -76,8 +86,10 @@ class VaultSettingsJsonAdapter {
|
|||||||
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.mountAfterUnlock().set(mountAfterUnlock);
|
|
||||||
vaultSettings.revealAfterMount().set(revealAfterMount);
|
vaultSettings.revealAfterMount().set(revealAfterMount);
|
||||||
|
vaultSettings.usesIndividualMountPath().set(usesIndividualMountPath);
|
||||||
|
vaultSettings.individualMountPath().set(individualMountPath);
|
||||||
|
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
|
||||||
return vaultSettings;
|
return vaultSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public enum VolumeImpl {
|
||||||
|
WEBDAV("WebDAV"),
|
||||||
|
FUSE("FUSE"),
|
||||||
|
DOKANY("Dokany");
|
||||||
|
|
||||||
|
private String displayName;
|
||||||
|
|
||||||
|
VolumeImpl(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@DisplayName("Environment Variables Test")
|
||||||
|
class EnvironmentTest {
|
||||||
|
|
||||||
|
private Environment env;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void init() {
|
||||||
|
System.setProperty("user.home", "/home/testuser");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void initEach() {
|
||||||
|
env = new Environment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cryptomator.settingsPath=~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json")
|
||||||
|
public void testSettingsPath() {
|
||||||
|
System.setProperty("cryptomator.settingsPath", "~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json");
|
||||||
|
|
||||||
|
List<Path> result = env.getSettingsPath().collect(Collectors.toList());
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/settings.json"),
|
||||||
|
Paths.get("/home/testuser/.Cryptomator/settings.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cryptomator.ipcPortPath=~/.config/Cryptomator/ipcPort.bin:~/.Cryptomator/ipcPort.bin")
|
||||||
|
public void testIpcPortPath() {
|
||||||
|
System.setProperty("cryptomator.ipcPortPath", "~/.config/Cryptomator/ipcPort.bin:~/.Cryptomator/ipcPort.bin");
|
||||||
|
|
||||||
|
List<Path> result = env.getIpcPortPath().collect(Collectors.toList());
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(2));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/.config/Cryptomator/ipcPort.bin"),
|
||||||
|
Paths.get("/home/testuser/.Cryptomator/ipcPort.bin")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cryptomator.keychainPath=~/AppData/Roaming/Cryptomator/keychain.json")
|
||||||
|
public void testKeychainPath() {
|
||||||
|
System.setProperty("cryptomator.keychainPath", "~/AppData/Roaming/Cryptomator/keychain.json");
|
||||||
|
|
||||||
|
List<Path> result = env.getKeychainPath().collect(Collectors.toList());
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(1));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/AppData/Roaming/Cryptomator/keychain.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cryptomator.logDir=/foo/bar")
|
||||||
|
public void testAbsoluteLogDir() {
|
||||||
|
System.setProperty("cryptomator.logDir", "/foo/bar");
|
||||||
|
|
||||||
|
Optional<Path> logDir = env.getLogDir();
|
||||||
|
|
||||||
|
Assertions.assertTrue(logDir.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("cryptomator.logDir=~/foo/bar")
|
||||||
|
public void testRelativeLogDir() {
|
||||||
|
System.setProperty("cryptomator.logDir", "~/foo/bar");
|
||||||
|
|
||||||
|
Optional<Path> logDir = env.getLogDir();
|
||||||
|
|
||||||
|
Assertions.assertTrue(logDir.isPresent());
|
||||||
|
Assertions.assertEquals(Paths.get("/home/testuser/foo/bar"), logDir.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Path Lists")
|
||||||
|
class SettingsPath {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("test.path.property=")
|
||||||
|
public void testEmptyList() {
|
||||||
|
System.setProperty("test.path.property", "");
|
||||||
|
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("test.path.property=/foo/bar/test")
|
||||||
|
public void testSingleAbsolutePath() {
|
||||||
|
System.setProperty("test.path.property", "/foo/bar/test");
|
||||||
|
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(1));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasItem(Paths.get("/foo/bar/test")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("test.path.property=~/test")
|
||||||
|
public void testSingleHomeRelativePath() {
|
||||||
|
System.setProperty("test.path.property", "~/test");
|
||||||
|
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(1));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasItem(Paths.get("/home/testuser/test")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("test.path.property=~/test:~/test2:/foo/bar/test")
|
||||||
|
public void testMultiplePaths() {
|
||||||
|
System.setProperty("test.path.property", "~/test:~/test2:/foo/bar/test");
|
||||||
|
List<Path> result = env.getPaths("test.path.property").collect(Collectors.toList());
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(result, Matchers.hasSize(3));
|
||||||
|
MatcherAssert.assertThat(result, Matchers.contains(Paths.get("/home/testuser/test"),
|
||||||
|
Paths.get("/home/testuser/test2"),
|
||||||
|
Paths.get("/foo/bar/test")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import java.util.Comparator;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class SemVerComparatorTest {
|
public class SemVerComparatorTest {
|
||||||
|
|
||||||
@@ -21,38 +21,38 @@ public class SemVerComparatorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compareEqualVersions() {
|
public void compareEqualVersions() {
|
||||||
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4")));
|
Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4")));
|
||||||
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha")));
|
Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha")));
|
||||||
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4+20170101", "1.23.4+20171231")));
|
Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4+20170101", "1.23.4+20171231")));
|
||||||
Assert.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha+20170101", "1.23.4-alpha+20171231")));
|
Assertions.assertEquals(0, Integer.signum(semVerComparator.compare("1.23.4-alpha+20170101", "1.23.4-alpha+20171231")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// newer versions in first argument
|
// newer versions in first argument
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compareHigherToLowerVersions() {
|
public void compareHigherToLowerVersions() {
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-SNAPSHOT")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-SNAPSHOT")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-56.78")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4-56.78")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-beta", "1.23.4-alpha")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-beta", "1.23.4-alpha")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-alpha.1", "1.23.4-alpha")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-alpha.1", "1.23.4-alpha")));
|
||||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-56.79", "1.23.4-56.78")));
|
Assertions.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4-56.79", "1.23.4-56.78")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// newer versions in second argument
|
// newer versions in second argument
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compareLowerToHigherVersions() {
|
public void compareLowerToHigherVersions() {
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-SNAPSHOT", "1.23.4")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-SNAPSHOT", "1.23.4")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-beta")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-beta")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha.1")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-alpha", "1.23.4-alpha.1")));
|
||||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4-56.79")));
|
Assertions.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4-56.78", "1.23.4-56.79")));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class SettingsJsonAdapterTest {
|
public class SettingsJsonAdapterTest {
|
||||||
|
|
||||||
@@ -21,17 +21,17 @@ public class SettingsJsonAdapterTest {
|
|||||||
String json = "{\"directories\": [" + vault1Json + "," + vault2Json + "]," //
|
String json = "{\"directories\": [" + vault1Json + "," + vault2Json + "]," //
|
||||||
+ "\"checkForUpdatesEnabled\": true,"//
|
+ "\"checkForUpdatesEnabled\": true,"//
|
||||||
+ "\"port\": 8080,"//
|
+ "\"port\": 8080,"//
|
||||||
+ "\"useIpv6\": true,"//
|
+ "\"numTrayNotifications\": 42,"//
|
||||||
+ "\"numTrayNotifications\": 42}";
|
+ "\"preferredVolumeImpl\": \"FUSE\"}";
|
||||||
|
|
||||||
Settings settings = adapter.fromJson(json);
|
Settings settings = adapter.fromJson(json);
|
||||||
|
|
||||||
Assert.assertTrue(settings.checkForUpdates().get());
|
Assertions.assertTrue(settings.checkForUpdates().get());
|
||||||
Assert.assertEquals(2, settings.getDirectories().size());
|
Assertions.assertEquals(2, settings.getDirectories().size());
|
||||||
Assert.assertEquals(8080, settings.port().get());
|
Assertions.assertEquals(8080, settings.port().get());
|
||||||
// Assert.assertTrue(settings.useIpv6().get()); temporarily ignored
|
Assertions.assertEquals(42, settings.numTrayNotifications().get());
|
||||||
Assert.assertEquals(42, settings.numTrayNotifications().get());
|
Assertions.assertEquals("dav", settings.preferredGvfsScheme().get());
|
||||||
Assert.assertEquals("dav", settings.preferredGvfsScheme().get());
|
Assertions.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
public class SettingsTest {
|
public class SettingsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -5,29 +5,29 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
|
|
||||||
public class VaultSettingsJsonAdapterTest {
|
public class VaultSettingsJsonAdapterTest {
|
||||||
|
|
||||||
private final VaultSettingsJsonAdapter adapter = new VaultSettingsJsonAdapter();
|
private final VaultSettingsJsonAdapter adapter = new VaultSettingsJsonAdapter();
|
||||||
|
|
||||||
@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}";
|
String json = "{\"id\": \"foo\", \"path\": \"/foo/bar\", \"mountName\": \"test\", \"winDriveLetter\": \"X\", \"shouldBeIgnored\": true, \"individualMountPath\": \"/home/test/crypto\"}";
|
||||||
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
JsonReader jsonReader = new JsonReader(new StringReader(json));
|
||||||
|
|
||||||
VaultSettings vaultSettings = adapter.read(jsonReader);
|
VaultSettings vaultSettings = adapter.read(jsonReader);
|
||||||
Assert.assertEquals("foo", vaultSettings.getId());
|
Assertions.assertEquals("foo", vaultSettings.getId());
|
||||||
Assert.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
|
Assertions.assertEquals(Paths.get("/foo/bar"), vaultSettings.path().get());
|
||||||
Assert.assertEquals("test", vaultSettings.mountName().get());
|
Assertions.assertEquals("test", vaultSettings.mountName().get());
|
||||||
Assert.assertEquals("X", vaultSettings.winDriveLetter().get());
|
Assertions.assertEquals("X", vaultSettings.winDriveLetter().get());
|
||||||
|
Assertions.assertEquals("/home/test/crypto", vaultSettings.individualMountPath().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class VaultSettingsTest {
|
public class VaultSettingsTest {
|
||||||
|
|
||||||
|
|||||||
1
main/jacoco-report/.gitignore
vendored
1
main/jacoco-report/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
/target/
|
|
||||||
@@ -1,93 +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.3.0</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>jacoco-report</artifactId>
|
|
||||||
<name>Cryptomator Code Coverage Report</name>
|
|
||||||
<packaging>pom</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- all modules containing unit tests: -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>commons</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>keychain</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>ui</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>launcher</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<!-- conflict with codacy-coverage-reporter -->
|
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- binary dependency used during build -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.codacy</groupId>
|
|
||||||
<artifactId>codacy-coverage-reporter</artifactId>
|
|
||||||
<version>1.0.13</version>
|
|
||||||
<classifier>assembly</classifier>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>report-aggregate</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>report-aggregate</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
|
||||||
<version>1.5.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>java</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>com.codacy.CodacyCoverageReporter</mainClass>
|
|
||||||
<arguments>
|
|
||||||
<argument>-l</argument>
|
|
||||||
<argument>Java</argument>
|
|
||||||
<argument>-r</argument>
|
|
||||||
<argument>${project.build.directory}/site/jacoco-aggregate/jacoco.xml</argument>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
|
<?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="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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.4.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>keychain</artifactId>
|
<artifactId>keychain</artifactId>
|
||||||
<name>System Keychain Access</name>
|
<name>System Keychain Access</name>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>commons</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
|||||||
@@ -8,17 +8,27 @@ package org.cryptomator.keychain;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.cryptomator.jni.JniModule;
|
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.ElementsIntoSet;
|
import dagger.multibindings.ElementsIntoSet;
|
||||||
|
import org.cryptomator.jni.JniFunctions;
|
||||||
|
import org.cryptomator.jni.MacFunctions;
|
||||||
|
import org.cryptomator.jni.WinFunctions;
|
||||||
|
|
||||||
@Module(includes = {JniModule.class})
|
@Module
|
||||||
public class KeychainModule {
|
public class KeychainModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
Optional<MacFunctions> provideOptionalMacFunctions() {
|
||||||
|
return JniFunctions.macFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
Optional<WinFunctions> provideOptionalWinFunctions() {
|
||||||
|
return JniFunctions.winFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@ElementsIntoSet
|
@ElementsIntoSet
|
||||||
Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain) {
|
Set<KeychainAccessStrategy> provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsProtectedKeychainAccess winKeychain) {
|
||||||
|
|||||||
@@ -15,35 +15,35 @@ import org.cryptomator.jni.MacKeychainAccess;
|
|||||||
|
|
||||||
class MacSystemKeychainAccess implements KeychainAccessStrategy {
|
class MacSystemKeychainAccess implements KeychainAccessStrategy {
|
||||||
|
|
||||||
private final MacKeychainAccess keychain;
|
private final Optional<MacFunctions> macFunctions;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MacSystemKeychainAccess(Optional<MacFunctions> macFunctions) {
|
public MacSystemKeychainAccess(Optional<MacFunctions> macFunctions) {
|
||||||
if (macFunctions.isPresent()) {
|
this.macFunctions = macFunctions;
|
||||||
this.keychain = macFunctions.get().keychainAccess();
|
}
|
||||||
} else {
|
|
||||||
this.keychain = null;
|
private MacKeychainAccess keychain() {
|
||||||
}
|
return macFunctions.orElseThrow(IllegalStateException::new).keychainAccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePassphrase(String key, CharSequence passphrase) {
|
public void storePassphrase(String key, CharSequence passphrase) {
|
||||||
keychain.storePassword(key, passphrase);
|
keychain().storePassword(key, passphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char[] loadPassphrase(String key) {
|
public char[] loadPassphrase(String key) {
|
||||||
return keychain.loadPassword(key);
|
return keychain().loadPassword(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return SystemUtils.IS_OS_MAC_OSX && keychain != null;
|
return SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deletePassphrase(String key) {
|
public void deletePassphrase(String key) {
|
||||||
keychain.deletePassword(key);
|
keychain().deletePassword(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,38 +5,6 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.keychain;
|
package org.cryptomator.keychain;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.UncheckedIOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.CharBuffer;
|
|
||||||
import java.nio.file.FileSystems;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.NoSuchFileException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.jni.WinDataProtection;
|
|
||||||
import org.cryptomator.jni.WinFunctions;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
@@ -49,6 +17,38 @@ import com.google.gson.JsonSerializationContext;
|
|||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.jni.WinDataProtection;
|
||||||
|
import org.cryptomator.jni.WinFunctions;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
||||||
|
|
||||||
@@ -57,29 +57,18 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
.registerTypeHierarchyAdapter(byte[].class, new ByteArrayJsonAdapter()) //
|
.registerTypeHierarchyAdapter(byte[].class, new ByteArrayJsonAdapter()) //
|
||||||
.disableHtmlEscaping().create();
|
.disableHtmlEscaping().create();
|
||||||
|
|
||||||
private final WinDataProtection dataProtection;
|
private final Optional<WinFunctions> winFunctions;
|
||||||
private final Path keychainPath;
|
private final List<Path> keychainPaths;
|
||||||
private Map<String, KeychainEntry> keychainEntries;
|
private Map<String, KeychainEntry> keychainEntries;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WindowsProtectedKeychainAccess(Optional<WinFunctions> winFunctions) {
|
public WindowsProtectedKeychainAccess(Optional<WinFunctions> winFunctions, Environment environment) {
|
||||||
if (winFunctions.isPresent()) {
|
this.winFunctions = winFunctions;
|
||||||
this.dataProtection = winFunctions.get().dataProtection();
|
this.keychainPaths = environment.getKeychainPath().collect(Collectors.toList());
|
||||||
} else {
|
}
|
||||||
this.dataProtection = null;
|
|
||||||
}
|
private WinDataProtection dataProtection() {
|
||||||
String keychainPathProperty = System.getProperty("cryptomator.keychainPath");
|
return winFunctions.orElseThrow(IllegalStateException::new).dataProtection();
|
||||||
if (dataProtection != null && keychainPathProperty == null) {
|
|
||||||
LOG.warn("Windows DataProtection module loaded, but no cryptomator.keychainPath property found.");
|
|
||||||
}
|
|
||||||
if (keychainPathProperty != null) {
|
|
||||||
if (keychainPathProperty.startsWith("~/")) {
|
|
||||||
keychainPathProperty = SystemUtils.USER_HOME + keychainPathProperty.substring(1);
|
|
||||||
}
|
|
||||||
this.keychainPath = FileSystems.getDefault().getPath(keychainPathProperty);
|
|
||||||
} else {
|
|
||||||
this.keychainPath = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,7 +79,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
buf.get(cleartext);
|
buf.get(cleartext);
|
||||||
KeychainEntry entry = new KeychainEntry();
|
KeychainEntry entry = new KeychainEntry();
|
||||||
entry.salt = generateSalt();
|
entry.salt = generateSalt();
|
||||||
entry.ciphertext = dataProtection.protect(cleartext, entry.salt);
|
entry.ciphertext = dataProtection().protect(cleartext, entry.salt);
|
||||||
Arrays.fill(buf.array(), (byte) 0x00);
|
Arrays.fill(buf.array(), (byte) 0x00);
|
||||||
Arrays.fill(cleartext, (byte) 0x00);
|
Arrays.fill(cleartext, (byte) 0x00);
|
||||||
keychainEntries.put(key, entry);
|
keychainEntries.put(key, entry);
|
||||||
@@ -104,7 +93,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
byte[] cleartext = dataProtection.unprotect(entry.ciphertext, entry.salt);
|
byte[] cleartext = dataProtection().unprotect(entry.ciphertext, entry.salt);
|
||||||
if (cleartext == null) {
|
if (cleartext == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -125,7 +114,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return SystemUtils.IS_OS_WINDOWS && dataProtection != null && keychainPath != null;
|
return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && !keychainPaths.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] generateSalt() {
|
private byte[] generateSalt() {
|
||||||
@@ -139,30 +128,44 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
|
|
||||||
private void loadKeychainEntriesIfNeeded() {
|
private void loadKeychainEntriesIfNeeded() {
|
||||||
if (keychainEntries == null) {
|
if (keychainEntries == null) {
|
||||||
loadKeychainEntries();
|
for (Path keychainPath : keychainPaths) {
|
||||||
}
|
Optional<Map<String, KeychainEntry>> keychain = loadKeychainEntries(keychainPath);
|
||||||
assert keychainEntries != null;
|
if (keychain.isPresent()) {
|
||||||
}
|
keychainEntries = keychain.get();
|
||||||
|
break;
|
||||||
private void loadKeychainEntries() {
|
}
|
||||||
Type type = new TypeToken<Map<String, KeychainEntry>>() {
|
}
|
||||||
}.getType();
|
|
||||||
try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); //
|
|
||||||
Reader reader = new InputStreamReader(in, UTF_8)) {
|
|
||||||
keychainEntries = GSON.fromJson(reader, type);
|
|
||||||
} catch (JsonParseException | NoSuchFileException e) {
|
|
||||||
LOG.info("Creating new keychain at path {}", keychainPath);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e);
|
|
||||||
}
|
}
|
||||||
if (keychainEntries == null) {
|
if (keychainEntries == null) {
|
||||||
|
LOG.info("Unable to load existing keychain file, creating new keychain.");
|
||||||
keychainEntries = new HashMap<>();
|
keychainEntries = new HashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<Map<String, KeychainEntry>> loadKeychainEntries(Path keychainPath) {
|
||||||
|
LOG.debug("Attempting to load keychain from {}", keychainPath);
|
||||||
|
Type type = new TypeToken<Map<String, KeychainEntry>>() {
|
||||||
|
}.getType();
|
||||||
|
try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); //
|
||||||
|
Reader reader = new InputStreamReader(in, UTF_8)) {
|
||||||
|
return Optional.of(GSON.fromJson(reader, type));
|
||||||
|
} catch (NoSuchFileException | JsonParseException e) {
|
||||||
|
return Optional.empty();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void saveKeychainEntries() {
|
private void saveKeychainEntries() {
|
||||||
|
if (keychainPaths.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Can't save keychain if no keychain path is specified.");
|
||||||
|
}
|
||||||
|
saveKeychainEntries(keychainPaths.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveKeychainEntries(Path keychainPath) {
|
||||||
try (OutputStream out = Files.newOutputStream(keychainPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
try (OutputStream out = Files.newOutputStream(keychainPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); //
|
||||||
Writer writer = new OutputStreamWriter(out, UTF_8)) {
|
Writer writer = new OutputStreamWriter(out, UTF_8)) {
|
||||||
GSON.toJson(keychainEntries, writer);
|
GSON.toJson(keychainEntries, writer);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e);
|
throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e);
|
||||||
@@ -170,6 +173,7 @@ class WindowsProtectedKeychainAccess implements KeychainAccessStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class KeychainEntry {
|
private static class KeychainEntry {
|
||||||
|
|
||||||
@SerializedName("ciphertext")
|
@SerializedName("ciphertext")
|
||||||
byte[] ciphertext;
|
byte[] ciphertext;
|
||||||
@SerializedName("salt")
|
@SerializedName("salt")
|
||||||
|
|||||||
@@ -5,18 +5,20 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.keychain;
|
package org.cryptomator.keychain;
|
||||||
|
|
||||||
import java.util.Optional;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import java.util.Optional;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class KeychainModuleTest {
|
public class KeychainModuleTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetKeychain() {
|
public void testGetKeychain() {
|
||||||
Optional<KeychainAccess> keychainAccess = DaggerTestKeychainComponent.builder().jniModule(new TestJniModule()).keychainModule(new TestKeychainModule()).build().keychainAccess();
|
Optional<KeychainAccess> keychainAccess = DaggerTestKeychainComponent.builder().keychainModule(new TestKeychainModule()).build().keychainAccess();
|
||||||
Assert.assertTrue(keychainAccess.isPresent());
|
Assertions.assertTrue(keychainAccess.isPresent());
|
||||||
Assert.assertTrue(keychainAccess.get() instanceof MapKeychainAccess);
|
Assertions.assertTrue(keychainAccess.get() instanceof MapKeychainAccess);
|
||||||
|
keychainAccess.get().storePassphrase("test", "asd");
|
||||||
|
Assertions.assertArrayEquals("asd".toCharArray(), keychainAccess.get().loadPassphrase("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.keychain;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.cryptomator.jni.JniModule;
|
|
||||||
import org.cryptomator.jni.MacFunctions;
|
|
||||||
import org.cryptomator.jni.WinFunctions;
|
|
||||||
|
|
||||||
import dagger.Lazy;
|
|
||||||
|
|
||||||
public class TestJniModule extends JniModule {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<WinFunctions> winFunctions(Lazy<WinFunctions> winFunction) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<MacFunctions> macFunctions(Lazy<MacFunctions> winFunction) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,11 +5,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.keychain;
|
package org.cryptomator.keychain;
|
||||||
|
|
||||||
import java.util.Optional;
|
import dagger.Component;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Optional;
|
||||||
import dagger.Component;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Component(modules = KeychainModule.class)
|
@Component(modules = KeychainModule.class)
|
||||||
|
|||||||
@@ -5,46 +5,36 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.keychain;
|
package org.cryptomator.keychain;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.cryptomator.common.Environment;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.cryptomator.jni.WinDataProtection;
|
import org.cryptomator.jni.WinDataProtection;
|
||||||
import org.cryptomator.jni.WinFunctions;
|
import org.cryptomator.jni.WinFunctions;
|
||||||
import org.junit.After;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.Assert;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.Before;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.Rule;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class WindowsProtectedKeychainAccessTest {
|
public class WindowsProtectedKeychainAccessTest {
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final ExpectedException thrown = ExpectedException.none();
|
|
||||||
|
|
||||||
private Path tmpFile;
|
|
||||||
private WindowsProtectedKeychainAccess keychain;
|
private WindowsProtectedKeychainAccess keychain;
|
||||||
|
|
||||||
@Before
|
@BeforeEach
|
||||||
public void setup() throws IOException, ReflectiveOperationException {
|
public void setup(@TempDir Path tempDir) {
|
||||||
tmpFile = Files.createTempFile("unit-tests", ".tmp");
|
Path keychainPath = tempDir.resolve("keychainfile.tmp");
|
||||||
System.setProperty("cryptomator.keychainPath", tmpFile.toAbsolutePath().normalize().toString());
|
Environment env = Mockito.mock(Environment.class);
|
||||||
|
Mockito.when(env.getKeychainPath()).thenReturn(Stream.of(keychainPath));
|
||||||
WinFunctions winFunctions = Mockito.mock(WinFunctions.class);
|
WinFunctions winFunctions = Mockito.mock(WinFunctions.class);
|
||||||
WinDataProtection winDataProtection = Mockito.mock(WinDataProtection.class);
|
WinDataProtection winDataProtection = Mockito.mock(WinDataProtection.class);
|
||||||
Mockito.when(winFunctions.dataProtection()).thenReturn(winDataProtection);
|
Mockito.when(winFunctions.dataProtection()).thenReturn(winDataProtection);
|
||||||
Answer<byte[]> answerReturningFirstArg = invocation -> ((byte[]) invocation.getArgument(0)).clone();
|
Answer<byte[]> answerReturningFirstArg = invocation -> ((byte[]) invocation.getArgument(0)).clone();
|
||||||
Mockito.when(winDataProtection.protect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg);
|
Mockito.when(winDataProtection.protect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg);
|
||||||
Mockito.when(winDataProtection.unprotect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg);
|
Mockito.when(winDataProtection.unprotect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg);
|
||||||
keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions));
|
keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions), env);
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void teardown() throws IOException {
|
|
||||||
Files.deleteIfExists(tmpFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -55,11 +45,12 @@ public class WindowsProtectedKeychainAccessTest {
|
|||||||
keychain.storePassphrase("myOtherPassword", storedPw2);
|
keychain.storePassphrase("myOtherPassword", storedPw2);
|
||||||
String loadedPw1 = new String(keychain.loadPassphrase("myPassword"));
|
String loadedPw1 = new String(keychain.loadPassphrase("myPassword"));
|
||||||
String loadedPw2 = new String(keychain.loadPassphrase("myOtherPassword"));
|
String loadedPw2 = new String(keychain.loadPassphrase("myOtherPassword"));
|
||||||
Assert.assertEquals(storedPw1, loadedPw1);
|
Assertions.assertEquals(storedPw1, loadedPw1);
|
||||||
Assert.assertEquals(storedPw2, loadedPw2);
|
Assertions.assertEquals(storedPw2, loadedPw2);
|
||||||
keychain.deletePassphrase("myPassword");
|
keychain.deletePassphrase("myPassword");
|
||||||
Assert.assertNull(keychain.loadPassphrase("myPassword"));
|
Assertions.assertNull(keychain.loadPassphrase("myPassword"));
|
||||||
Assert.assertNull(keychain.loadPassphrase("nonExistingPassword"));
|
Assertions.assertNotNull(keychain.loadPassphrase("myOtherPassword"));
|
||||||
|
Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.4.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>launcher</artifactId>
|
<artifactId>launcher</artifactId>
|
||||||
<name>Cryptomator Launcher</name>
|
<name>Cryptomator Launcher</name>
|
||||||
|
|||||||
@@ -1,20 +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.launcher;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class ApplicationVersion {
|
|
||||||
|
|
||||||
public static String orElse(String other) {
|
|
||||||
return get().orElse(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<String> get() {
|
|
||||||
return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,55 +5,115 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
import java.io.IOException;
|
import javafx.application.Application;
|
||||||
import java.nio.file.Path;
|
import javafx.stage.Stage;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.logging.DebugMode;
|
||||||
|
import org.cryptomator.logging.LoggerConfiguration;
|
||||||
|
import org.cryptomator.ui.controllers.MainController;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
public class Cryptomator {
|
public class Cryptomator {
|
||||||
|
|
||||||
|
// DaggerCryptomatorComponent gets generated by Dagger.
|
||||||
|
// Run Maven and include target/generated-sources/annotations in your IDE.
|
||||||
|
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||||
static final BlockingQueue<Path> FILE_OPEN_REQUESTS = new ArrayBlockingQueue<>(10);
|
|
||||||
|
private final LoggerConfiguration logConfig;
|
||||||
|
private final DebugMode debugMode;
|
||||||
|
private final IpcFactory ipcFactory;
|
||||||
|
private final Optional<String> applicationVersion;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, IpcFactory ipcFactory, @Named("applicationVersion") Optional<String> applicationVersion) {
|
||||||
|
this.logConfig = logConfig;
|
||||||
|
this.debugMode = debugMode;
|
||||||
|
this.ipcFactory = ipcFactory;
|
||||||
|
this.applicationVersion = applicationVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
LOG.info("Starting Cryptomator {} on {} {} ({})", ApplicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
|
||||||
|
System.exit(exitCode); // end remaining non-daemon threads.
|
||||||
|
}
|
||||||
|
|
||||||
FileOpenRequestHandler fileOpenRequestHandler = new FileOpenRequestHandler(FILE_OPEN_REQUESTS);
|
/**
|
||||||
try (InterProcessCommunicator communicator = InterProcessCommunicator.start(new IpcProtocolImpl(fileOpenRequestHandler))) {
|
* Main entry point of the application launcher.
|
||||||
if (communicator.isServer()) {
|
* @param args The arguments passed to this program via {@link #main(String[])}.
|
||||||
fileOpenRequestHandler.handleLaunchArgs(args);
|
* @return Nonzero exit code in case of an error.
|
||||||
CleanShutdownPerformer.registerShutdownHook();
|
*/
|
||||||
Application.launch(MainApplication.class, args);
|
private int run(String[] args) {
|
||||||
|
logConfig.init();
|
||||||
|
LOG.info("Starting Cryptomator {} on {} {} ({})", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||||
|
debugMode.initialize();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
||||||
|
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
|
||||||
|
*/
|
||||||
|
try (IpcFactory.IpcEndpoint endpoint = ipcFactory.create()) {
|
||||||
|
endpoint.getRemote().handleLaunchArgs(args); // if we are the server, getRemote() returns self.
|
||||||
|
if (endpoint.isConnectedToRemote()) {
|
||||||
|
LOG.info("Found running application instance. Shutting down...");
|
||||||
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
communicator.handleLaunchArgs(args);
|
LOG.debug("Did not find running application instance. Launching GUI...");
|
||||||
LOG.info("Found running application instance. Shutting down.");
|
return runGuiApplication();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to initiate inter-process communication.", e);
|
LOG.error("Failed to initiate inter-process communication.", e);
|
||||||
|
return runGuiApplication();
|
||||||
}
|
}
|
||||||
System.exit(0); // end remaining non-daemon threads.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class IpcProtocolImpl implements InterProcessCommunicationProtocol {
|
/**
|
||||||
|
* Launches the JavaFX application and waits until shutdown is requested.
|
||||||
|
* @return Nonzero exit code in case of an error.
|
||||||
|
*/
|
||||||
|
private int runGuiApplication() {
|
||||||
|
try {
|
||||||
|
CleanShutdownPerformer.registerShutdownHook();
|
||||||
|
Application.launch(MainApp.class);
|
||||||
|
LOG.info("Shutting down...");
|
||||||
|
return 0;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOG.error("Terminating due to error", e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final FileOpenRequestHandler fileOpenRequestHandler;
|
|
||||||
|
|
||||||
// TODO: inject?
|
// We need a separate FX Application class, until we can use the module system. See https://stackoverflow.com/q/54756176/4014509
|
||||||
public IpcProtocolImpl(FileOpenRequestHandler fileOpenRequestHandler) {
|
public static class MainApp extends Application {
|
||||||
this.fileOpenRequestHandler = fileOpenRequestHandler;
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
LOG.info("JavaFX application started.");
|
||||||
|
primaryStage.setMinWidth(652.0);
|
||||||
|
primaryStage.setMinHeight(440.0);
|
||||||
|
|
||||||
|
FxApplicationComponent fxApplicationComponent = CRYPTOMATOR_COMPONENT.fxApplicationComponent() //
|
||||||
|
.fxApplication(this) //
|
||||||
|
.mainWindow(primaryStage) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MainController mainCtrl = fxApplicationComponent.fxmlLoader().load("/fxml/main.fxml");
|
||||||
|
mainCtrl.initStage(primaryStage);
|
||||||
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleLaunchArgs(String[] args) {
|
public void stop() {
|
||||||
LOG.info("Received launch args: {}", Arrays.stream(args).reduce((a, b) -> a + ", " + b).orElse(""));
|
LOG.info("JavaFX application stopped.");
|
||||||
fileOpenRequestHandler.handleLaunchArgs(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
import org.cryptomator.common.CommonsModule;
|
||||||
|
import org.cryptomator.logging.LoggerModule;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
|
||||||
|
public interface CryptomatorComponent {
|
||||||
|
|
||||||
|
Cryptomator application();
|
||||||
|
|
||||||
|
FxApplicationComponent.Builder fxApplicationComponent();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.SettingsProvider;
|
||||||
|
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
class CryptomatorModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static Settings provideSettings(SettingsProvider settingsProvider) {
|
||||||
|
return settingsProvider.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("launchEventQueue")
|
||||||
|
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
||||||
|
return new ArrayBlockingQueue<>(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("applicationVersion")
|
||||||
|
static Optional<String> provideApplicationVersion() {
|
||||||
|
return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,29 +6,45 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.awt.desktop.OpenFilesEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.cryptomator.ui.util.EawtApplicationWrapper;
|
@Singleton
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
class FileOpenRequestHandler {
|
class FileOpenRequestHandler {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class);
|
||||||
private final BlockingQueue<Path> fileOpenRequests;
|
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||||
|
|
||||||
public FileOpenRequestHandler(BlockingQueue<Path> fileOpenRequests) {
|
@Inject
|
||||||
this.fileOpenRequests = fileOpenRequests;
|
public FileOpenRequestHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
||||||
EawtApplicationWrapper.getApplication().ifPresent(app -> {
|
this.launchEventQueue = launchEventQueue;
|
||||||
app.setOpenFileHandler(files -> {
|
try {
|
||||||
files.stream().map(File::toPath).forEach(fileOpenRequests::add);
|
Desktop.getDesktop().setOpenFileHandler(this::openFiles);
|
||||||
});
|
} catch (UnsupportedOperationException e) {
|
||||||
});
|
LOG.info("Unable to setOpenFileHandler, probably not supported on this OS.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openFiles(final OpenFilesEvent evt) {
|
||||||
|
Stream<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath);
|
||||||
|
AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
|
||||||
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleLaunchArgs(String[] args) {
|
public void handleLaunchArgs(String[] args) {
|
||||||
@@ -37,19 +53,22 @@ class FileOpenRequestHandler {
|
|||||||
|
|
||||||
// visible for testing
|
// visible for testing
|
||||||
void handleLaunchArgs(FileSystem fs, String[] args) {
|
void handleLaunchArgs(FileSystem fs, String[] args) {
|
||||||
for (String arg : args) {
|
Stream<Path> pathsToOpen = Arrays.stream(args).map(str -> {
|
||||||
try {
|
try {
|
||||||
Path path = fs.getPath(arg);
|
return fs.getPath(str);
|
||||||
tryToEnqueueFileOpenRequest(path);
|
|
||||||
} catch (InvalidPathException e) {
|
} catch (InvalidPathException e) {
|
||||||
LOG.trace("{} not a valid path", arg);
|
LOG.trace("Argument not a valid path: {}", str);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}).filter(Objects::nonNull);
|
||||||
|
AppLaunchEvent launchEvent = new AppLaunchEvent(pathsToOpen);
|
||||||
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToEnqueueFileOpenRequest(Path path) {
|
|
||||||
if (!fileOpenRequests.offer(path)) {
|
private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) {
|
||||||
LOG.warn("{} could not be enqueued for opening.", path);
|
if (!launchEventQueue.offer(launchEvent)) {
|
||||||
|
LOG.warn("Could not enqueue application launch event.", launchEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.launcher;
|
||||||
|
|
||||||
|
import dagger.BindsInstance;
|
||||||
|
import dagger.Subcomponent;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
import org.cryptomator.ui.controllers.ViewControllerLoader;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
@FxApplicationScoped
|
||||||
|
@Subcomponent(modules = FxApplicationModule.class)
|
||||||
|
interface FxApplicationComponent {
|
||||||
|
|
||||||
|
ViewControllerLoader fxmlLoader();
|
||||||
|
|
||||||
|
@Subcomponent.Builder
|
||||||
|
interface Builder {
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder fxApplication(Application application);
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder mainWindow(@Named("mainWindow") Stage mainWindow);
|
||||||
|
|
||||||
|
FxApplicationComponent build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.launcher;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
import org.cryptomator.ui.UiModule;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Module(includes = {UiModule.class})
|
||||||
|
class FxApplicationModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxApplicationScoped
|
||||||
|
@Named("shutdownTaskScheduler")
|
||||||
|
Consumer<Runnable> provideShutdownTaskScheduler() {
|
||||||
|
return CleanShutdownPerformer::scheduleShutdownTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,240 +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.launcher;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.rmi.ConnectException;
|
|
||||||
import java.rmi.NotBoundException;
|
|
||||||
import java.rmi.Remote;
|
|
||||||
import java.rmi.RemoteException;
|
|
||||||
import java.rmi.registry.LocateRegistry;
|
|
||||||
import java.rmi.registry.Registry;
|
|
||||||
import java.rmi.server.RMIClientSocketFactory;
|
|
||||||
import java.rmi.server.RMIServerSocketFactory;
|
|
||||||
import java.rmi.server.RMISocketFactory;
|
|
||||||
import java.rmi.server.UnicastRemoteObject;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.io.MoreFiles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First running application on a machine opens a server socket. Further processes will connect as clients.
|
|
||||||
*/
|
|
||||||
abstract class InterProcessCommunicator implements InterProcessCommunicationProtocol, Closeable {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(InterProcessCommunicator.class);
|
|
||||||
private static final String RMI_NAME = "Cryptomator";
|
|
||||||
|
|
||||||
public abstract boolean isServer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param endpoint The server-side communication endpoint.
|
|
||||||
* @return Either a client or a server communicator.
|
|
||||||
* @throws IOException In case of communication errors.
|
|
||||||
*/
|
|
||||||
public static InterProcessCommunicator start(InterProcessCommunicationProtocol endpoint) throws IOException {
|
|
||||||
return start(getIpcPortPath(), endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException {
|
|
||||||
System.setProperty("java.rmi.server.hostname", "localhost");
|
|
||||||
// try to connect to existing server:
|
|
||||||
int port = readPort(portFilePath);
|
|
||||||
LOG.debug("Connecting to running process on TCP port {}...", port);
|
|
||||||
try {
|
|
||||||
ClientCommunicator client = new ClientCommunicator(port);
|
|
||||||
LOG.trace("Connected to running process.");
|
|
||||||
return client;
|
|
||||||
} catch (ConnectException | NotBoundException e) {
|
|
||||||
LOG.debug("Did not find running process.");
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// spawn a new server:
|
|
||||||
LOG.trace("Spawning new server...");
|
|
||||||
ServerCommunicator server = new ServerCommunicator(endpoint);
|
|
||||||
writePort(portFilePath, server.getPort());
|
|
||||||
LOG.debug("Server listening on port {}.", server.getPort());
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path getIpcPortPath() {
|
|
||||||
final String settingsPathProperty = System.getProperty("cryptomator.ipcPortPath");
|
|
||||||
if (settingsPathProperty == null) {
|
|
||||||
LOG.warn("System property cryptomator.ipcPortPath not set.");
|
|
||||||
return Paths.get("ipcPort.tmp");
|
|
||||||
} else {
|
|
||||||
return Paths.get(replaceHomeDir(settingsPathProperty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String replaceHomeDir(String path) {
|
|
||||||
if (path.startsWith("~/")) {
|
|
||||||
return SystemUtils.USER_HOME + path.substring(1);
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ClientCommunicator extends InterProcessCommunicator {
|
|
||||||
|
|
||||||
private final IpcProtocolRemote remote;
|
|
||||||
|
|
||||||
private ClientCommunicator(int port) throws ConnectException, NotBoundException, RemoteException {
|
|
||||||
if (port == 0) {
|
|
||||||
throw new ConnectException("Can not connect to port 0.");
|
|
||||||
}
|
|
||||||
Registry registry = LocateRegistry.getRegistry("localhost", port);
|
|
||||||
this.remote = (IpcProtocolRemote) registry.lookup(RMI_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleLaunchArgs(String[] args) {
|
|
||||||
try {
|
|
||||||
remote.handleLaunchArgs(args);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isServer() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ServerCommunicator extends InterProcessCommunicator {
|
|
||||||
|
|
||||||
private final ServerSocket socket;
|
|
||||||
private final Registry registry;
|
|
||||||
private final IpcProtocolRemoteImpl remote;
|
|
||||||
|
|
||||||
private ServerCommunicator(InterProcessCommunicationProtocol delegate) throws IOException {
|
|
||||||
this.socket = new ServerSocket(0, Byte.MAX_VALUE, InetAddress.getByName("localhost"));
|
|
||||||
RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory();
|
|
||||||
SingletonServerSocketFactory ssf = new SingletonServerSocketFactory(socket);
|
|
||||||
this.registry = LocateRegistry.createRegistry(0, csf, ssf);
|
|
||||||
this.remote = new IpcProtocolRemoteImpl(delegate);
|
|
||||||
UnicastRemoteObject.exportObject(remote, 0);
|
|
||||||
registry.rebind(RMI_NAME, remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleLaunchArgs(String[] args) {
|
|
||||||
throw new UnsupportedOperationException("Server doesn't invoke methods.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isServer() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getPort() {
|
|
||||||
return socket.getLocalPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
try {
|
|
||||||
registry.unbind(RMI_NAME);
|
|
||||||
UnicastRemoteObject.unexportObject(remote, true);
|
|
||||||
socket.close();
|
|
||||||
LOG.debug("Server shut down.");
|
|
||||||
} catch (NotBoundException | IOException e) {
|
|
||||||
LOG.warn("Failed to close IPC Server.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static interface IpcProtocolRemote extends Remote {
|
|
||||||
void handleLaunchArgs(String[] args) throws RemoteException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class IpcProtocolRemoteImpl implements IpcProtocolRemote {
|
|
||||||
|
|
||||||
private final InterProcessCommunicationProtocol delegate;
|
|
||||||
|
|
||||||
protected IpcProtocolRemoteImpl(InterProcessCommunicationProtocol delegate) throws RemoteException {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleLaunchArgs(String[] args) {
|
|
||||||
delegate.handleLaunchArgs(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Always returns the same pre-constructed server socket.
|
|
||||||
*/
|
|
||||||
private static class SingletonServerSocketFactory implements RMIServerSocketFactory {
|
|
||||||
|
|
||||||
private final ServerSocket socket;
|
|
||||||
|
|
||||||
public SingletonServerSocketFactory(ServerSocket socket) {
|
|
||||||
this.socket = socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized ServerSocket createServerSocket(int port) throws IOException {
|
|
||||||
if (port != 0) {
|
|
||||||
throw new IllegalArgumentException("This factory doesn't support specific ports.");
|
|
||||||
}
|
|
||||||
return this.socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int readPort(Path path) throws IOException {
|
|
||||||
if (Files.notExists(path)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
|
|
||||||
try (ReadableByteChannel ch = Files.newByteChannel(path, StandardOpenOption.READ)) {
|
|
||||||
if (ch.read(buf) == Integer.BYTES) {
|
|
||||||
buf.flip();
|
|
||||||
return buf.getInt();
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writePort(Path path, int port) throws IOException {
|
|
||||||
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
|
|
||||||
buf.putInt(port);
|
|
||||||
buf.flip();
|
|
||||||
MoreFiles.createParentDirectories(path);
|
|
||||||
try (WritableByteChannel ch = Files.newByteChannel(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
|
||||||
if (ch.write(buf) != Integer.BYTES) {
|
|
||||||
throw new IOException("Did not write expected number of bytes.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.launcher;
|
||||||
|
|
||||||
|
import com.google.common.io.MoreFiles;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.rmi.NotBoundException;
|
||||||
|
import java.rmi.registry.LocateRegistry;
|
||||||
|
import java.rmi.registry.Registry;
|
||||||
|
import java.rmi.server.RMIClientSocketFactory;
|
||||||
|
import java.rmi.server.RMIServerSocketFactory;
|
||||||
|
import java.rmi.server.RMISocketFactory;
|
||||||
|
import java.rmi.server.UnicastRemoteObject;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First running application on a machine opens a server socket. Further processes will connect as clients.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class IpcFactory {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(IpcFactory.class);
|
||||||
|
private static final String RMI_NAME = "Cryptomator";
|
||||||
|
|
||||||
|
private final List<Path> portFilePaths;
|
||||||
|
private final IpcProtocolImpl ipcHandler;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public IpcFactory(Environment env, IpcProtocolImpl ipcHandler) {
|
||||||
|
this.portFilePaths = env.getIpcPortPath().collect(Collectors.toUnmodifiableList());
|
||||||
|
this.ipcHandler = ipcHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IpcEndpoint create() {
|
||||||
|
if (portFilePaths.isEmpty()) {
|
||||||
|
LOG.warn("No IPC port file path specified.");
|
||||||
|
return new SelfEndpoint(ipcHandler);
|
||||||
|
} else {
|
||||||
|
System.setProperty("java.rmi.server.hostname", "localhost");
|
||||||
|
return attemptClientConnection().or(this::createServerEndpoint).orElseGet(() -> new SelfEndpoint(ipcHandler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<IpcEndpoint> attemptClientConnection() {
|
||||||
|
for (Path portFilePath : portFilePaths) {
|
||||||
|
try {
|
||||||
|
int port = readPort(portFilePath);
|
||||||
|
LOG.debug("[Client] Connecting to port {}...", port);
|
||||||
|
Registry registry = LocateRegistry.getRegistry("localhost", port, new ClientSocketFactory());
|
||||||
|
IpcProtocol remoteInterface = (IpcProtocol) registry.lookup(RMI_NAME);
|
||||||
|
return Optional.of(new ClientEndpoint(remoteInterface));
|
||||||
|
} catch (NotBoundException | IOException e) {
|
||||||
|
LOG.debug("[Client] Failed to connect.");
|
||||||
|
// continue with next portFilePath...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readPort(Path portFilePath) throws IOException {
|
||||||
|
try (ReadableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.READ)) {
|
||||||
|
LOG.debug("[Client] Reading IPC port from {}", portFilePath);
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
|
||||||
|
if (ch.read(buf) == Integer.BYTES) {
|
||||||
|
buf.flip();
|
||||||
|
return buf.getInt();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Invalid IPC port file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<IpcEndpoint> createServerEndpoint() {
|
||||||
|
assert !portFilePaths.isEmpty();
|
||||||
|
Path portFilePath = portFilePaths.get(0);
|
||||||
|
try {
|
||||||
|
ServerSocket socket = new ServerSocket(0, Byte.MAX_VALUE, InetAddress.getByName("localhost"));
|
||||||
|
RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory();
|
||||||
|
SingletonServerSocketFactory ssf = new SingletonServerSocketFactory(socket);
|
||||||
|
Registry registry = LocateRegistry.createRegistry(0, csf, ssf);
|
||||||
|
UnicastRemoteObject.exportObject(ipcHandler, 0);
|
||||||
|
registry.rebind(RMI_NAME, ipcHandler);
|
||||||
|
writePort(portFilePath, socket.getLocalPort());
|
||||||
|
return Optional.of(new ServerEndpoint(ipcHandler, socket, registry, portFilePath));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("[Server] Failed to create IPC server.", e);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writePort(Path portFilePath, int port) throws IOException {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
|
||||||
|
buf.putInt(port);
|
||||||
|
buf.flip();
|
||||||
|
MoreFiles.createParentDirectories(portFilePath);
|
||||||
|
try (WritableByteChannel ch = Files.newByteChannel(portFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||||
|
if (ch.write(buf) != Integer.BYTES) {
|
||||||
|
throw new IOException("Did not write expected number of bytes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug("[Server] Wrote IPC port {} to {}", port, portFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IpcEndpoint extends Closeable {
|
||||||
|
|
||||||
|
boolean isConnectedToRemote();
|
||||||
|
|
||||||
|
IpcProtocol getRemote();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SelfEndpoint implements IpcEndpoint {
|
||||||
|
|
||||||
|
protected final IpcProtocol remoteObject;
|
||||||
|
|
||||||
|
SelfEndpoint(IpcProtocol remoteObject) {
|
||||||
|
this.remoteObject = remoteObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectedToRemote() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IpcProtocol getRemote() {
|
||||||
|
return remoteObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClientEndpoint implements IpcEndpoint {
|
||||||
|
|
||||||
|
private final IpcProtocol remoteInterface;
|
||||||
|
|
||||||
|
public ClientEndpoint(IpcProtocol remoteInterface) {
|
||||||
|
this.remoteInterface = remoteInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IpcProtocol getRemote() {
|
||||||
|
return remoteInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectedToRemote() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerEndpoint extends SelfEndpoint {
|
||||||
|
|
||||||
|
private final ServerSocket socket;
|
||||||
|
private final Registry registry;
|
||||||
|
private final Path portFilePath;
|
||||||
|
|
||||||
|
private ServerEndpoint(IpcProtocol remoteObject, ServerSocket socket, Registry registry, Path portFilePath) {
|
||||||
|
super(remoteObject);
|
||||||
|
this.socket = socket;
|
||||||
|
this.registry = registry;
|
||||||
|
this.portFilePath = portFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
registry.unbind(RMI_NAME);
|
||||||
|
UnicastRemoteObject.unexportObject(remoteObject, true);
|
||||||
|
socket.close();
|
||||||
|
Files.deleteIfExists(portFilePath);
|
||||||
|
LOG.debug("[Server] Shut down");
|
||||||
|
} catch (NotBoundException | IOException e) {
|
||||||
|
LOG.warn("[Server] Error shutting down:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always returns the same pre-constructed server socket.
|
||||||
|
*/
|
||||||
|
private static class SingletonServerSocketFactory implements RMIServerSocketFactory {
|
||||||
|
|
||||||
|
private final ServerSocket socket;
|
||||||
|
|
||||||
|
public SingletonServerSocketFactory(ServerSocket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized ServerSocket createServerSocket(int port) throws IOException {
|
||||||
|
if (port != 0) {
|
||||||
|
throw new IllegalArgumentException("This factory doesn't support specific ports.");
|
||||||
|
}
|
||||||
|
return this.socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates client sockets with short timeouts.
|
||||||
|
*/
|
||||||
|
private static class ClientSocketFactory implements RMIClientSocketFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port) throws IOException {
|
||||||
|
return new SocketWithFixedTimeout(host, port, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SocketWithFixedTimeout extends Socket {
|
||||||
|
|
||||||
|
public SocketWithFixedTimeout(String host, int port, int timeoutInMs) throws UnknownHostException, IOException {
|
||||||
|
super(host, port);
|
||||||
|
super.setSoTimeout(timeoutInMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setSoTimeout(int timeout) throws SocketException {
|
||||||
|
// do nothing, timeout is fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,11 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
public interface InterProcessCommunicationProtocol {
|
import java.rmi.Remote;
|
||||||
void handleLaunchArgs(String[] args);
|
import java.rmi.RemoteException;
|
||||||
|
|
||||||
|
interface IpcProtocol extends Remote {
|
||||||
|
|
||||||
|
void handleLaunchArgs(String[] args) throws RemoteException;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class IpcProtocolImpl implements IpcProtocol {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(IpcProtocolImpl.class);
|
||||||
|
|
||||||
|
private final FileOpenRequestHandler fileOpenRequestHandler;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public IpcProtocolImpl(FileOpenRequestHandler fileOpenRequestHandler) {
|
||||||
|
this.fileOpenRequestHandler = fileOpenRequestHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleLaunchArgs(String[] args) {
|
||||||
|
LOG.info("Received launch args: {}", Arrays.stream(args).reduce((a, b) -> a + ", " + b).orElse(""));
|
||||||
|
fileOpenRequestHandler.handleLaunchArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,23 +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.launcher;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.logging.DebugMode;
|
|
||||||
import org.cryptomator.ui.controllers.ViewControllerLoader;
|
|
||||||
|
|
||||||
import dagger.Component;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = LauncherModule.class)
|
|
||||||
interface LauncherComponent {
|
|
||||||
|
|
||||||
ViewControllerLoader fxmlLoader();
|
|
||||||
|
|
||||||
DebugMode debugMode();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +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.launcher;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.ui.UiModule;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@Module(includes = {UiModule.class})
|
|
||||||
class LauncherModule {
|
|
||||||
|
|
||||||
private final Application application;
|
|
||||||
private final Stage mainWindow;
|
|
||||||
|
|
||||||
public LauncherModule(Application application, Stage mainWindow) {
|
|
||||||
this.application = application;
|
|
||||||
this.mainWindow = mainWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Application provideApplication() {
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("applicationVersion")
|
|
||||||
Optional<String> provideApplicationVersion() {
|
|
||||||
return ApplicationVersion.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("mainWindow")
|
|
||||||
Stage provideMainWindow() {
|
|
||||||
return mainWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("fileOpenRequests")
|
|
||||||
BlockingQueue<Path> provideFileOpenRequests() {
|
|
||||||
return Cryptomator.FILE_OPEN_REQUESTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@Named("shutdownTaskScheduler")
|
|
||||||
Consumer<Runnable> provideShutdownTaskScheduler() {
|
|
||||||
return CleanShutdownPerformer::scheduleShutdownTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +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.launcher;
|
|
||||||
|
|
||||||
import org.cryptomator.ui.controllers.MainController;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
public class MainApplication extends Application {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
|
|
||||||
private Stage primaryStage;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start(Stage primaryStage) throws Exception {
|
|
||||||
LOG.info("JavaFX application started.");
|
|
||||||
this.primaryStage = primaryStage;
|
|
||||||
setupFXMLClassLoader();
|
|
||||||
|
|
||||||
LauncherModule launcherModule = new LauncherModule(this, primaryStage);
|
|
||||||
LauncherComponent launcherComponent = DaggerLauncherComponent.builder() //
|
|
||||||
.launcherModule(launcherModule) //
|
|
||||||
.build();
|
|
||||||
|
|
||||||
launcherComponent.debugMode().initialize();
|
|
||||||
|
|
||||||
MainController mainCtrl = launcherComponent.fxmlLoader().load("/fxml/main.fxml");
|
|
||||||
mainCtrl.initStage(primaryStage);
|
|
||||||
|
|
||||||
primaryStage.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() throws Exception {
|
|
||||||
assert primaryStage != null;
|
|
||||||
primaryStage.hide();
|
|
||||||
LOG.info("JavaFX application stopped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix discussed in https://github.com/cryptomator/cryptomator/pull/29
|
|
||||||
private void setupFXMLClassLoader() {
|
|
||||||
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
FXMLLoader.setDefaultClassLoader(contextClassLoader);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
/*
|
|
||||||
* This fixes a bug on OSX where the magic file open handler leads to no context class loader being set in the AppKit (event)
|
|
||||||
* thread if the application is not started opening a file.
|
|
||||||
*/
|
|
||||||
if (Thread.currentThread().getContextClassLoader() == null) {
|
|
||||||
Thread.currentThread().setContextClassLoader(contextClassLoader);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,77 +5,55 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.logging;
|
package org.cryptomator.logging;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.slf4j.ILoggerFactory;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
import ch.qos.logback.classic.Level;
|
||||||
import ch.qos.logback.classic.Logger;
|
import ch.qos.logback.classic.Logger;
|
||||||
import ch.qos.logback.classic.LoggerContext;
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class DebugMode {
|
public class DebugMode {
|
||||||
|
|
||||||
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DebugMode.class);
|
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DebugMode.class);
|
||||||
|
|
||||||
private static final Collection<LoggerUpgrade> LOGGER_UPGRADES = asList( //
|
|
||||||
loggerUpgrade(org.slf4j.Logger.ROOT_LOGGER_NAME, Level.INFO), //
|
|
||||||
loggerUpgrade("org.cryptomator", Level.TRACE), //
|
|
||||||
loggerUpgrade("org.eclipse.jetty.server.HttpChannel", Level.DEBUG) //
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final LoggerContext context;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DebugMode(Settings settings) {
|
public DebugMode(Settings settings, LoggerContext context) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
if (settings.debugMode().get()) {
|
setLogLevels(settings.debugMode().get());
|
||||||
enable();
|
settings.debugMode().addListener(this::logLevelChanged);
|
||||||
LOG.debug("Debug mode initialized");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enable() {
|
private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
|
||||||
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
|
setLogLevels(newValue);
|
||||||
if (loggerFactory instanceof LoggerContext) {
|
}
|
||||||
LoggerContext context = (LoggerContext) loggerFactory;
|
|
||||||
LOGGER_UPGRADES.forEach(loggerUpgrade -> loggerUpgrade.execute(context));
|
private void setLogLevels(boolean debugMode) {
|
||||||
|
if (debugMode) {
|
||||||
|
setLogLevels(LoggerModule.DEBUG_LOG_LEVELS);
|
||||||
|
LOG.debug("Debug mode enabled");
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("SLF4J not bound to Logback.");
|
LOG.debug("Debug mode disabled");
|
||||||
|
setLogLevels(LoggerModule.DEFAULT_LOG_LEVELS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LoggerUpgrade loggerUpgrade(String loggerName, Level minLevel) {
|
private void setLogLevels(Map<String, Level> logLevels) {
|
||||||
return new LoggerUpgrade(loggerName, minLevel);
|
for (Map.Entry<String, Level> loglevel : logLevels.entrySet()) {
|
||||||
}
|
Logger logger = context.getLogger(loglevel.getKey());
|
||||||
|
logger.setLevel(loglevel.getValue());
|
||||||
private static class LoggerUpgrade {
|
|
||||||
|
|
||||||
private final Level level;
|
|
||||||
private final String loggerName;
|
|
||||||
|
|
||||||
public LoggerUpgrade(String loggerName, Level minLevel) {
|
|
||||||
this.loggerName = loggerName;
|
|
||||||
this.level = minLevel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(LoggerContext context) {
|
|
||||||
Logger logger = context.getLogger(loggerName);
|
|
||||||
if (logger != null && logger.getEffectiveLevel().isGreaterOrEqual(level)) {
|
|
||||||
logger.setLevel(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package org.cryptomator.logging;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.Appender;
|
||||||
|
import ch.qos.logback.core.hook.DelayingShutdownHook;
|
||||||
|
import ch.qos.logback.core.util.Duration;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class LoggerConfiguration {
|
||||||
|
|
||||||
|
private static final double SHUTDOWN_DELAY_MS = 100;
|
||||||
|
|
||||||
|
private final LoggerContext context;
|
||||||
|
private final Environment environment;
|
||||||
|
private final Appender<ILoggingEvent> stdout;
|
||||||
|
private final Appender<ILoggingEvent> upgrade;
|
||||||
|
private final Appender<ILoggingEvent> file;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
LoggerConfiguration(LoggerContext context, //
|
||||||
|
Environment environment, //
|
||||||
|
@Named("stdoutAppender") Appender<ILoggingEvent> stdout, //
|
||||||
|
@Named("upgradeAppender") Appender<ILoggingEvent> upgrade, //
|
||||||
|
@Named("fileAppender") Appender<ILoggingEvent> file) {
|
||||||
|
this.context = context;
|
||||||
|
this.environment = environment;
|
||||||
|
this.stdout = stdout;
|
||||||
|
this.upgrade = upgrade;
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
if (environment.useCustomLogbackConfig()) {
|
||||||
|
Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||||
|
root.info("Using external logback configuration file.");
|
||||||
|
} else {
|
||||||
|
context.reset();
|
||||||
|
|
||||||
|
// configure loggers:
|
||||||
|
for (Map.Entry<String, Level> loglevel : LoggerModule.DEFAULT_LOG_LEVELS.entrySet()) {
|
||||||
|
Logger logger = context.getLogger(loglevel.getKey());
|
||||||
|
logger.setLevel(loglevel.getValue());
|
||||||
|
logger.setAdditive(false);
|
||||||
|
logger.addAppender(stdout);
|
||||||
|
logger.addAppender(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure upgrade logger:
|
||||||
|
Logger upgrades = context.getLogger("org.cryptomator.ui.model.upgrade");
|
||||||
|
upgrades.setLevel(Level.DEBUG);
|
||||||
|
upgrades.addAppender(stdout);
|
||||||
|
upgrades.addAppender(upgrade);
|
||||||
|
upgrades.setAdditive(false);
|
||||||
|
|
||||||
|
// add shutdown hook
|
||||||
|
DelayingShutdownHook shutdownHook = new DelayingShutdownHook();
|
||||||
|
shutdownHook.setContext(context);
|
||||||
|
shutdownHook.setDelay(Duration.buildByMilliseconds(SHUTDOWN_DELAY_MS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package org.cryptomator.logging;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.Appender;
|
||||||
|
import ch.qos.logback.core.ConsoleAppender;
|
||||||
|
import ch.qos.logback.core.FileAppender;
|
||||||
|
import ch.qos.logback.core.helpers.NOPAppender;
|
||||||
|
import ch.qos.logback.core.hook.DelayingShutdownHook;
|
||||||
|
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
|
||||||
|
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||||
|
import ch.qos.logback.core.util.Duration;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class LoggerModule {
|
||||||
|
|
||||||
|
private static final String UPGRADE_FILENAME = "upgrade.log";
|
||||||
|
private static final String LOGFILE_NAME = "cryptomator0.log";
|
||||||
|
private static final String LOGFILE_ROLLING_PATTERN = "cryptomator%i.log";
|
||||||
|
private static final int LOGFILE_ROLLING_MIN = 1;
|
||||||
|
private static final int LOGFILE_ROLLING_MAX = 9;
|
||||||
|
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
||||||
|
static final Map<String, Level> DEFAULT_LOG_LEVELS = Map.of( //
|
||||||
|
Logger.ROOT_LOGGER_NAME, Level.INFO, //
|
||||||
|
"org.cryptomator", Level.INFO //
|
||||||
|
);
|
||||||
|
static final Map<String, Level> DEBUG_LOG_LEVELS = Map.of( //
|
||||||
|
Logger.ROOT_LOGGER_NAME, Level.INFO, //
|
||||||
|
"org.cryptomator", Level.TRACE //
|
||||||
|
);
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static LoggerContext provideLoggerContext() {
|
||||||
|
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
|
||||||
|
if (loggerFactory instanceof LoggerContext) {
|
||||||
|
return (LoggerContext) loggerFactory;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("SLF4J not bound to Logback.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static PatternLayoutEncoder provideLayoutEncoder(LoggerContext context) {
|
||||||
|
PatternLayoutEncoder ple = new PatternLayoutEncoder();
|
||||||
|
ple.setPattern(LOG_PATTERN);
|
||||||
|
ple.setContext(context);
|
||||||
|
ple.start();
|
||||||
|
return ple;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("stdoutAppender")
|
||||||
|
static Appender<ILoggingEvent> provideStdoutAppender(LoggerContext context, PatternLayoutEncoder encoder) {
|
||||||
|
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
|
||||||
|
appender.setContext(context);
|
||||||
|
appender.setEncoder(encoder);
|
||||||
|
appender.start();
|
||||||
|
return appender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("fileAppender")
|
||||||
|
static Appender<ILoggingEvent> provideFileAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) {
|
||||||
|
if (environment.getLogDir().isPresent()) {
|
||||||
|
Path logDir = environment.getLogDir().get();
|
||||||
|
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
|
||||||
|
appender.setContext(context);
|
||||||
|
appender.setFile(logDir.resolve(LOGFILE_NAME).toString());
|
||||||
|
appender.setEncoder(encoder);
|
||||||
|
LaunchBasedTriggeringPolicy triggeringPolicy = new LaunchBasedTriggeringPolicy();
|
||||||
|
triggeringPolicy.setContext(context);
|
||||||
|
triggeringPolicy.start();
|
||||||
|
appender.setTriggeringPolicy(triggeringPolicy);
|
||||||
|
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
|
||||||
|
rollingPolicy.setContext(context);
|
||||||
|
rollingPolicy.setFileNamePattern(logDir.resolve(LOGFILE_ROLLING_PATTERN).toString());
|
||||||
|
rollingPolicy.setMinIndex(LOGFILE_ROLLING_MIN);
|
||||||
|
rollingPolicy.setMaxIndex(LOGFILE_ROLLING_MAX);
|
||||||
|
rollingPolicy.setParent(appender);
|
||||||
|
rollingPolicy.start();
|
||||||
|
appender.setRollingPolicy(rollingPolicy);
|
||||||
|
appender.start();
|
||||||
|
return appender;
|
||||||
|
} else {
|
||||||
|
NOPAppender appender = new NOPAppender<>();
|
||||||
|
appender.setContext(context);
|
||||||
|
return appender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("upgradeAppender")
|
||||||
|
static Appender<ILoggingEvent> provideUpgradeAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) {
|
||||||
|
if (environment.getLogDir().isPresent()) {
|
||||||
|
FileAppender<ILoggingEvent> appender = new FileAppender<>();
|
||||||
|
appender.setFile(environment.getLogDir().get().resolve(UPGRADE_FILENAME).toString());
|
||||||
|
appender.setContext(context);
|
||||||
|
appender.setEncoder(encoder);
|
||||||
|
appender.start();
|
||||||
|
return appender;
|
||||||
|
} else {
|
||||||
|
NOPAppender appender = new NOPAppender<>();
|
||||||
|
appender.setContext(context);
|
||||||
|
return appender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,73 +5,69 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Assumptions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
import java.util.List;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.junit.Assert;
|
import java.util.stream.Stream;
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
public class FileOpenRequestHandlerTest {
|
public class FileOpenRequestHandlerTest {
|
||||||
|
|
||||||
@Test
|
private FileOpenRequestHandler inTest;
|
||||||
public void testOpenArgsWithCorrectPaths() throws IOException {
|
private BlockingQueue<AppLaunchEvent> queue;
|
||||||
Path p1 = Mockito.mock(Path.class);
|
|
||||||
Path p2 = Mockito.mock(Path.class);
|
|
||||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
|
||||||
FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
|
|
||||||
BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
|
|
||||||
Mockito.when(p1.getFileSystem()).thenReturn(fs);
|
|
||||||
Mockito.when(p2.getFileSystem()).thenReturn(fs);
|
|
||||||
Mockito.when(fs.provider()).thenReturn(provider);
|
|
||||||
Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p1, p2);
|
|
||||||
Mockito.when(provider.readAttributes(Mockito.any(), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
|
|
||||||
Mockito.when(attrs.isRegularFile()).thenReturn(true);
|
|
||||||
|
|
||||||
BlockingQueue<Path> queue = new ArrayBlockingQueue<>(10);
|
@BeforeEach
|
||||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
public void setup() {
|
||||||
handler.handleLaunchArgs(fs, new String[] {"foo", "bar"});
|
queue = new ArrayBlockingQueue<>(1);
|
||||||
|
inTest = new FileOpenRequestHandler(queue);
|
||||||
Assert.assertEquals(p1, queue.poll());
|
|
||||||
Assert.assertEquals(p2, queue.poll());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@DisplayName("./cryptomator.exe foo bar")
|
||||||
|
public void testOpenArgsWithCorrectPaths() throws IOException {
|
||||||
|
inTest.handleLaunchArgs(new String[]{"foo", "bar"});
|
||||||
|
|
||||||
|
AppLaunchEvent evt = queue.poll();
|
||||||
|
Assertions.assertNotNull(evt);
|
||||||
|
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
||||||
|
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)")
|
||||||
public void testOpenArgsWithIncorrectPaths() throws IOException {
|
public void testOpenArgsWithIncorrectPaths() throws IOException {
|
||||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
FileSystem fs = Mockito.mock(FileSystem.class);
|
||||||
Mockito.when(fs.getPath(Mockito.anyString())).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
||||||
|
inTest.handleLaunchArgs(fs, new String[]{"foo"});
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
AppLaunchEvent evt = queue.poll();
|
||||||
BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
|
Assertions.assertNotNull(evt);
|
||||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
||||||
handler.handleLaunchArgs(fs, new String[] {"foo"});
|
Assertions.assertTrue(paths.isEmpty());
|
||||||
|
|
||||||
Mockito.verifyNoMoreInteractions(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@DisplayName("./cryptomator.exe foo (with full event queue)")
|
||||||
public void testOpenArgsWithFullQueue() throws IOException {
|
public void testOpenArgsWithFullQueue() throws IOException {
|
||||||
Path p = Mockito.mock(Path.class);
|
queue.add(new AppLaunchEvent(Stream.empty()));
|
||||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
Assumptions.assumeTrue(queue.remainingCapacity() == 0);
|
||||||
FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
|
|
||||||
BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
|
|
||||||
Mockito.when(p.getFileSystem()).thenReturn(fs);
|
|
||||||
Mockito.when(fs.provider()).thenReturn(provider);
|
|
||||||
Mockito.when(fs.getPath(Mockito.anyString())).thenReturn(p);
|
|
||||||
Mockito.when(provider.readAttributes(Mockito.eq(p), Mockito.eq(BasicFileAttributes.class))).thenReturn(attrs);
|
|
||||||
Mockito.when(attrs.isRegularFile()).thenReturn(true);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
inTest.handleLaunchArgs(new String[]{"foo"});
|
||||||
BlockingQueue<Path> queue = Mockito.mock(BlockingQueue.class);
|
|
||||||
Mockito.when(queue.offer(Mockito.any())).thenReturn(false);
|
|
||||||
FileOpenRequestHandler handler = new FileOpenRequestHandler(queue);
|
|
||||||
handler.handleLaunchArgs(fs, new String[] {"foo"});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +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.launcher;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SeekableByteChannel;
|
|
||||||
import java.nio.file.FileSystem;
|
|
||||||
import java.nio.file.NoSuchFileException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
public class InterProcessCommunicatorTest {
|
|
||||||
|
|
||||||
Path portFilePath = Mockito.mock(Path.class);
|
|
||||||
Path portFileParentPath = Mockito.mock(Path.class);
|
|
||||||
BasicFileAttributes portFileParentPathAttrs = Mockito.mock(BasicFileAttributes.class);
|
|
||||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
|
||||||
FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
|
|
||||||
SeekableByteChannel portFileChannel = Mockito.mock(SeekableByteChannel.class);
|
|
||||||
AtomicInteger port = new AtomicInteger(-1);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() throws IOException {
|
|
||||||
Mockito.when(portFilePath.getFileSystem()).thenReturn(fs);
|
|
||||||
Mockito.when(portFilePath.toAbsolutePath()).thenReturn(portFilePath);
|
|
||||||
Mockito.when(portFilePath.normalize()).thenReturn(portFilePath);
|
|
||||||
Mockito.when(portFilePath.getParent()).thenReturn(portFileParentPath);
|
|
||||||
Mockito.when(portFileParentPath.getFileSystem()).thenReturn(fs);
|
|
||||||
Mockito.when(fs.provider()).thenReturn(provider);
|
|
||||||
Mockito.when(provider.readAttributes(portFileParentPath, BasicFileAttributes.class)).thenReturn(portFileParentPathAttrs);
|
|
||||||
Mockito.when(portFileParentPathAttrs.isDirectory()).thenReturn(false, true); // Guava's MoreFiles will check if dir exists before attempting to create them.
|
|
||||||
Mockito.when(provider.newByteChannel(Mockito.eq(portFilePath), Mockito.any(), Mockito.any())).thenReturn(portFileChannel);
|
|
||||||
Mockito.when(portFileChannel.read(Mockito.any())).then(invocation -> {
|
|
||||||
ByteBuffer buf = invocation.getArgument(0);
|
|
||||||
buf.putInt(port.get());
|
|
||||||
return Integer.BYTES;
|
|
||||||
});
|
|
||||||
Mockito.when(portFileChannel.write(Mockito.any())).then(invocation -> {
|
|
||||||
ByteBuffer buf = invocation.getArgument(0);
|
|
||||||
port.set(buf.getInt());
|
|
||||||
return Integer.BYTES;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UnsupportedOperationException.class)
|
|
||||||
public void testStartWithDummyPort1() throws IOException {
|
|
||||||
port.set(0);
|
|
||||||
InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class);
|
|
||||||
try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) {
|
|
||||||
Assert.assertTrue(result.isServer());
|
|
||||||
Mockito.verify(provider).createDirectory(portFileParentPath);
|
|
||||||
Mockito.verifyZeroInteractions(protocol);
|
|
||||||
result.handleLaunchArgs(new String[] {"foo"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStartWithDummyPort2() throws IOException {
|
|
||||||
Mockito.doThrow(new NoSuchFileException("port file")).when(provider).checkAccess(portFilePath);
|
|
||||||
|
|
||||||
InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class);
|
|
||||||
try (InterProcessCommunicator result = InterProcessCommunicator.start(portFilePath, protocol)) {
|
|
||||||
Assert.assertTrue(result.isServer());
|
|
||||||
Mockito.verify(provider).createDirectory(portFileParentPath);
|
|
||||||
Mockito.verifyZeroInteractions(protocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInterProcessCommunication() throws IOException, InterruptedException {
|
|
||||||
port.set(-1);
|
|
||||||
InterProcessCommunicationProtocol protocol = Mockito.mock(InterProcessCommunicationProtocol.class);
|
|
||||||
try (InterProcessCommunicator result1 = InterProcessCommunicator.start(portFilePath, protocol)) {
|
|
||||||
Assert.assertTrue(result1.isServer());
|
|
||||||
Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath);
|
|
||||||
Mockito.verifyZeroInteractions(protocol);
|
|
||||||
|
|
||||||
try (InterProcessCommunicator result2 = InterProcessCommunicator.start(portFilePath, null)) {
|
|
||||||
Assert.assertFalse(result2.isServer());
|
|
||||||
Mockito.verify(provider, Mockito.times(1)).createDirectory(portFileParentPath);
|
|
||||||
Assert.assertNotSame(result1, result2);
|
|
||||||
|
|
||||||
result2.handleLaunchArgs(new String[] {"foo"});
|
|
||||||
Mockito.verify(protocol).handleLaunchArgs(new String[] {"foo"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.launcher;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.launcher.IpcFactory.IpcEndpoint;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class IpcFactoryTest {
|
||||||
|
|
||||||
|
private Environment environment = Mockito.mock(Environment.class);
|
||||||
|
private IpcProtocolImpl protocolHandler = Mockito.mock(IpcProtocolImpl.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Wihout IPC port files")
|
||||||
|
public void testNoIpcWithoutPortFile() throws IOException {
|
||||||
|
IpcFactory inTest = new IpcFactory(environment, protocolHandler);
|
||||||
|
|
||||||
|
Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.empty());
|
||||||
|
try (IpcEndpoint endpoint1 = inTest.create()) {
|
||||||
|
Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint1.getClass());
|
||||||
|
Assertions.assertFalse(endpoint1.isConnectedToRemote());
|
||||||
|
Assertions.assertSame(protocolHandler, endpoint1.getRemote());
|
||||||
|
try (IpcEndpoint endpoint2 = inTest.create()) {
|
||||||
|
Assertions.assertEquals(IpcFactory.SelfEndpoint.class, endpoint2.getClass());
|
||||||
|
Assertions.assertNotSame(endpoint1, endpoint2);
|
||||||
|
Assertions.assertFalse(endpoint2.isConnectedToRemote());
|
||||||
|
Assertions.assertSame(protocolHandler, endpoint2.getRemote());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Start server and client with port shared via file")
|
||||||
|
public void testInterProcessCommunication(@TempDir Path tmpDir) throws IOException {
|
||||||
|
Path portFile = tmpDir.resolve("testPortFile");
|
||||||
|
Mockito.when(environment.getIpcPortPath()).thenReturn(Stream.of(portFile));
|
||||||
|
IpcFactory inTest = new IpcFactory(environment, protocolHandler);
|
||||||
|
|
||||||
|
Assertions.assertFalse(Files.exists(portFile));
|
||||||
|
try (IpcEndpoint endpoint1 = inTest.create()) {
|
||||||
|
Assertions.assertEquals(IpcFactory.ServerEndpoint.class, endpoint1.getClass());
|
||||||
|
Assertions.assertFalse(endpoint1.isConnectedToRemote());
|
||||||
|
Assertions.assertTrue(Files.exists(portFile));
|
||||||
|
Assertions.assertSame(protocolHandler, endpoint1.getRemote());
|
||||||
|
Mockito.verifyZeroInteractions(protocolHandler);
|
||||||
|
try (IpcEndpoint endpoint2 = inTest.create()) {
|
||||||
|
Assertions.assertEquals(IpcFactory.ClientEndpoint.class, endpoint2.getClass());
|
||||||
|
Assertions.assertNotSame(endpoint1, endpoint2);
|
||||||
|
Assertions.assertTrue(endpoint2.isConnectedToRemote());
|
||||||
|
Assertions.assertNotSame(protocolHandler, endpoint2.getRemote());
|
||||||
|
Mockito.verifyZeroInteractions(protocolHandler);
|
||||||
|
endpoint2.getRemote().handleLaunchArgs(new String[] {"foo"});
|
||||||
|
Mockito.verify(protocolHandler).handleLaunchArgs(new String[] {"foo"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,12 +5,12 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.logging;
|
package org.cryptomator.logging;
|
||||||
|
|
||||||
import java.io.File;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class LaunchBasedTriggeringPolicyTest {
|
public class LaunchBasedTriggeringPolicyTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -21,15 +21,15 @@ public class LaunchBasedTriggeringPolicyTest {
|
|||||||
|
|
||||||
// 1st invocation
|
// 1st invocation
|
||||||
boolean triggered = policy.isTriggeringEvent(activeFile, event);
|
boolean triggered = policy.isTriggeringEvent(activeFile, event);
|
||||||
Assert.assertTrue(triggered);
|
Assertions.assertTrue(triggered);
|
||||||
|
|
||||||
// 2nd invocation
|
// 2nd invocation
|
||||||
triggered = policy.isTriggeringEvent(activeFile, event);
|
triggered = policy.isTriggeringEvent(activeFile, event);
|
||||||
Assert.assertFalse(triggered);
|
Assertions.assertFalse(triggered);
|
||||||
|
|
||||||
// 3rd invocation
|
// 3rd invocation
|
||||||
triggered = policy.isTriggeringEvent(activeFile, event);
|
triggered = policy.isTriggeringEvent(activeFile, event);
|
||||||
Assert.assertFalse(triggered);
|
Assertions.assertFalse(triggered);
|
||||||
|
|
||||||
Mockito.verifyZeroInteractions(activeFile);
|
Mockito.verifyZeroInteractions(activeFile);
|
||||||
Mockito.verifyZeroInteractions(event);
|
Mockito.verifyZeroInteractions(event);
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE xml>
|
|
||||||
<!-- log config used during unit tests and starts from IDE. For production please specify -Dlogback.configurationFile=/path/to/config -->
|
|
||||||
<configuration>
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="org.cryptomator" level="DEBUG" />
|
|
||||||
<logger name="org.eclipse.jetty" level="INFO" />
|
|
||||||
<logger name="org.eclipse.jetty.server.Server" level="DEBUG" />
|
|
||||||
<logger name="org.apache" level="INFO" />
|
|
||||||
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</root>
|
|
||||||
</configuration>
|
|
||||||
158
main/pom.xml
158
main/pom.xml
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.4.9</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Cryptomator</name>
|
<name>Cryptomator</name>
|
||||||
|
|
||||||
@@ -24,28 +24,30 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<!-- dependency versions -->
|
<!-- dependency versions -->
|
||||||
<cryptomator.cryptolib.version>1.1.5</cryptomator.cryptolib.version>
|
<cryptomator.cryptolib.version>1.2.1</cryptomator.cryptolib.version>
|
||||||
<cryptomator.cryptofs.version>1.4.0</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>1.8.2</cryptomator.cryptofs.version>
|
||||||
<cryptomator.webdav.version>0.6.2</cryptomator.webdav.version>
|
<cryptomator.jni.version>2.0.0</cryptomator.jni.version>
|
||||||
<cryptomator.jni.version>1.0.2</cryptomator.jni.version>
|
<cryptomator.fuse.version>1.1.2</cryptomator.fuse.version>
|
||||||
|
<cryptomator.dokany.version>1.1.7</cryptomator.dokany.version>
|
||||||
<commons-io.version>2.5</commons-io.version>
|
<cryptomator.webdav.version>1.0.9</cryptomator.webdav.version>
|
||||||
<commons-lang3.version>3.6</commons-lang3.version>
|
|
||||||
<httpclient.version>4.5.3</httpclient.version>
|
<javafx.version>12</javafx.version>
|
||||||
|
|
||||||
|
<commons-io.version>2.6</commons-io.version>
|
||||||
|
<commons-lang3.version>3.8.1</commons-lang3.version>
|
||||||
|
|
||||||
<easybind.version>1.0.3</easybind.version>
|
<easybind.version>1.0.3</easybind.version>
|
||||||
|
|
||||||
<guava.version>22.0</guava.version>
|
<guava.version>27.1-jre</guava.version>
|
||||||
<dagger.version>2.11</dagger.version>
|
<dagger.version>2.22.1</dagger.version>
|
||||||
<gson.version>2.8.1</gson.version>
|
<gson.version>2.8.5</gson.version>
|
||||||
|
|
||||||
<slf4j.version>1.7.25</slf4j.version>
|
<slf4j.version>1.7.26</slf4j.version>
|
||||||
<logback.version>1.2.2</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
|
|
||||||
<junit.version>4.12</junit.version>
|
<junit.jupiter.version>5.4.2</junit.jupiter.version>
|
||||||
<junit.hierarchicalrunner.version>4.12.1</junit.hierarchicalrunner.version>
|
<mockito.version>2.27.0</mockito.version>
|
||||||
<mockito.version>2.7.21</mockito.version>
|
<hamcrest.version>2.1</hamcrest.version>
|
||||||
<hamcrest.version>1.3</hamcrest.version> <!-- keep in sync with version required by JUnit -->
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
@@ -59,6 +61,10 @@
|
|||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>jcenter</id>
|
||||||
|
<url>http://jcenter.bintray.com</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -96,6 +102,16 @@
|
|||||||
<artifactId>cryptofs</artifactId>
|
<artifactId>cryptofs</artifactId>
|
||||||
<version>${cryptomator.cryptofs.version}</version>
|
<version>${cryptomator.cryptofs.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>fuse-nio-adapter</artifactId>
|
||||||
|
<version>${cryptomator.fuse.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>dokany-nio-adapter</artifactId>
|
||||||
|
<version>${cryptomator.dokany.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>webdav-nio-adapter</artifactId>
|
<artifactId>webdav-nio-adapter</artifactId>
|
||||||
@@ -107,6 +123,23 @@
|
|||||||
<version>${cryptomator.jni.version}</version>
|
<version>${cryptomator.jni.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JavaFX -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-base</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-controls</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Logging -->
|
<!-- Logging -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
@@ -140,11 +173,6 @@
|
|||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>${commons-lang3.version}</version>
|
<version>${commons-lang3.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
<version>${httpclient.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- EasyBind -->
|
<!-- EasyBind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -172,20 +200,10 @@
|
|||||||
|
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<version>${junit.version}</version>
|
<version>${junit.jupiter.version}</version>
|
||||||
<exclusions>
|
<scope>test</scope>
|
||||||
<exclusion>
|
|
||||||
<artifactId>hamcrest-core</artifactId>
|
|
||||||
<groupId>org.hamcrest</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>de.bechte.junit</groupId>
|
|
||||||
<artifactId>junit-hierarchicalcontextrunner</artifactId>
|
|
||||||
<version>${junit.hierarchicalrunner.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
@@ -194,9 +212,15 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-all</artifactId>
|
<artifactId>hamcrest</artifactId>
|
||||||
<version>${hamcrest.version}</version>
|
<version>${hamcrest.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
@@ -207,13 +231,12 @@
|
|||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hamcrest</groupId>
|
<groupId>org.hamcrest</groupId>
|
||||||
<artifactId>hamcrest-all</artifactId>
|
<artifactId>hamcrest</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -221,11 +244,6 @@
|
|||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>de.bechte.junit</groupId>
|
|
||||||
<artifactId>junit-hierarchicalcontextrunner</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
@@ -239,15 +257,11 @@
|
|||||||
<profile>
|
<profile>
|
||||||
<id>release</id>
|
<id>release</id>
|
||||||
<modules>
|
<modules>
|
||||||
<module>uber-jar</module>
|
<module>buildkit</module>
|
||||||
<module>ant-kit</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
<id>coverage</id>
|
<id>coverage</id>
|
||||||
<modules>
|
|
||||||
<module>jacoco-report</module>
|
|
||||||
</modules>
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -264,7 +278,7 @@
|
|||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
<version>3.0.1</version>
|
<version>3.1.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-libs</id>
|
<id>copy-libs</id>
|
||||||
@@ -278,10 +292,22 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<!-- adds Implementation-Version which can be read during runtime -->
|
||||||
|
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>0.7.9</version>
|
<version>0.8.2</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>prepare-agent</id>
|
<id>prepare-agent</id>
|
||||||
@@ -289,6 +315,12 @@
|
|||||||
<goal>prepare-agent</goal>
|
<goal>prepare-agent</goal>
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>report</id>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludes>
|
<excludes>
|
||||||
@@ -302,10 +334,9 @@
|
|||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.6.1</version>
|
<version>3.8.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.8</source>
|
<release>11</release>
|
||||||
<target>1.8</target>
|
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
<groupId>com.google.dagger</groupId>
|
<groupId>com.google.dagger</groupId>
|
||||||
@@ -315,6 +346,11 @@
|
|||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.1</version>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +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.3.0</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>uber-jar</artifactId>
|
|
||||||
<name>Single über jar with all dependencies</name>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>launcher</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-assembly</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<finalName>Cryptomator-${project.version}</finalName>
|
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
|
||||||
<transformers>
|
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
||||||
<manifestEntries>
|
|
||||||
<Main-Class>org.cryptomator.launcher.Cryptomator</Main-Class>
|
|
||||||
<Implementation-Version>${project.version}</Implementation-Version>
|
|
||||||
</manifestEntries>
|
|
||||||
</transformer>
|
|
||||||
</transformers>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE xml>
|
|
||||||
<!-- log config used during unit tests and starts from IDE. For production please specify -Dlogback.configurationFile=/path/to/config -->
|
|
||||||
<configuration>
|
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="org.cryptomator" level="DEBUG" />
|
|
||||||
<logger name="org.eclipse.jetty" level="INFO" />
|
|
||||||
<logger name="org.eclipse.jetty.server.Server" level="INFO" />
|
|
||||||
<logger name="org.apache" level="INFO" />
|
|
||||||
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="STDOUT" />
|
|
||||||
</root>
|
|
||||||
</configuration>
|
|
||||||
@@ -4,39 +4,58 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.4.9</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>ui</artifactId>
|
<artifactId>ui</artifactId>
|
||||||
<name>Cryptomator GUI</name>
|
<name>Cryptomator GUI</name>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>keychain</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptofs</artifactId>
|
<artifactId>cryptofs</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>webdav-nio-adapter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>jni</artifactId>
|
<artifactId>jni</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>keychain</artifactId>
|
<artifactId>fuse-nio-adapter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>dokany-nio-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>webdav-nio-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- CryptoLib -->
|
<!-- CryptoLib -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptolib</artifactId>
|
<artifactId>cryptolib</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JavaFx -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-controls</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- EasyBind -->
|
<!-- EasyBind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.fxmisc.easybind</groupId>
|
<groupId>org.fxmisc.easybind</groupId>
|
||||||
@@ -62,10 +81,6 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- DI -->
|
<!-- DI -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -86,5 +101,18 @@
|
|||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Testing -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.jimfs</groupId>
|
||||||
|
<artifactId>jimfs</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -9,6 +9,24 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui;
|
package org.cryptomator.ui;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.jni.JniException;
|
||||||
|
import org.cryptomator.jni.MacApplicationUiState;
|
||||||
|
import org.cryptomator.jni.MacFunctions;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptEngineManager;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import java.awt.AWTException;
|
import java.awt.AWTException;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.MenuItem;
|
import java.awt.MenuItem;
|
||||||
@@ -24,27 +42,7 @@ import java.io.IOException;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
@FxApplicationScoped
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.script.ScriptEngine;
|
|
||||||
import javax.script.ScriptEngineManager;
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.jni.JniException;
|
|
||||||
import org.cryptomator.jni.MacApplicationUiState;
|
|
||||||
import org.cryptomator.jni.MacFunctions;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ExitUtil {
|
public class ExitUtil {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class);
|
||||||
@@ -53,6 +51,7 @@ public class ExitUtil {
|
|||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final Optional<MacFunctions> macFunctions;
|
private final Optional<MacFunctions> macFunctions;
|
||||||
|
private TrayIcon trayIcon;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional<MacFunctions> macFunctions) {
|
public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional<MacFunctions> macFunctions) {
|
||||||
@@ -82,7 +81,7 @@ public class ExitUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initTrayIconExitHandler(Runnable exitCommand) {
|
private void initTrayIconExitHandler(Runnable exitCommand) {
|
||||||
final TrayIcon trayIcon = createTrayIcon(exitCommand);
|
trayIcon = createTrayIcon(exitCommand);
|
||||||
try {
|
try {
|
||||||
// double clicking tray icon should open Cryptomator
|
// double clicking tray icon should open Cryptomator
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
@@ -118,14 +117,7 @@ public class ExitUtil {
|
|||||||
exitItem.addActionListener(e -> exitCommand.run());
|
exitItem.addActionListener(e -> exitCommand.run());
|
||||||
popup.add(exitItem);
|
popup.add(exitItem);
|
||||||
|
|
||||||
final Image image;
|
final Image image = getAppropriateTrayIconImage(true);
|
||||||
if (SystemUtils.IS_OS_MAC_OSX && isMacMenuBarDarkMode()) {
|
|
||||||
image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/tray_icon_mac_white.png"));
|
|
||||||
} else if (SystemUtils.IS_OS_MAC_OSX) {
|
|
||||||
image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/tray_icon_mac_black.png"));
|
|
||||||
} else {
|
|
||||||
image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/tray_icon.png"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TrayIcon(image, localization.getString("app.name"), popup);
|
return new TrayIcon(image, localization.getString("app.name"), popup);
|
||||||
}
|
}
|
||||||
@@ -202,4 +194,23 @@ public class ExitUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateTrayIcon(boolean areAllVaultsLocked) {
|
||||||
|
if (trayIcon != null) {
|
||||||
|
Image image = getAppropriateTrayIconImage(areAllVaultsLocked);
|
||||||
|
trayIcon.setImage(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image getAppropriateTrayIconImage(boolean areAllVaultsLocked) {
|
||||||
|
String resourceName;
|
||||||
|
if (SystemUtils.IS_OS_MAC_OSX && isMacMenuBarDarkMode()) {
|
||||||
|
resourceName = areAllVaultsLocked ? "/tray_icon_mac_white.png" : "/tray_icon_unlocked_mac_white.png";
|
||||||
|
} else if (SystemUtils.IS_OS_MAC_OSX) {
|
||||||
|
resourceName = areAllVaultsLocked ? "/tray_icon_mac_black.png" : "/tray_icon_unlocked_mac_black.png";
|
||||||
|
} else {
|
||||||
|
resourceName = areAllVaultsLocked ? "/tray_icon.png" : "/tray_icon_unlocked.png";
|
||||||
|
}
|
||||||
|
return Toolkit.getDefaultToolkit().getImage(getClass().getResource(resourceName));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,48 +8,61 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui;
|
package org.cryptomator.ui;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import dagger.Module;
|
||||||
import java.util.concurrent.ExecutorService;
|
import dagger.Provides;
|
||||||
import java.util.concurrent.Executors;
|
import javafx.beans.binding.Binding;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.CommonsModule;
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.SettingsProvider;
|
|
||||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||||
import org.cryptomator.jni.JniModule;
|
|
||||||
import org.cryptomator.keychain.KeychainModule;
|
import org.cryptomator.keychain.KeychainModule;
|
||||||
import org.cryptomator.ui.controllers.ViewControllerModule;
|
import org.cryptomator.ui.controllers.ViewControllerModule;
|
||||||
import org.cryptomator.ui.model.VaultComponent;
|
import org.cryptomator.ui.model.VaultComponent;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
|
||||||
import dagger.Module;
|
import javax.inject.Named;
|
||||||
import dagger.Provides;
|
import java.net.InetSocketAddress;
|
||||||
import javafx.beans.binding.Binding;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class, JniModule.class}, subcomponents = {VaultComponent.class})
|
@Module(includes = {ViewControllerModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class})
|
||||||
public class UiModule {
|
public class UiModule {
|
||||||
|
|
||||||
@Provides
|
private static final int NUM_SCHEDULER_THREADS = 4;
|
||||||
@Singleton
|
|
||||||
Settings provideSettings(SettingsProvider settingsProvider) {
|
|
||||||
return settingsProvider.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@FxApplicationScoped
|
||||||
ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
||||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
|
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName("Scheduler Thread " + threadNumber.getAndIncrement());
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
shutdownTaskScheduler.accept(executorService::shutdown);
|
shutdownTaskScheduler.accept(executorService::shutdown);
|
||||||
return executorService;
|
return executorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@FxApplicationScoped
|
||||||
|
ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
||||||
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
|
ExecutorService executorService = Executors.newCachedThreadPool(r -> {
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName("Background Thread " + threadNumber.getAndIncrement());
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
shutdownTaskScheduler.accept(executorService::shutdown);
|
||||||
|
return executorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxApplicationScoped
|
||||||
Binding<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
Binding<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
||||||
return EasyBind.map(settings.port(), (Number port) -> {
|
return EasyBind.map(settings.port(), (Number port) -> {
|
||||||
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
||||||
@@ -58,7 +71,7 @@ public class UiModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@FxApplicationScoped
|
||||||
WebDavServer provideWebDavServer(Binding<InetSocketAddress> serverSocketAddressBinding) {
|
WebDavServer provideWebDavServer(Binding<InetSocketAddress> serverSocketAddressBinding) {
|
||||||
WebDavServer server = WebDavServer.create();
|
WebDavServer server = WebDavServer.create();
|
||||||
// no need to unsubscribe eventually, because server is a singleton
|
// no need to unsubscribe eventually, because server is a singleton
|
||||||
|
|||||||
@@ -9,27 +9,9 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UncheckedIOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
|
||||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
|
||||||
import org.cryptomator.ui.controls.SecPasswordField;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.util.PasswordStrengthUtil;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -41,8 +23,22 @@ import javafx.scene.control.Label;
|
|||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||||
|
import org.cryptomator.ui.controls.SecPasswordField;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.util.PasswordStrengthUtil;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ChangePasswordController implements ViewController {
|
public class ChangePasswordController implements ViewController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class);
|
||||||
@@ -50,7 +46,7 @@ public class ChangePasswordController implements ViewController {
|
|||||||
private final Application app;
|
private final Application app;
|
||||||
private final PasswordStrengthUtil strengthRater;
|
private final PasswordStrengthUtil strengthRater;
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(); // 0-4
|
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(-1); // 0-4
|
||||||
private Optional<ChangePasswordListener> listener = Optional.empty();
|
private Optional<ChangePasswordListener> listener = Optional.empty();
|
||||||
private Vault vault;
|
private Vault vault;
|
||||||
|
|
||||||
@@ -102,11 +98,9 @@ public class ChangePasswordController implements ViewController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
BooleanBinding oldPasswordIsEmpty = oldPasswordField.textProperty().isEmpty();
|
oldPasswordField.textProperty().addListener(this::passwordsChanged);
|
||||||
BooleanBinding newPasswordIsEmpty = newPasswordField.textProperty().isEmpty();
|
newPasswordField.textProperty().addListener(this::passwordsChanged);
|
||||||
BooleanBinding passwordsDiffer = newPasswordField.textProperty().isNotEqualTo(retypePasswordField.textProperty());
|
retypePasswordField.textProperty().addListener(this::passwordsChanged);
|
||||||
changePasswordButton.disableProperty().bind(oldPasswordIsEmpty.or(newPasswordIsEmpty.or(passwordsDiffer)));
|
|
||||||
passwordStrength.bind(EasyBind.map(newPasswordField.textProperty(), strengthRater::computeRate));
|
|
||||||
|
|
||||||
passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor));
|
passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor));
|
||||||
passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor));
|
passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor));
|
||||||
@@ -116,16 +110,26 @@ public class ChangePasswordController implements ViewController {
|
|||||||
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
|
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void passwordsChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
|
boolean oldPasswordEmpty = oldPasswordField.getCharacters().length() == 0;
|
||||||
|
boolean newPasswordEmpty = newPasswordField.getCharacters().length() == 0;
|
||||||
|
boolean passwordsEqual = newPasswordField.getCharacters().equals(retypePasswordField.getCharacters());
|
||||||
|
changePasswordButton.setDisable(oldPasswordEmpty || newPasswordEmpty || !passwordsEqual);
|
||||||
|
passwordStrength.set(strengthRater.computeRate(newPasswordField.getCharacters().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Parent getRoot() {
|
public Parent getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focus() {
|
||||||
|
oldPasswordField.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
void setVault(Vault vault) {
|
void setVault(Vault vault) {
|
||||||
this.vault = Objects.requireNonNull(vault);
|
this.vault = Objects.requireNonNull(vault);
|
||||||
oldPasswordField.swipe();
|
|
||||||
newPasswordField.swipe();
|
|
||||||
retypePasswordField.swipe();
|
|
||||||
// trigger "default" change to refresh key bindings:
|
// trigger "default" change to refresh key bindings:
|
||||||
changePasswordButton.setDefaultButton(false);
|
changePasswordButton.setDefaultButton(false);
|
||||||
changePasswordButton.setDefaultButton(true);
|
changePasswordButton.setDefaultButton(true);
|
||||||
|
|||||||
@@ -9,25 +9,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.DirectoryNotEmptyException;
|
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.ui.controls.SecPasswordField;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.util.PasswordStrengthUtil;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -37,15 +20,27 @@ import javafx.scene.control.Button;
|
|||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import org.cryptomator.ui.controls.SecPasswordField;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.util.PasswordStrengthUtil;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class InitializeController implements ViewController {
|
public class InitializeController implements ViewController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
|
||||||
|
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final PasswordStrengthUtil strengthRater;
|
private final PasswordStrengthUtil strengthRater;
|
||||||
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(); // 0-4
|
private IntegerProperty passwordStrength = new SimpleIntegerProperty(-1); // strengths: 0-4
|
||||||
private Optional<InitializationListener> listener = Optional.empty();
|
private Optional<InitializationListener> listener = Optional.empty();
|
||||||
private Vault vault;
|
private Vault vault;
|
||||||
|
|
||||||
@@ -90,10 +85,8 @@ public class InitializeController implements ViewController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
BooleanBinding passwordIsEmpty = passwordField.textProperty().isEmpty();
|
passwordField.textProperty().addListener(this::passwordsChanged);
|
||||||
BooleanBinding passwordsDiffer = passwordField.textProperty().isNotEqualTo(retypePasswordField.textProperty());
|
retypePasswordField.textProperty().addListener(this::passwordsChanged);
|
||||||
okButton.disableProperty().bind(passwordIsEmpty.or(passwordsDiffer));
|
|
||||||
passwordStrength.bind(EasyBind.map(passwordField.textProperty(), strengthRater::computeRate));
|
|
||||||
|
|
||||||
passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor));
|
passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor));
|
||||||
passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor));
|
passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor));
|
||||||
@@ -103,15 +96,25 @@ public class InitializeController implements ViewController {
|
|||||||
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
|
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void passwordsChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
|
boolean passwordsEmpty = passwordField.getCharacters().length() == 0;
|
||||||
|
boolean passwordsEqual = passwordField.getCharacters().equals(retypePasswordField.getCharacters());
|
||||||
|
okButton.setDisable(passwordsEmpty || !passwordsEqual);
|
||||||
|
passwordStrength.set(strengthRater.computeRate(passwordField.getCharacters().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Parent getRoot() {
|
public Parent getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focus() {
|
||||||
|
passwordField.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
void setVault(Vault vault) {
|
void setVault(Vault vault) {
|
||||||
this.vault = Objects.requireNonNull(vault);
|
this.vault = Objects.requireNonNull(vault);
|
||||||
passwordField.swipe();
|
|
||||||
retypePasswordField.swipe();
|
|
||||||
// trigger "default" change to refresh key bindings:
|
// trigger "default" change to refresh key bindings:
|
||||||
okButton.setDefaultButton(false);
|
okButton.setDefaultButton(false);
|
||||||
okButton.setDefaultButton(true);
|
okButton.setDefaultButton(true);
|
||||||
@@ -129,8 +132,6 @@ public class InitializeController implements ViewController {
|
|||||||
listener.ifPresent(this::invokeListenerLater);
|
listener.ifPresent(this::invokeListenerLater);
|
||||||
} catch (FileAlreadyExistsException ex) {
|
} catch (FileAlreadyExistsException ex) {
|
||||||
messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized"));
|
messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized"));
|
||||||
} catch (DirectoryNotEmptyException ex) {
|
|
||||||
messageLabel.setText(localization.getString("initialize.messageLabel.notEmpty"));
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOG.error("I/O Exception", ex);
|
LOG.error("I/O Exception", ex);
|
||||||
messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed"));
|
messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed"));
|
||||||
|
|||||||
@@ -2,48 +2,13 @@
|
|||||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Sebastian Stenzel - initial API and implementation
|
* Sebastian Stenzel - initial API and implementation
|
||||||
* Jean-Noël Charon - confirmation dialog on vault removal
|
* Jean-Noël Charon - confirmation dialog on vault removal
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
|
||||||
import org.cryptomator.ui.ExitUtil;
|
|
||||||
import org.cryptomator.ui.controls.DirectoryListCell;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.AutoUnlocker;
|
|
||||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
|
||||||
import org.cryptomator.ui.model.UpgradeStrategy;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.model.VaultFactory;
|
|
||||||
import org.cryptomator.ui.model.VaultList;
|
|
||||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
|
||||||
import org.cryptomator.ui.util.EawtApplicationWrapper;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.fxmisc.easybind.Subscription;
|
|
||||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Binding;
|
import javafx.beans.binding.Binding;
|
||||||
@@ -55,7 +20,6 @@ import javafx.beans.property.ObjectProperty;
|
|||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
@@ -63,19 +27,62 @@ import javafx.scene.Scene;
|
|||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Cell;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.ToggleButton;
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.ui.ExitUtil;
|
||||||
|
import org.cryptomator.ui.controls.DirectoryListCell;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.AppLaunchEvent;
|
||||||
|
import org.cryptomator.ui.model.AutoUnlocker;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.model.VaultFactory;
|
||||||
|
import org.cryptomator.ui.model.VaultList;
|
||||||
|
import org.cryptomator.ui.model.upgrade.UpgradeStrategies;
|
||||||
|
import org.cryptomator.ui.model.upgrade.UpgradeStrategy;
|
||||||
|
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||||
|
import org.cryptomator.ui.util.Tasks;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.fxmisc.easybind.Subscription;
|
||||||
|
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Singleton
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.cryptomator.ui.util.DialogBuilderUtil.buildErrorDialog;
|
||||||
|
|
||||||
|
@FxApplicationScoped
|
||||||
public class MainController implements ViewController {
|
public class MainController implements ViewController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
|
||||||
@@ -86,7 +93,7 @@ public class MainController implements ViewController {
|
|||||||
private final ExitUtil exitUtil;
|
private final ExitUtil exitUtil;
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
private final BlockingQueue<Path> fileOpenRequests;
|
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||||
private final VaultFactory vaultFactoy;
|
private final VaultFactory vaultFactoy;
|
||||||
private final ViewControllerLoader viewControllerLoader;
|
private final ViewControllerLoader viewControllerLoader;
|
||||||
private final ObjectProperty<ViewController> activeController = new SimpleObjectProperty<>();
|
private final ObjectProperty<ViewController> activeController = new SimpleObjectProperty<>();
|
||||||
@@ -103,11 +110,11 @@ public class MainController implements ViewController {
|
|||||||
private Subscription subs = Subscription.EMPTY;
|
private Subscription subs = Subscription.EMPTY;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests, ExitUtil exitUtil, Localization localization,
|
public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExitUtil exitUtil, Localization localization,
|
||||||
VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) {
|
VaultFactory vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, VaultList vaults, AutoUnlocker autoUnlocker) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
this.fileOpenRequests = fileOpenRequests;
|
this.launchEventQueue = launchEventQueue;
|
||||||
this.exitUtil = exitUtil;
|
this.exitUtil = exitUtil;
|
||||||
this.localization = localization;
|
this.localization = localization;
|
||||||
this.vaultFactoy = vaultFactoy;
|
this.vaultFactoy = vaultFactoy;
|
||||||
@@ -119,14 +126,17 @@ public class MainController implements ViewController {
|
|||||||
this.upgradeStrategyForSelectedVault = EasyBind.monadic(selectedVault).map(upgradeStrategies::getUpgradeStrategy);
|
this.upgradeStrategyForSelectedVault = EasyBind.monadic(selectedVault).map(upgradeStrategies::getUpgradeStrategy);
|
||||||
this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault.NOT_LOCKED));
|
this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault.NOT_LOCKED));
|
||||||
|
|
||||||
|
EasyBind.subscribe(areAllVaultsLocked, exitUtil::updateTrayIcon);
|
||||||
EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit);
|
EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit);
|
||||||
autoUnlocker.unlockAllSilently();
|
autoUnlocker.unlockAllSilently();
|
||||||
|
|
||||||
EawtApplicationWrapper.getApplication().ifPresent(app -> {
|
try {
|
||||||
app.setPreferencesHandler(() -> {
|
Desktop.getDesktop().setPreferencesHandler(e -> {
|
||||||
Platform.runLater(this::toggleShowSettings);
|
Platform.runLater(this::toggleShowSettings);
|
||||||
});
|
});
|
||||||
});
|
} catch (UnsupportedOperationException e) {
|
||||||
|
LOG.info("Unable to setPreferencesHandler, probably not supported on this OS.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -162,7 +172,10 @@ public class MainController implements ViewController {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
vaultList.setItems(vaults);
|
vaultList.setItems(vaults);
|
||||||
|
vaultList.getSelectionModel().clearSelection();
|
||||||
|
vaultList.setOnKeyReleased(this::didPressKeyOnList);
|
||||||
vaultList.setCellFactory(this::createDirecoryListCell);
|
vaultList.setCellFactory(this::createDirecoryListCell);
|
||||||
|
root.setOnKeyReleased(this::didPressKeyOnRoot);
|
||||||
activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
|
activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
|
||||||
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
||||||
removeVaultButton.disableProperty().bind(canEditSelectedVault.not());
|
removeVaultButton.disableProperty().bind(canEditSelectedVault.not());
|
||||||
@@ -183,6 +196,7 @@ public class MainController implements ViewController {
|
|||||||
public void initStage(Stage stage) {
|
public void initStage(Stage stage) {
|
||||||
stage.setScene(new Scene(getRoot()));
|
stage.setScene(new Scene(getRoot()));
|
||||||
stage.sizeToScene();
|
stage.sizeToScene();
|
||||||
|
stage.setTitle(localization.getString("app.name")); // set once before bind to avoid display bugs with Linux window managers
|
||||||
stage.titleProperty().bind(windowTitle());
|
stage.titleProperty().bind(windowTitle());
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
loadFont("/css/ionicons.ttf");
|
loadFont("/css/ionicons.ttf");
|
||||||
@@ -192,18 +206,42 @@ public class MainController implements ViewController {
|
|||||||
subs = subs.and(EasyBind.includeWhen(mainWindow.getScene().getRoot().getStyleClass(), INACTIVE_WINDOW_STYLE_CLASS, mainWindow.focusedProperty().not()));
|
subs = subs.and(EasyBind.includeWhen(mainWindow.getScene().getRoot().getStyleClass(), INACTIVE_WINDOW_STYLE_CLASS, mainWindow.focusedProperty().not()));
|
||||||
Application.setUserAgentStylesheet(getClass().getResource("/css/mac_theme.css").toString());
|
Application.setUserAgentStylesheet(getClass().getResource("/css/mac_theme.css").toString());
|
||||||
} else if (SystemUtils.IS_OS_LINUX) {
|
} else if (SystemUtils.IS_OS_LINUX) {
|
||||||
|
stage.getIcons().add(new Image(getClass().getResourceAsStream("/window_icon_512.png")));
|
||||||
Application.setUserAgentStylesheet(getClass().getResource("/css/linux_theme.css").toString());
|
Application.setUserAgentStylesheet(getClass().getResource("/css/linux_theme.css").toString());
|
||||||
} else if (SystemUtils.IS_OS_WINDOWS) {
|
} else if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
stage.getIcons().add(new Image(getClass().getResourceAsStream("/window_icon.png")));
|
stage.getIcons().add(new Image(getClass().getResourceAsStream("/window_icon_32.png")));
|
||||||
Application.setUserAgentStylesheet(getClass().getResource("/css/win_theme.css").toString());
|
Application.setUserAgentStylesheet(getClass().getResource("/css/win_theme.css").toString());
|
||||||
}
|
}
|
||||||
exitUtil.initExitHandler(this::gracefulShutdown);
|
exitUtil.initExitHandler(() -> Platform.runLater(this::gracefulShutdown));
|
||||||
listenToFileOpenRequests(stage);
|
listenToFileOpenRequests(stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gracefulShutdown() {
|
private void gracefulShutdown() {
|
||||||
vaults.filtered(Vault.NOT_LOCKED).forEach(Vault::prepareForShutdown);
|
vaults.filtered(Vault.NOT_LOCKED).forEach(Vault::prepareForShutdown);
|
||||||
Platform.runLater(Platform::exit);
|
if (!vaults.filtered(Vault.NOT_LOCKED).isEmpty()) {
|
||||||
|
mainWindow.show(); // to keep the application open
|
||||||
|
ButtonType tryAgainButtonType = new ButtonType(localization.getString("main.gracefulShutdown.button.tryAgain"));
|
||||||
|
ButtonType forceShutdownButtonType = new ButtonType(localization.getString("main.gracefulShutdown.button.forceShutdown"));
|
||||||
|
Alert gracefulShutdownDialog = DialogBuilderUtil.buildGracefulShutdownDialog(
|
||||||
|
localization.getString("main.gracefulShutdown.dialog.title"), localization.getString("main.gracefulShutdown.dialog.header"), localization.getString("main.gracefulShutdown.dialog.content"),
|
||||||
|
forceShutdownButtonType, ButtonType.CANCEL, forceShutdownButtonType, tryAgainButtonType);
|
||||||
|
Optional<ButtonType> choice = gracefulShutdownDialog.showAndWait();
|
||||||
|
choice.ifPresent(btnType -> {
|
||||||
|
if (tryAgainButtonType.equals(btnType)) {
|
||||||
|
gracefulShutdown();
|
||||||
|
} else if (forceShutdownButtonType.equals(btnType)) {
|
||||||
|
Platform.runLater(Platform::exit);
|
||||||
|
} else {
|
||||||
|
if (!vaults.filtered(Vault.NOT_LOCKED).isEmpty()) {
|
||||||
|
showUnlockedView(vaults.get(0), false); //if there are still unlocked vaults, show one of them
|
||||||
|
} else {
|
||||||
|
showUnlockView(UnlockController.State.UNLOCKING); //otherwise show any vault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Platform.runLater(Platform::exit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFont(String resourcePath) {
|
private void loadFont(String resourcePath) {
|
||||||
@@ -215,27 +253,19 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void listenToFileOpenRequests(Stage stage) {
|
private void listenToFileOpenRequests(Stage stage) {
|
||||||
executorService.submit(() -> {
|
Tasks.create(launchEventQueue::take).onSuccess(event -> {
|
||||||
while (!Thread.interrupted()) {
|
stage.setIconified(false);
|
||||||
try {
|
stage.show();
|
||||||
final Path path = fileOpenRequests.take();
|
stage.toFront();
|
||||||
Platform.runLater(() -> {
|
stage.requestFocus();
|
||||||
addVault(path, true);
|
event.getPathsToOpen().forEach(path -> addVault(path, true));
|
||||||
stage.setIconified(false);
|
}).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO);
|
||||||
stage.show();
|
|
||||||
stage.toFront();
|
|
||||||
stage.requestFocus();
|
|
||||||
});
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListCell<Vault> createDirecoryListCell(ListView<Vault> param) {
|
private ListCell<Vault> createDirecoryListCell(ListView<Vault> param) {
|
||||||
final DirectoryListCell cell = new DirectoryListCell();
|
final DirectoryListCell cell = new DirectoryListCell();
|
||||||
cell.setVaultContextMenu(vaultListCellContextMenu);
|
cell.setVaultContextMenu(vaultListCellContextMenu);
|
||||||
|
cell.setOnMouseClicked(this::didClickOnListCell);
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +274,7 @@ public class MainController implements ViewController {
|
|||||||
// ****************************************
|
// ****************************************
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickAddVault(ActionEvent event) {
|
private void didClickAddVault() {
|
||||||
if (addVaultContextMenu.isShowing()) {
|
if (addVaultContextMenu.isShowing()) {
|
||||||
addVaultContextMenu.hide();
|
addVaultContextMenu.hide();
|
||||||
} else {
|
} else {
|
||||||
@@ -253,7 +283,7 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickCreateNewVault(ActionEvent event) {
|
private void didClickCreateNewVault() {
|
||||||
final FileChooser fileChooser = new FileChooser();
|
final FileChooser fileChooser = new FileChooser();
|
||||||
final File file = fileChooser.showSaveDialog(mainWindow);
|
final File file = fileChooser.showSaveDialog(mainWindow);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
@@ -261,7 +291,18 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final Path vaultDir = file.toPath();
|
final Path vaultDir = file.toPath();
|
||||||
if (!Files.exists(vaultDir)) {
|
if (Files.exists(vaultDir)) {
|
||||||
|
try (Stream<Path> stream = Files.list(vaultDir)) {
|
||||||
|
if (stream.filter(this::isNotHidden).findAny().isPresent()) {
|
||||||
|
buildErrorDialog( //
|
||||||
|
localization.getString("main.createVault.nonEmptyDir.title"), //
|
||||||
|
localization.getString("main.createVault.nonEmptyDir.header"), //
|
||||||
|
localization.getString("main.createVault.nonEmptyDir.content"), //
|
||||||
|
ButtonType.OK).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Files.createDirectory(vaultDir);
|
Files.createDirectory(vaultDir);
|
||||||
}
|
}
|
||||||
addVault(vaultDir, true);
|
addVault(vaultDir, true);
|
||||||
@@ -270,8 +311,12 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isNotHidden(Path file) {
|
||||||
|
return !file.getFileName().toString().startsWith(".");
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickAddExistingVaults(ActionEvent event) {
|
private void didClickAddExistingVaults() {
|
||||||
final FileChooser fileChooser = new FileChooser();
|
final FileChooser fileChooser = new FileChooser();
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
|
||||||
final List<File> files = fileChooser.showOpenMultipleDialog(mainWindow);
|
final List<File> files = fileChooser.showOpenMultipleDialog(mainWindow);
|
||||||
@@ -284,14 +329,14 @@ public class MainController implements ViewController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* adds the given directory or selects it if it is already in the list of directories.
|
* adds the given directory or selects it if it is already in the list of directories.
|
||||||
*
|
*
|
||||||
* @param path to a vault directory or masterkey file
|
* @param path to a vault directory or masterkey file
|
||||||
*/
|
*/
|
||||||
public void addVault(final Path path, boolean select) {
|
public void addVault(final Path path, boolean select) {
|
||||||
final Path vaultPath;
|
final Path vaultPath;
|
||||||
if (path != null && Files.isDirectory(path)) {
|
if (path != null && Files.isDirectory(path)) {
|
||||||
vaultPath = path;
|
vaultPath = path;
|
||||||
} else if (path != null && Files.isRegularFile(path)) {
|
} else if (path != null && Files.isReadable(path)) {
|
||||||
vaultPath = path.getParent();
|
vaultPath = path.getParent();
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("Ignoring attempt to add vault with invalid path: {}", path);
|
LOG.warn("Ignoring attempt to add vault with invalid path: {}", path);
|
||||||
@@ -309,11 +354,12 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
if (select) {
|
if (select) {
|
||||||
vaultList.getSelectionModel().select(vault);
|
vaultList.getSelectionModel().select(vault);
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickRemoveSelectedEntry(ActionEvent e) {
|
private void didClickRemoveSelectedEntry() {
|
||||||
Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( //
|
Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( //
|
||||||
localization.getString("main.directoryList.remove.confirmation.title"), //
|
localization.getString("main.directoryList.remove.confirmation.title"), //
|
||||||
localization.getString("main.directoryList.remove.confirmation.header"), //
|
localization.getString("main.directoryList.remove.confirmation.header"), //
|
||||||
@@ -325,17 +371,19 @@ public class MainController implements ViewController {
|
|||||||
vaults.remove(selectedVault.get());
|
vaults.remove(selectedVault.get());
|
||||||
if (vaults.isEmpty()) {
|
if (vaults.isEmpty()) {
|
||||||
activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
|
activeController.set(viewControllerLoader.load("/fxml/welcome.fxml"));
|
||||||
|
} else {
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickChangePassword(ActionEvent e) {
|
private void didClickChangePassword() {
|
||||||
showChangePasswordView();
|
showChangePasswordView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickShowSettings(ActionEvent e) {
|
private void didClickShowSettings() {
|
||||||
toggleShowSettings();
|
toggleShowSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,18 +411,54 @@ public class MainController implements ViewController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newValue.getState() != Vault.State.LOCKED) {
|
if (newValue.getState() != Vault.State.LOCKED) {
|
||||||
this.showUnlockedView(newValue);
|
this.showUnlockedView(newValue, false);
|
||||||
} else if (!newValue.doesVaultDirectoryExist()) {
|
} else if (!newValue.doesVaultDirectoryExist()) {
|
||||||
this.showNotFoundView();
|
this.showNotFoundView();
|
||||||
} else if (newValue.isValidVaultDirectory() && upgradeStrategyForSelectedVault.isPresent()) {
|
} else if (newValue.isValidVaultDirectory() && upgradeStrategyForSelectedVault.isPresent()) {
|
||||||
this.showUpgradeView();
|
this.showUpgradeView();
|
||||||
} else if (newValue.isValidVaultDirectory()) {
|
} else if (newValue.isValidVaultDirectory()) {
|
||||||
this.showUnlockView();
|
this.showUnlockView(UnlockController.State.UNLOCKING);
|
||||||
} else {
|
} else {
|
||||||
this.showInitializeView();
|
this.showInitializeView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void didPressKeyOnList(KeyEvent e) {
|
||||||
|
if (e.getCode() == KeyCode.ENTER || e.getCode() == KeyCode.SPACE) {
|
||||||
|
activeController.get().focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void didPressKeyOnRoot(KeyEvent event) {
|
||||||
|
boolean triggered;
|
||||||
|
if (SystemUtils.IS_OS_MAC) {
|
||||||
|
triggered = event.isMetaDown();
|
||||||
|
} else {
|
||||||
|
triggered = event.isControlDown() && !event.isAltDown();
|
||||||
|
}
|
||||||
|
if (triggered && event.getCode().isDigitKey()) {
|
||||||
|
int digit = Integer.valueOf(event.getText());
|
||||||
|
switch (digit) {
|
||||||
|
case 0: {
|
||||||
|
vaultList.getSelectionModel().clearSelection();
|
||||||
|
showWelcomeView();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
vaultList.getSelectionModel().select(digit - 1);
|
||||||
|
activeController.get().focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void didClickOnListCell(MouseEvent e) {
|
||||||
|
if (MouseEvent.MOUSE_CLICKED.equals(e.getEventType()) && e.getSource() instanceof Cell && ((Cell<?>) e.getSource()).isSelected()) {
|
||||||
|
activeController.get().focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ****************************************
|
// ****************************************
|
||||||
// Public Bindings
|
// Public Bindings
|
||||||
// ****************************************
|
// ****************************************
|
||||||
@@ -407,7 +491,8 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void didInitialize() {
|
public void didInitialize() {
|
||||||
showUnlockView();
|
showUnlockView(UnlockController.State.INITIALIZED);
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUpgradeView() {
|
private void showUpgradeView() {
|
||||||
@@ -418,34 +503,39 @@ public class MainController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void didUpgrade() {
|
public void didUpgrade() {
|
||||||
showUnlockView();
|
showUnlockView(UnlockController.State.UPGRADED);
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUnlockView() {
|
private void showUnlockView(UnlockController.State state) {
|
||||||
final UnlockController ctrl = viewControllerLoader.load("/fxml/unlock.fxml");
|
final UnlockController ctrl = viewControllerLoader.load("/fxml/unlock.fxml");
|
||||||
ctrl.setVault(selectedVault.get());
|
ctrl.setVault(selectedVault.get(), state);
|
||||||
ctrl.setListener(this::didUnlock);
|
ctrl.setListener(this::didUnlock);
|
||||||
activeController.set(ctrl);
|
activeController.set(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void didUnlock(Vault vault) {
|
public void didUnlock(Vault vault) {
|
||||||
if (vault.equals(selectedVault.getValue())) {
|
if (vault.equals(selectedVault.getValue())) {
|
||||||
this.showUnlockedView(vault);
|
this.showUnlockedView(vault, vault.getVaultSettings().revealAfterMount().getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUnlockedView(Vault vault) {
|
private void showUnlockedView(Vault vault, boolean reveal) {
|
||||||
final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> {
|
final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> viewControllerLoader.load("/fxml/unlocked.fxml"));
|
||||||
return viewControllerLoader.load("/fxml/unlocked.fxml");
|
|
||||||
});
|
|
||||||
ctrl.setVault(vault);
|
ctrl.setVault(vault);
|
||||||
ctrl.setListener(this::didLock);
|
ctrl.setListener(this::didLock);
|
||||||
|
if (reveal) {
|
||||||
|
ctrl.revealVault(vault);
|
||||||
|
}
|
||||||
activeController.set(ctrl);
|
activeController.set(ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void didLock(UnlockedController ctrl) {
|
public void didLock(UnlockedController ctrl) {
|
||||||
unlockedVaults.remove(ctrl.getVault());
|
unlockedVaults.remove(ctrl.getVault());
|
||||||
showUnlockView();
|
if (ctrl.getVault().getId() == selectedVault.get().getId()) {
|
||||||
|
showUnlockView(UnlockController.State.UNLOCKING);
|
||||||
|
}
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showChangePasswordView() {
|
private void showChangePasswordView() {
|
||||||
@@ -453,10 +543,12 @@ public class MainController implements ViewController {
|
|||||||
ctrl.setVault(selectedVault.get());
|
ctrl.setVault(selectedVault.get());
|
||||||
ctrl.setListener(this::didChangePassword);
|
ctrl.setListener(this::didChangePassword);
|
||||||
activeController.set(ctrl);
|
activeController.set(ctrl);
|
||||||
|
Platform.runLater(ctrl::focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void didChangePassword() {
|
public void didChangePassword() {
|
||||||
showUnlockView();
|
showUnlockView(UnlockController.State.PASSWORD_CHANGED);
|
||||||
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
|
||||||
@Singleton
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@FxApplicationScoped
|
||||||
public class NotFoundController implements ViewController {
|
public class NotFoundController implements ViewController {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
|||||||
@@ -2,28 +2,19 @@
|
|||||||
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
* Copyright (c) 2014, 2017 Sebastian Stenzel
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Sebastian Stenzel - initial API and implementation
|
* Sebastian Stenzel - initial API and implementation
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
|
|
||||||
import com.google.common.base.CharMatcher;
|
import com.google.common.base.CharMatcher;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Group;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
@@ -32,8 +23,19 @@ import javafx.scene.control.Label;
|
|||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.FxApplicationScoped;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.Volume;
|
||||||
|
|
||||||
@Singleton
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@FxApplicationScoped
|
||||||
public class SettingsController implements ViewController {
|
public class SettingsController implements ViewController {
|
||||||
|
|
||||||
private static final CharMatcher DIGITS_MATCHER = CharMatcher.inRange('0', '9');
|
private static final CharMatcher DIGITS_MATCHER = CharMatcher.inRange('0', '9');
|
||||||
@@ -47,11 +49,17 @@ public class SettingsController implements ViewController {
|
|||||||
this.localization = localization;
|
this.localization = localization;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.applicationVersion = applicationVersion;
|
this.applicationVersion = applicationVersion;
|
||||||
|
this.webdavSettings = new Group();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private CheckBox checkForUpdatesCheckbox;
|
private CheckBox checkForUpdatesCheckbox;
|
||||||
|
|
||||||
|
private Group webdavSettings;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label portFieldLabel;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField portField;
|
private TextField portField;
|
||||||
|
|
||||||
@@ -67,6 +75,9 @@ public class SettingsController implements ViewController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ChoiceBox<String> prefGvfsScheme;
|
private ChoiceBox<String> prefGvfsScheme;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ChoiceBox<VolumeImpl> volume;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private CheckBox debugModeCheckbox;
|
private CheckBox debugModeCheckbox;
|
||||||
|
|
||||||
@@ -75,22 +86,41 @@ public class SettingsController implements ViewController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion.orElse("SNAPSHOT")));
|
||||||
checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally());
|
checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally());
|
||||||
checkForUpdatesCheckbox.setSelected(settings.checkForUpdates().get() && !areUpdatesManagedExternally());
|
checkForUpdatesCheckbox.setSelected(settings.checkForUpdates().get() && !areUpdatesManagedExternally());
|
||||||
|
|
||||||
|
//NIOADAPTER
|
||||||
|
volume.getItems().addAll(Volume.getCurrentSupportedAdapters());
|
||||||
|
volume.setValue(settings.preferredVolumeImpl().get());
|
||||||
|
volume.setConverter(new NioAdapterImplStringConverter());
|
||||||
|
volume.valueProperty().addListener(this::setVisibilityGvfsElements);
|
||||||
|
|
||||||
|
//WEBDAV
|
||||||
|
webdavSettings.visibleProperty().bind(volume.valueProperty().isEqualTo(VolumeImpl.WEBDAV));
|
||||||
|
webdavSettings.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
prefGvfsScheme.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
prefGvfsSchemeLabel.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
portFieldLabel.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
portFieldLabel.visibleProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
changePortButton.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
portField.managedProperty().bind(webdavSettings.visibleProperty());
|
||||||
|
portField.visibleProperty().bind(webdavSettings.visibleProperty());
|
||||||
portField.setText(String.valueOf(settings.port().intValue()));
|
portField.setText(String.valueOf(settings.port().intValue()));
|
||||||
portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents);
|
portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents);
|
||||||
changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty()));
|
changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty()));
|
||||||
changePortButton.disableProperty().bind(Bindings.createBooleanBinding(this::isPortValid, portField.textProperty()).not());
|
changePortButton.disableProperty().bind(Bindings.createBooleanBinding(this::isPortValid, portField.textProperty()).not());
|
||||||
versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion.orElse("SNAPSHOT")));
|
|
||||||
prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX);
|
|
||||||
prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX);
|
|
||||||
prefGvfsScheme.getItems().add("dav");
|
prefGvfsScheme.getItems().add("dav");
|
||||||
prefGvfsScheme.getItems().add("webdav");
|
prefGvfsScheme.getItems().add("webdav");
|
||||||
prefGvfsScheme.setValue(settings.preferredGvfsScheme().get());
|
prefGvfsScheme.setValue(settings.preferredGvfsScheme().get());
|
||||||
|
prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX);
|
||||||
|
prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX);
|
||||||
|
|
||||||
debugModeCheckbox.setSelected(settings.debugMode().get());
|
debugModeCheckbox.setSelected(settings.debugMode().get());
|
||||||
|
|
||||||
settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty());
|
settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty());
|
||||||
settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty());
|
settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty());
|
||||||
|
settings.preferredVolumeImpl().bind(volume.valueProperty());
|
||||||
settings.debugMode().bind(debugModeCheckbox.selectedProperty());
|
settings.debugMode().bind(debugModeCheckbox.selectedProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +130,7 @@ public class SettingsController implements ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void changePort(ActionEvent evt) {
|
private void changePort() {
|
||||||
assert isPortValid() : "Button must be disabled, if port is invalid.";
|
assert isPortValid() : "Button must be disabled, if port is invalid.";
|
||||||
try {
|
try {
|
||||||
int port = Integer.parseInt(portField.getText());
|
int port = Integer.parseInt(portField.getText());
|
||||||
@@ -113,11 +143,8 @@ public class SettingsController implements ViewController {
|
|||||||
private boolean isPortValid() {
|
private boolean isPortValid() {
|
||||||
try {
|
try {
|
||||||
int port = Integer.parseInt(portField.getText());
|
int port = Integer.parseInt(portField.getText());
|
||||||
if (port == 0 || port >= Settings.MIN_PORT && port <= Settings.MAX_PORT) {
|
return port == 0 // choose port automatically
|
||||||
return true;
|
|| port >= Settings.MIN_PORT && port <= Settings.MAX_PORT; // port within range
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -129,8 +156,26 @@ public class SettingsController implements ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setVisibilityGvfsElements(@SuppressWarnings("unused") Observable obs, @SuppressWarnings("unused")Object oldValue, Object newValue) {
|
||||||
|
prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV"));
|
||||||
|
prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV"));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean areUpdatesManagedExternally() {
|
private boolean areUpdatesManagedExternally() {
|
||||||
return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false"));
|
return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class NioAdapterImplStringConverter extends StringConverter<VolumeImpl> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(VolumeImpl object) {
|
||||||
|
return object.getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VolumeImpl fromString(String string) {
|
||||||
|
return VolumeImpl.forDisplayName(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user