mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ad496f41 | ||
|
|
a272bf2614 | ||
|
|
119e0724d5 | ||
|
|
76a9cb9a06 | ||
|
|
acb8d4cd0c | ||
|
|
ffe8887114 | ||
|
|
d8c8c2380a | ||
|
|
fe5ce79802 | ||
|
|
bf7a8686a6 | ||
|
|
143070d02d | ||
|
|
06827a7466 | ||
|
|
5add862ce8 | ||
|
|
2b8d7c6c3b | ||
|
|
f5da13d3b4 | ||
|
|
03dfd3e887 | ||
|
|
8241559362 | ||
|
|
c289040624 | ||
|
|
767acbd922 | ||
|
|
ccd4cedb08 | ||
|
|
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 |
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
- 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 [FAQ](https://community.cryptomator.org/c/faq).
|
- 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).
|
||||||
|
|
||||||
## Did you write a patch that fixes a bug?
|
## Did you write a patch that fixes a bug?
|
||||||
@@ -18,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/develop/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
|
||||||
|
|
||||||
@@ -4,8 +4,8 @@ Before creating a new issue make sure that you
|
|||||||
- searched existing (and closed) issues: https://github.com/cryptomator/cryptomator/issues
|
- searched existing (and closed) issues: https://github.com/cryptomator/cryptomator/issues
|
||||||
- searched the knowledge base: https://community.cryptomator.org/c/kb
|
- 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 support guide: https://github.com/cryptomator/cryptomator/blob/develop/SUPPORT.md
|
||||||
- have read the contribution guide: https://github.com/cryptomator/cryptomator/blob/develop/CONTRIBUTING.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/CODE_OF_CONDUCT.md
|
- have read the code of conduct: https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
## Basic Info
|
## Basic Info
|
||||||
|
|
||||||
@@ -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?
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
+17
-9
@@ -1,7 +1,7 @@
|
|||||||
language: java
|
language: java
|
||||||
sudo: false
|
sudo: false
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk9
|
- oraclejdk10
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
- continuous # To avoid infinite loops, as this tag is created by this Travis config
|
- continuous # To avoid infinite loops, as this tag is created by this Travis config
|
||||||
@@ -14,6 +14,7 @@ env:
|
|||||||
- secure: "lV9OwUbHMrMpLUH1CY+Z4puLDdFXytudyPlG1eGRsesdpuG6KM3uQVz6uAtf6lrU8DRbMM/T7ML+PmvQ4UoPPYLdLxESLLBat2qUPOIVBOhTSlCc7I0DmGy04CSvkeMy8dPaQC0ukgNiR7zwoNzfcpGRN/U9S8tziDruuHoZSrg=" # 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
|
- secure: "zJxgytA2Ks5Xzv+7kUaUq+EBFNQw9Qec63lcMJVuXVWczjL16nKW1EzzV515ag+OWL46z3lEPForDhufw0VtFnNmaX68jkO0mp01eLrHApc1llN2Y/U8GBXfNNazN4+Kom4H+z/AO+wJr8EsKMMUczCdQ3APgd9uVI0hzXw/Z3M=" # GITHUB_API_KEY
|
||||||
|
- secure: "PiH/o9MLOyPdjIwECIEfj3TuUxx7QB0CIs3o1WXjqb1PbDdHDbQyvswYit6xDw9NrJp/A+ov2k00jq+n+8fLTSd4AY21y5WiJN/ccCTWUuUiFhGxOyM37aeWAPAn4rUp7D7o8jLxEdpGZAfglIzaz+GCEQYxfV/w3FDwztViXgQ=" # GPG_PASSPHRASE
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
@@ -28,13 +29,8 @@ install:
|
|||||||
- curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/e60ee486e34ee0c79f89f947abe2c83b4290c6bb/settings.xml
|
- curl -o $HOME/.m2/settings.xml https://gist.githubusercontent.com/cryptobot/cf5fbd909c4782aaeeeb7c7f4a1a43da/raw/e60ee486e34ee0c79f89f947abe2c83b4290c6bb/settings.xml
|
||||||
- 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 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
|
||||||
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:
|
|
||||||
- jdk_switcher use oraclejdk8
|
|
||||||
- curl -o ~/codacy-coverage-reporter-assembly-latest.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/2.0.1/codacy-coverage-reporter-2.0.1-assembly.jar
|
|
||||||
- $JAVA_HOME/bin/java -cp ~/codacy-coverage-reporter-assembly-latest.jar com.codacy.CodacyCoverageReporter -l Java -r main/jacoco-report/target/site/jacoco-aggregate/jacoco.xml
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
- jdk_switcher use oraclejdk9
|
|
||||||
- |
|
- |
|
||||||
if [[ $TRAVIS_BRANCH == "develop" ]] && [[ $TRAVIS_PULL_REQUEST == "false" ]]; then
|
if [[ $TRAVIS_BRANCH == "develop" ]] && [[ $TRAVIS_PULL_REQUEST == "false" ]]; then
|
||||||
CONTINUOUS_RELEASE_URL=`curl -s https://api.github.com/repos/cryptomator/cryptomator/releases/tags/continuous | jq -re '.url'`
|
CONTINUOUS_RELEASE_URL=`curl -s https://api.github.com/repos/cryptomator/cryptomator/releases/tags/continuous | jq -re '.url'`
|
||||||
@@ -54,6 +50,8 @@ before_deploy:
|
|||||||
git remote remove gh
|
git remote remove gh
|
||||||
fi
|
fi
|
||||||
- mvn -fmain/pom.xml clean package -Prelease -DskipTests
|
- mvn -fmain/pom.xml clean package -Prelease -DskipTests
|
||||||
|
- gpg --import 34C80F11.gpg
|
||||||
|
- gpg --detach-sign -a -u 34C80F11 --batch --passphrase ${GPG_PASSPHRASE} main/ant-kit/target/antkit.zip
|
||||||
deploy:
|
deploy:
|
||||||
- provider: releases # CONTINUOUS
|
- provider: releases # CONTINUOUS
|
||||||
prerelease: true
|
prerelease: true
|
||||||
@@ -63,7 +61,8 @@ deploy:
|
|||||||
file_glob: true
|
file_glob: true
|
||||||
file:
|
file:
|
||||||
- "main/uber-jar/target/Cryptomator-*.jar"
|
- "main/uber-jar/target/Cryptomator-*.jar"
|
||||||
- "main/ant-kit/target/antkit.tar.gz"
|
- "main/ant-kit/target/antkit.zip"
|
||||||
|
- "main/ant-kit/target/antkit.zip.asc"
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
name: Cryptomator continuous build
|
name: Cryptomator continuous build
|
||||||
body: Automatically built on $(date +'%F %T %Z').
|
body: Automatically built on $(date +'%F %T %Z').
|
||||||
@@ -76,7 +75,8 @@ deploy:
|
|||||||
api_key: $GITHUB_API_KEY
|
api_key: $GITHUB_API_KEY
|
||||||
file:
|
file:
|
||||||
- "main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar"
|
- "main/uber-jar/target/Cryptomator-$TRAVIS_TAG.jar"
|
||||||
- "main/ant-kit/target/antkit.tar.gz"
|
- "main/ant-kit/target/antkit.zip"
|
||||||
|
- "main/ant-kit/target/antkit.zip.asc"
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
repo: cryptomator/cryptomator
|
repo: cryptomator/cryptomator
|
||||||
@@ -91,3 +91,11 @@ deploy:
|
|||||||
on:
|
on:
|
||||||
repo: cryptomator/cryptomator
|
repo: cryptomator/cryptomator
|
||||||
tags: true
|
tags: true
|
||||||
|
after_script:
|
||||||
|
- jdk_switcher use oraclejdk8
|
||||||
|
- curl -o ~/codacy-coverage-reporter-assembly-latest.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/4.0.1/codacy-coverage-reporter-4.0.1-assembly.jar
|
||||||
|
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter-assembly-latest.jar report -l Java -r main/commons/target/site/jacoco/jacoco.xml --partial
|
||||||
|
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter-assembly-latest.jar report -l Java -r main/keychain/target/site/jacoco/jacoco.xml --partial
|
||||||
|
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter-assembly-latest.jar report -l Java -r main/ui/target/site/jacoco/jacoco.xml --partial
|
||||||
|
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter-assembly-latest.jar report -l Java -r main/launcher/target/site/jacoco/jacoco.xml --partial
|
||||||
|
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter-assembly-latest.jar final
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: GnuPG v1
|
||||||
|
|
||||||
|
lQc+BFdtLXQBEACzObgsAnfD2JInQ2J7BDv0kARpfDLaNIbQJxdnSUZxJk7yOGge
|
||||||
|
64juAzkIDBq4jE5fy3ErZCHaEceDj/mVaAvIlygrQ5KZGzFCi9dZXWjKW/VBvVm0
|
||||||
|
hPUbr0NWTEMnZSXCAL03L1LVjHjfSMRAwl9gClwlff9eKW8gVIG+2gww+wQO0xnY
|
||||||
|
y6DO6xEtfxxz+hoHsygEDh+qpONSoffEWhoTdn6qh6jJ72sOi6azGqFA30mxWM6i
|
||||||
|
YNp3zrH0q8IrjS1KqzaVg7frfeok5CtPZCgdfDNBDZA/Dfbu1BxpRWEleDMLJ6zz
|
||||||
|
lh4MIKC6RqDLjkzpygP+r7XKPve/hJ6nhH9FQZqASlrjn8KheaOcLqRgScDmC9Bq
|
||||||
|
VbxgieBUpVfjj8KclxiQh51jYHPIp/1QIgwvGEY5R1Wb8QzLnNkIxWzLDKzAG5XB
|
||||||
|
F1jb5JMmwoK9dwv+X9jiPh/xZpu6dh/5o13NQoSS6/p7AdlRXyDiaNtij0VOP1Jt
|
||||||
|
UlSCPp01peyLlKr8cDdI1UQadYpbmOzMplDKD2Yf+QSiyYdIYPPrfn8tNUyhbZH8
|
||||||
|
jATI26l1ctL133Gd9o0SYoBleNPNiQqhk9T0AcaOHujQDH/dM0+9c5w/zgPkDFpQ
|
||||||
|
S/uMFEq4yMok4AARd8qBRATrGYL2sFtB4HKEXtQw/SlaaGL41EJyvpZnbwARAQAB
|
||||||
|
/gMDAoX3Q3p+1aU3YKQDwxMDDPerrO0eT7bUGdo9A3usVEHSZ/uG2hcIHYpgW/j3
|
||||||
|
nNmaoLVDEgkbpPub7tlCr6+e26J53otqYXmLSWxx0jmFGivSVRi6nyZz+HUKObCE
|
||||||
|
qi70z3fEZ8rdL6Rj24UnIHvu+9RvoW8GeoNZoy7G/hC60mPSQUvU0DE9NfMUiZVx
|
||||||
|
nxXqLlR/rwOOIMmW5FFPCFRe18oRuLZ/+AcN+COjciGE3Fj7fpCybc2rKHKsKIO9
|
||||||
|
OkMYhuHZSSxjh8Dsfaen7XOwp5S+UashEhV5d9NaSvpEgdCtH+JwAHrXOlJrUU4x
|
||||||
|
faRkJsFoej6DXdRL/vr91d2eB60A5Qtg3H+/9n/TQW/Low487h/j6Lod6JxoeYKj
|
||||||
|
L+UmIGprSn4WZYwJiLfBB/v3T33e4cHGwMiGyU4aR8KhMsv5c8UmgDqpFkeY7Sqw
|
||||||
|
accRDujoaIZviSlPgLG7UneqVk8+WR4LFtCwKXFyObTpx9JyzPYUgIj49WzHDAK+
|
||||||
|
TnYJROiC+HGetaRRG0lSS6o6eoBe+YXBl0y84+htQ8YPojDdOZ68tNbfrWKbbxG7
|
||||||
|
uOWquP30+Z+J8tlL/3o4cK3x9rYNkroreR1dA9He9Jev3/z3GzuuZAmLSIRlh6Cd
|
||||||
|
yPbn1kOfEl3L03Ty5sriJwSgdindDm3wiem0E+lkUSIYvrGnCVOPTZsTqe9+0DJz
|
||||||
|
KW5bzzIJRaCylT0ec9GTSqGvMIpqoFH8yuf5k0w0pkjhLUqDEKxgnvn2lp79eKjn
|
||||||
|
W22VHecGv98867IS7Y150ch6YSHCAcfT2D2LFD8B9NmSI3ue8BOwfq5nLEtGYmyb
|
||||||
|
+PBHKUGxU77xlaywVyxA451ok/3plg9fiGwCU605zdpAfoO1TToIi9/m3EDoJ6iv
|
||||||
|
rmV+Ox9O30W7B5hdHNgt4Lkf1LmQNatokzTcKjNIOJiWpQLgu619tMEEtIuHE7rF
|
||||||
|
lQZsVBYLHpmAZo1oQ5AqCQXlTdAp6l/ZzSh2sU5dATAxAy3hCM8zs2AawBrr9jdP
|
||||||
|
Xsj1A5aSQN/V4cbRg0VS4C0i/Z5t3Q2r+casMv3KCe+4wIwfkzeMPxRCzC4svMvp
|
||||||
|
FjnwbBUi+I3PvrOpFtIQo0hXM5wNZFujqR2nR6A+NcvfDyM30UaAs8KeSDvrDZrk
|
||||||
|
bt4W4NOfDxJrQLEeej9lMuZdI1SaoRFM2C8DO1045FKcGBeQWj5rfG9szbF2Bjp5
|
||||||
|
ZXY7yJiBi4bnl7FlJbcB6yO1N21wDmzfULx7XyyNAxQNyfId0Mcd6QKgNBsHnWsq
|
||||||
|
YPxfyCuP21G4Rhfg+Dsp4N5EUdczKFfAz+JyMkLp902OE+dM6y8Qim5HgLWsfKnO
|
||||||
|
5A6O18um2aMSSC01hmSZB6bAVH2yF66K+eBp2B1QkStNOKBel4xM0XmXfPfSxyrF
|
||||||
|
+cjTtyThstjZWUGCujGCmNyvN49/ouMkLLvLrKcN/ecZN6+ZsJokIdo1KJ2RPNbm
|
||||||
|
2I5lEKh3piZLMAim5aWQClS+GfF9UVR1jpGisQlw//cE8Afax1XASOaAiXWa7PzR
|
||||||
|
dss9SfASndOjWiB2FAyDsvqtn5vyDE/1UQHxkyNZyrA8IhPk5ytn7WTz+rczM6ZQ
|
||||||
|
cME1EMdwBVsftS/S/oTDRX9ms/78QLMbBA7qB+aQ5atQGvsV9TrIWeAv2AlYa/f7
|
||||||
|
3AFhPPY1XmmhNo1IwHQmVMoUFCI73X8LZnHHIXVV6zyGFfC/+2EwFu/c1wm9y2ct
|
||||||
|
wvHXLSm1cQNAFvSSNMeeLhpXImy1UFwbUulYZ44d//zQtDZDcnlwdG9ib3QgKFJl
|
||||||
|
bGVhc2UgTWFuYWdlcikgPHJlbGVhc2VzQGNyeXB0b21hdG9yLm9yZz6JAj4EEwEC
|
||||||
|
ACgFAldtLXQCGwMFCQWjmoAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFCc
|
||||||
|
nWM0yA8RW44P/2tDxJz5Ps4xYRIZie2gXZuK0Q6mpQATYX+5IZyAPBUdO5PmvwnI
|
||||||
|
CWI4uGxosJxtM9eL6iiOMIN6cxQdOMDQtdmeOW/CYM731TjtSNseIre/8Kc4O9uE
|
||||||
|
JxeeoT3Os7QUNUHMxLhfAI1gaJcYsSAJqvmLpiyOoH20n7FVxKv8B4JqG9b4zK8H
|
||||||
|
Ol9oQXMnfZA9XIYciOE2EpVD4xLg2+v4qbFaLM86ogA6xjdsImD+HqMO0icnh+Vo
|
||||||
|
Bf2EiwUieWemX6kBh37zuoQWWX1O+9BWvJ0Rk8xDWUM6dGU7Z/+cwXzz7UuXOxWT
|
||||||
|
YQlYLLgaC8HexyhrDAkOvcKOQpBYQIL0etovhzc1ZIrhBJeVE/5XsxdbLzc+adqB
|
||||||
|
SXhtrKeROklyyxaZng3nhNWGeIjPfBwXCXD7w/vbdX5KWIBhQg35s+sN3rnY099p
|
||||||
|
7eYmNS5+C/x/iTNi5rZpOKw7tvTMZxWXANYDBbfxEhecLF0C/K2oR7pelUIVGdJD
|
||||||
|
XprHXJj12FmCsfIRzbbJ7pLUy8wVDZBVjcKqo4Z4zlVDrISo74MD/hs91SWwa86f
|
||||||
|
aAt44g1aWKjXcqXjNvgKGe1GIadp6qjQT1z5kw/mg0xp0s2Plq12Z05q0zv38kcj
|
||||||
|
ynE4o6vDZcXMBHF02jH8QknXfhvQRyq/CHaQoV9pATCvuJBLML9udRd4
|
||||||
|
=BRSB
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
@@ -46,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))
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<id>tarball</id>
|
<id>tarball</id>
|
||||||
<includeBaseDirectory>false</includeBaseDirectory>
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
<formats>
|
<formats>
|
||||||
<format>tar.gz</format>
|
<format>zip</format>
|
||||||
</formats>
|
</formats>
|
||||||
<fileSets>
|
<fileSets>
|
||||||
<fileSet>
|
<fileSet>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>ant-kit</artifactId>
|
<artifactId>ant-kit</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- create antkit.tar.gz: -->
|
<!-- create antkit.zip: -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.1.0</version>
|
||||||
|
|||||||
@@ -4,9 +4,14 @@
|
|||||||
|
|
||||||
<!-- Define application to build -->
|
<!-- Define application to build -->
|
||||||
<fx:application id="Cryptomator" name="Cryptomator" version="${project.version}" mainClass="org.cryptomator.launcher.Cryptomator" />
|
<fx:application id="Cryptomator" name="Cryptomator" version="${project.version}" mainClass="org.cryptomator.launcher.Cryptomator" />
|
||||||
|
|
||||||
|
<!-- Print build environment properties -->
|
||||||
|
<target name="check-env">
|
||||||
|
<echoproperties/>
|
||||||
|
</target>
|
||||||
|
|
||||||
<!-- Create main application jar -->
|
<!-- Create main application jar -->
|
||||||
<target name="create-jar">
|
<target name="create-jar" depends="check-env">
|
||||||
<fx:jar destfile="antbuild/Cryptomator-${project.version}.jar">
|
<fx:jar destfile="antbuild/Cryptomator-${project.version}.jar">
|
||||||
<fx:application refid="Cryptomator" />
|
<fx:application refid="Cryptomator" />
|
||||||
<fx:fileset dir="libs" includes="launcher-${project.version}.jar" />
|
<fx:fileset dir="libs" includes="launcher-${project.version}.jar" />
|
||||||
@@ -21,27 +26,26 @@
|
|||||||
</fx:jar>
|
</fx:jar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- Create Image package -->
|
<!-- Create Image -->
|
||||||
<target name="image" depends="create-jar">
|
<target name="image" depends="create-jar">
|
||||||
<fx:deploy nativeBundles="image" outdir="antbuild" verbose="true">
|
<fx:deploy nativeBundles="image" outdir="antbuild" verbose="true">
|
||||||
<fx:application refid="Cryptomator" />
|
<fx:application refid="Cryptomator" />
|
||||||
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="GPL" category="Utility"/>
|
<fx:info title="Cryptomator" vendor="cryptomator.org" copyright="cryptomator.org" license="GPL" category="Utility"/>
|
||||||
<fx:platform j2se="9.0">
|
<fx:platform j2se="10">
|
||||||
<fx:property name="logback.configurationFile" value="logback.xml" />
|
<fx:property name="logback.configurationFile" value="\${antbuild.logback.configurationFile}" />
|
||||||
<fx:property name="cryptomator.settingsPath" value="~/.Cryptomator/settings.json" />
|
<fx:property name="cryptomator.settingsPath" value="\${antbuild.cryptomator.settingsPath}" />
|
||||||
<fx:property name="cryptomator.ipcPortPath" value="~/.Cryptomator/ipcPort.bin" />
|
<fx:property name="cryptomator.ipcPortPath" value="\${antbuild.cryptomator.ipcPortPath}" />
|
||||||
|
<fx:property name="cryptomator.keychainPath" value="\${antbuild.cryptomator.keychainPath}"/>
|
||||||
<fx:jvmarg value="-Xss2m"/>
|
<fx:jvmarg value="-Xss2m"/>
|
||||||
<fx:jvmarg value="-Xmx512m"/>
|
<fx:jvmarg value="-Xmx512m"/>
|
||||||
<fx:jvmarg value="--add-modules"/>
|
|
||||||
<fx:jvmarg value="jdk.incubator.httpclient"/>
|
|
||||||
</fx:platform>
|
</fx:platform>
|
||||||
<fx:resources>
|
<fx:resources>
|
||||||
<fx:fileset dir="." type="data" includes="logback.xml" />
|
|
||||||
<fx:fileset dir="antbuild" type="jar" includes="Cryptomator-${project.version}.jar" />
|
<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="libs" type="jar" includes="*.jar" excludes="launcher-${project.version}.jar"/>
|
||||||
</fx:resources>
|
</fx:resources>
|
||||||
<fx:permissions elevated="false" />
|
<fx:permissions elevated="false" />
|
||||||
<fx:preferences install="true" />
|
<fx:preferences install="true" />
|
||||||
|
<fx:bundleArgument arg="dropinResourcesRoot" value="\${antbuild.dropinResourcesRoot}"/>
|
||||||
</fx:deploy>
|
</fx:deploy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
<name>Cryptomator Commons</name>
|
<name>Cryptomator Commons</name>
|
||||||
|
|||||||
@@ -8,39 +8,34 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
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.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
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_VOLUME_IMPL = VolumeImpl.FUSE;
|
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> volumeImpl = new SimpleObjectProperty<>(DEFAULT_VOLUME_IMPL);
|
private final ObjectProperty<VolumeImpl> preferredVolumeImpl = new SimpleObjectProperty<>(DEFAULT_PREFERRED_VOLUME_IMPL);
|
||||||
|
|
||||||
private Consumer<Settings> saveCmd;
|
private Consumer<Settings> saveCmd;
|
||||||
|
|
||||||
@@ -49,12 +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);
|
||||||
volumeImpl.addListener(this::somethingChanged);
|
preferredVolumeImpl.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||||
@@ -77,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;
|
||||||
}
|
}
|
||||||
@@ -97,8 +97,8 @@ public class Settings {
|
|||||||
return debugMode;
|
return debugMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectProperty<VolumeImpl> volumeImpl() {
|
public ObjectProperty<VolumeImpl> preferredVolumeImpl() {
|
||||||
return volumeImpl;
|
return preferredVolumeImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-12
@@ -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,12 +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("volumeImpl").value(value.volumeImpl().get().name());
|
out.name("preferredVolumeImpl").value(value.preferredVolumeImpl().get().name());
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +56,9 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
case "directories":
|
case "directories":
|
||||||
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
settings.getDirectories().addAll(readVaultSettingsArray(in));
|
||||||
break;
|
break;
|
||||||
|
case "askedForUpdateCheck":
|
||||||
|
settings.askedForUpdateCheck().set(in.nextBoolean());
|
||||||
|
break;
|
||||||
case "checkForUpdatesEnabled":
|
case "checkForUpdatesEnabled":
|
||||||
settings.checkForUpdates().set(in.nextBoolean());
|
settings.checkForUpdates().set(in.nextBoolean());
|
||||||
break;
|
break;
|
||||||
@@ -71,8 +74,8 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
case "debugMode":
|
case "debugMode":
|
||||||
settings.debugMode().set(in.nextBoolean());
|
settings.debugMode().set(in.nextBoolean());
|
||||||
break;
|
break;
|
||||||
case "volumeImpl":
|
case "preferredVolumeImpl":
|
||||||
settings.volumeImpl().set(parseNioAdapterName(in.nextString()));
|
settings.preferredVolumeImpl().set(parsePreferredVolumeImplName(in.nextString()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
LOG.warn("Unsupported vault setting found in JSON: " + name);
|
||||||
@@ -84,11 +87,11 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VolumeImpl parseNioAdapterName(String nioAdapterName) {
|
private VolumeImpl parsePreferredVolumeImplName(String nioAdapterName) {
|
||||||
try {
|
try {
|
||||||
return VolumeImpl.valueOf(nioAdapterName);
|
return VolumeImpl.valueOf(nioAdapterName);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
return Settings.DEFAULT_VOLUME_IMPL;
|
return Settings.DEFAULT_PREFERRED_VOLUME_IMPL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import javafx.beans.property.StringProperty;
|
|||||||
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_INDIVIDUAL_MOUNTPATH = false;
|
||||||
|
|
||||||
@@ -35,7 +34,6 @@ public class VaultSettings {
|
|||||||
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 BooleanProperty usesIndividualMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
|
||||||
private final StringProperty individualMountPath = new SimpleStringProperty();
|
private final StringProperty individualMountPath = new SimpleStringProperty();
|
||||||
@@ -47,7 +45,7 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, mountAfterUnlock, revealAfterMount, usesIndividualMountPath, individualMountPath};
|
return new Observable[]{path, mountName, winDriveLetter, unlockAfterStartup, revealAfterMount, usesIndividualMountPath, individualMountPath};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deriveMountNameFromPath(Path path) {
|
private void deriveMountNameFromPath(Path path) {
|
||||||
@@ -118,10 +116,6 @@ public class VaultSettings {
|
|||||||
return unlockAfterStartup;
|
return unlockAfterStartup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty mountAfterUnlock() {
|
|
||||||
return mountAfterUnlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty revealAfterMount() {
|
public BooleanProperty revealAfterMount() {
|
||||||
return revealAfterMount;
|
return revealAfterMount;
|
||||||
}
|
}
|
||||||
|
|||||||
-6
@@ -25,7 +25,6 @@ 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("usesIndividualMountPath").value(value.usesIndividualMountPath().get());
|
||||||
//TODO: should this always be written? ( because it could contain metadata, which the user does not want to save!)
|
//TODO: should this always be written? ( because it could contain metadata, which the user does not want to save!)
|
||||||
@@ -40,7 +39,6 @@ class VaultSettingsJsonAdapter {
|
|||||||
String individualMountPath = 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 usesIndividualMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
|
||||||
|
|
||||||
@@ -63,9 +61,6 @@ class VaultSettingsJsonAdapter {
|
|||||||
case "unlockAfterStartup":
|
case "unlockAfterStartup":
|
||||||
unlockAfterStartup = in.nextBoolean();
|
unlockAfterStartup = in.nextBoolean();
|
||||||
break;
|
break;
|
||||||
case "mountAfterUnlock":
|
|
||||||
mountAfterUnlock = in.nextBoolean();
|
|
||||||
break;
|
|
||||||
case "revealAfterMount":
|
case "revealAfterMount":
|
||||||
revealAfterMount = in.nextBoolean();
|
revealAfterMount = in.nextBoolean();
|
||||||
break;
|
break;
|
||||||
@@ -87,7 +82,6 @@ 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.usesIndividualMountPath().set(usesIndividualMountPath);
|
||||||
vaultSettings.individualMountPath().set(individualMountPath);
|
vaultSettings.individualMountPath().set(individualMountPath);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public enum VolumeImpl {
|
public enum VolumeImpl {
|
||||||
WEBDAV("WebDAV"),
|
WEBDAV("WebDAV"),
|
||||||
FUSE("FUSE");
|
FUSE("FUSE"),
|
||||||
|
DOKANY("DOKANY");
|
||||||
|
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -22,7 +22,7 @@ public class SettingsJsonAdapterTest {
|
|||||||
+ "\"checkForUpdatesEnabled\": true,"//
|
+ "\"checkForUpdatesEnabled\": true,"//
|
||||||
+ "\"port\": 8080,"//
|
+ "\"port\": 8080,"//
|
||||||
+ "\"numTrayNotifications\": 42,"//
|
+ "\"numTrayNotifications\": 42,"//
|
||||||
+ "\"volumeImpl\": \"FUSE\"}";
|
+ "\"preferredVolumeImpl\": \"FUSE\"}";
|
||||||
|
|
||||||
Settings settings = adapter.fromJson(json);
|
Settings settings = adapter.fromJson(json);
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ public class SettingsJsonAdapterTest {
|
|||||||
Assert.assertEquals(8080, settings.port().get());
|
Assert.assertEquals(8080, settings.port().get());
|
||||||
Assert.assertEquals(42, settings.numTrayNotifications().get());
|
Assert.assertEquals(42, settings.numTrayNotifications().get());
|
||||||
Assert.assertEquals("dav", settings.preferredGvfsScheme().get());
|
Assert.assertEquals("dav", settings.preferredGvfsScheme().get());
|
||||||
Assert.assertEquals(VolumeImpl.FUSE, settings.volumeImpl().get());
|
Assert.assertEquals(VolumeImpl.FUSE, settings.preferredVolumeImpl().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
/target/
|
|
||||||
@@ -1,50 +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.4.0-beta1</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>
|
|
||||||
</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>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>keychain</artifactId>
|
<artifactId>keychain</artifactId>
|
||||||
<name>System Keychain Access</name>
|
<name>System Keychain Access</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>launcher</artifactId>
|
<artifactId>launcher</artifactId>
|
||||||
<name>Cryptomator Launcher</name>
|
<name>Cryptomator Launcher</name>
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class Cryptomator {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to initiate inter-process communication.", e);
|
LOG.error("Failed to initiate inter-process communication.", e);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOG.error("Error during startup", e);
|
||||||
}
|
}
|
||||||
System.exit(0); // end remaining non-daemon threads.
|
System.exit(0); // end remaining non-daemon threads.
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-16
@@ -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.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Cryptomator</name>
|
<name>Cryptomator</name>
|
||||||
|
|
||||||
@@ -26,25 +26,26 @@
|
|||||||
<!-- dependency versions -->
|
<!-- dependency versions -->
|
||||||
<cryptomator.cryptolib.version>1.2.0</cryptomator.cryptolib.version>
|
<cryptomator.cryptolib.version>1.2.0</cryptomator.cryptolib.version>
|
||||||
<cryptomator.cryptofs.version>1.5.2</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>1.5.2</cryptomator.cryptofs.version>
|
||||||
<cryptomator.webdav.version>1.0.4</cryptomator.webdav.version>
|
|
||||||
<cryptomator.jni.version>2.0.0</cryptomator.jni.version>
|
<cryptomator.jni.version>2.0.0</cryptomator.jni.version>
|
||||||
<cryptomator.fuse.version>0.1.4</cryptomator.fuse.version>
|
<cryptomator.fuse.version>0.1.5</cryptomator.fuse.version>
|
||||||
|
<cryptomator.dokany.version>0.1.2</cryptomator.dokany.version>
|
||||||
|
<cryptomator.webdav.version>1.0.4</cryptomator.webdav.version>
|
||||||
|
|
||||||
<commons-io.version>2.5</commons-io.version>
|
<commons-io.version>2.5</commons-io.version>
|
||||||
<commons-lang3.version>3.6</commons-lang3.version>
|
<commons-lang3.version>3.6</commons-lang3.version>
|
||||||
|
|
||||||
<easybind.version>1.0.3</easybind.version>
|
<easybind.version>1.0.3</easybind.version>
|
||||||
|
|
||||||
<guava.version>24.1-jre</guava.version>
|
<guava.version>25.1-jre</guava.version>
|
||||||
<dagger.version>2.14.1</dagger.version>
|
<dagger.version>2.16</dagger.version>
|
||||||
<gson.version>2.8.2</gson.version>
|
<gson.version>2.8.5</gson.version>
|
||||||
|
|
||||||
<slf4j.version>1.7.25</slf4j.version>
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
|
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<junit.hierarchicalrunner.version>4.12.1</junit.hierarchicalrunner.version>
|
<junit.hierarchicalrunner.version>4.12.1</junit.hierarchicalrunner.version>
|
||||||
<mockito.version>2.11.0</mockito.version>
|
<mockito.version>2.19.0</mockito.version>
|
||||||
<hamcrest.version>1.3</hamcrest.version> <!-- keep in sync with version required by JUnit -->
|
<hamcrest.version>1.3</hamcrest.version> <!-- keep in sync with version required by JUnit -->
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -59,6 +60,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>
|
||||||
@@ -101,6 +106,11 @@
|
|||||||
<artifactId>fuse-nio-adapter</artifactId>
|
<artifactId>fuse-nio-adapter</artifactId>
|
||||||
<version>${cryptomator.fuse.version}</version>
|
<version>${cryptomator.fuse.version}</version>
|
||||||
</dependency>
|
</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>
|
||||||
@@ -245,9 +255,6 @@
|
|||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
<id>coverage</id>
|
<id>coverage</id>
|
||||||
<modules>
|
|
||||||
<module>jacoco-report</module>
|
|
||||||
</modules>
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -289,6 +296,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>
|
||||||
@@ -304,9 +317,9 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.7.0</version>
|
<version>3.7.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>9</source>
|
<source>10</source>
|
||||||
<target>9</target>
|
<target>10</target>
|
||||||
<release>9</release>
|
<release>10</release>
|
||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>--add-modules</arg>
|
<arg>--add-modules</arg>
|
||||||
<arg>jdk.incubator.httpclient</arg>
|
<arg>jdk.incubator.httpclient</arg>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>uber-jar</artifactId>
|
<artifactId>uber-jar</artifactId>
|
||||||
<name>Single über jar with all dependencies</name>
|
<name>Single über jar with all dependencies</name>
|
||||||
|
|||||||
+12
-7
@@ -4,35 +4,40 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.4.0-beta1</version>
|
<version>1.4.0-beta2</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>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>fuse-nio-adapter</artifactId>
|
<artifactId>dokany-nio-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>webdav-nio-adapter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- CryptoLib -->
|
<!-- CryptoLib -->
|
||||||
|
|||||||
@@ -53,6 +53,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 +83,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 +119,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 +196,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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package org.cryptomator.ui;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -32,16 +34,38 @@ import org.fxmisc.easybind.EasyBind;
|
|||||||
@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class})
|
@Module(includes = {ViewControllerModule.class, CommonsModule.class, KeychainModule.class}, subcomponents = {VaultComponent.class})
|
||||||
public class UiModule {
|
public class UiModule {
|
||||||
|
|
||||||
|
private static final int NUM_SCHEDULER_THREADS = 4;
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Settings provideSettings(SettingsProvider settingsProvider) {
|
Settings provideSettings(SettingsProvider settingsProvider) {
|
||||||
return settingsProvider.get();
|
return settingsProvider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ScheduledExecutorService provideScheduledExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
||||||
|
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);
|
||||||
|
return executorService;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
ExecutorService provideExecutorService(@Named("shutdownTaskScheduler") Consumer<Runnable> shutdownTaskScheduler) {
|
||||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
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);
|
shutdownTaskScheduler.accept(executorService::shutdown);
|
||||||
return executorService;
|
return executorService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ 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();
|
||||||
|
|
||||||
@@ -395,7 +396,7 @@ 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()) {
|
||||||
@@ -494,22 +495,25 @@ public class MainController implements ViewController {
|
|||||||
|
|
||||||
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(UnlockController.State.UNLOCKING);
|
if (ctrl.getVault().getId() == selectedVault.get().getId()) {
|
||||||
|
showUnlockView(UnlockController.State.UNLOCKING);
|
||||||
|
}
|
||||||
activeController.get().focus();
|
activeController.get().focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,6 @@ public class SettingsController implements ViewController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ChoiceBox<String> prefGvfsScheme;
|
private ChoiceBox<String> prefGvfsScheme;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Label volumeLabel;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ChoiceBox<VolumeImpl> volume;
|
private ChoiceBox<VolumeImpl> volume;
|
||||||
|
|
||||||
@@ -97,8 +94,7 @@ public class SettingsController implements ViewController {
|
|||||||
|
|
||||||
//NIOADAPTER
|
//NIOADAPTER
|
||||||
volume.getItems().addAll(getSupportedAdapters());
|
volume.getItems().addAll(getSupportedAdapters());
|
||||||
volume.setValue(settings.volumeImpl().get());
|
volume.setValue(settings.preferredVolumeImpl().get());
|
||||||
volume.setVisible(true);
|
|
||||||
volume.setConverter(new NioAdapterImplStringConverter());
|
volume.setConverter(new NioAdapterImplStringConverter());
|
||||||
|
|
||||||
//WEBDAV
|
//WEBDAV
|
||||||
@@ -127,13 +123,13 @@ public class SettingsController implements ViewController {
|
|||||||
|
|
||||||
settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty());
|
settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty());
|
||||||
settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty());
|
settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty());
|
||||||
settings.volumeImpl().bind(volume.valueProperty());
|
settings.preferredVolumeImpl().bind(volume.valueProperty());
|
||||||
settings.debugMode().bind(debugModeCheckbox.selectedProperty());
|
settings.debugMode().bind(debugModeCheckbox.selectedProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: how to implement this?
|
|
||||||
private VolumeImpl[] getSupportedAdapters() {
|
private VolumeImpl[] getSupportedAdapters() {
|
||||||
return new VolumeImpl[]{VolumeImpl.FUSE, VolumeImpl.WEBDAV};
|
// TODO: filter depending on supported drivers
|
||||||
|
return VolumeImpl.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,41 +8,21 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import javax.inject.Inject;
|
||||||
import java.nio.file.*;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import org.apache.commons.lang3.CharUtils;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.settings.VolumeImpl;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
|
||||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
|
||||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
|
||||||
import org.cryptomator.keychain.KeychainAccess;
|
|
||||||
import org.cryptomator.ui.controls.SecPasswordField;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.model.WindowsDriveLetters;
|
|
||||||
import org.cryptomator.ui.util.AsyncTaskService;
|
|
||||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.fxmisc.easybind.Subscription;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
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.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -59,8 +39,28 @@ import javafx.scene.control.ProgressIndicator;
|
|||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
import org.apache.commons.lang3.CharUtils;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.common.settings.VolumeImpl;
|
||||||
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||||
|
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
||||||
|
import org.cryptomator.keychain.KeychainAccess;
|
||||||
|
import org.cryptomator.ui.controls.SecPasswordField;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.model.WindowsDriveLetters;
|
||||||
|
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||||
|
import org.cryptomator.ui.util.Tasks;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.fxmisc.easybind.Subscription;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class UnlockController implements ViewController {
|
public class UnlockController implements ViewController {
|
||||||
|
|
||||||
@@ -73,23 +73,23 @@ public class UnlockController implements ViewController {
|
|||||||
|
|
||||||
private final Application app;
|
private final Application app;
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final AsyncTaskService asyncTaskService;
|
|
||||||
private final WindowsDriveLetters driveLetters;
|
private final WindowsDriveLetters driveLetters;
|
||||||
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
private final ChangeListener<Character> driveLetterChangeListener = this::winDriveLetterDidChange;
|
||||||
private final Optional<KeychainAccess> keychainAccess;
|
private final Optional<KeychainAccess> keychainAccess;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final ExecutorService executor;
|
||||||
private Vault vault;
|
private Vault vault;
|
||||||
private Optional<UnlockListener> listener = Optional.empty();
|
private Optional<UnlockListener> listener = Optional.empty();
|
||||||
private Subscription vaultSubs = Subscription.EMPTY;
|
private Subscription vaultSubs = Subscription.EMPTY;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UnlockController(Application app, Localization localization, AsyncTaskService asyncTaskService, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess, Settings settings) {
|
public UnlockController(Application app, Localization localization, WindowsDriveLetters driveLetters, Optional<KeychainAccess> keychainAccess, Settings settings, ExecutorService executor) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.localization = localization;
|
this.localization = localization;
|
||||||
this.asyncTaskService = asyncTaskService;
|
|
||||||
this.driveLetters = driveLetters;
|
this.driveLetters = driveLetters;
|
||||||
this.keychainAccess = keychainAccess;
|
this.keychainAccess = keychainAccess;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -107,9 +107,6 @@ public class UnlockController implements ViewController {
|
|||||||
@FXML
|
@FXML
|
||||||
private CheckBox savePassword;
|
private CheckBox savePassword;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private CheckBox mountAfterUnlock;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField mountName;
|
private TextField mountName;
|
||||||
|
|
||||||
@@ -159,8 +156,6 @@ public class UnlockController implements ViewController {
|
|||||||
public void initialize() {
|
public void initialize() {
|
||||||
advancedOptions.managedProperty().bind(advancedOptions.visibleProperty());
|
advancedOptions.managedProperty().bind(advancedOptions.visibleProperty());
|
||||||
unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
|
unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
|
||||||
mountName.disableProperty().bind(mountAfterUnlock.selectedProperty().not());
|
|
||||||
revealAfterMount.disableProperty().bind(mountAfterUnlock.selectedProperty().not());
|
|
||||||
mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
|
mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
|
||||||
mountName.textProperty().addListener(this::mountNameDidChange);
|
mountName.textProperty().addListener(this::mountNameDidChange);
|
||||||
savePassword.setDisable(!keychainAccess.isPresent());
|
savePassword.setDisable(!keychainAccess.isPresent());
|
||||||
@@ -186,7 +181,7 @@ public class UnlockController implements ViewController {
|
|||||||
winDriveLetterLabel.setManaged(false);
|
winDriveLetterLabel.setManaged(false);
|
||||||
winDriveLetter.setVisible(false);
|
winDriveLetter.setVisible(false);
|
||||||
winDriveLetter.setManaged(false);
|
winDriveLetter.setManaged(false);
|
||||||
if (VolumeImpl.WEBDAV.equals(settings.volumeImpl().get())) {
|
if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) {
|
||||||
useOwnMountPath.setVisible(false);
|
useOwnMountPath.setVisible(false);
|
||||||
useOwnMountPath.setManaged(false);
|
useOwnMountPath.setManaged(false);
|
||||||
mountPathLabel.setManaged(false);
|
mountPathLabel.setManaged(false);
|
||||||
@@ -250,12 +245,10 @@ public class UnlockController implements ViewController {
|
|||||||
}
|
}
|
||||||
VaultSettings vaultSettings = vault.getVaultSettings();
|
VaultSettings vaultSettings = vault.getVaultSettings();
|
||||||
unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get());
|
unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get());
|
||||||
mountAfterUnlock.setSelected(vaultSettings.mountAfterUnlock().get());
|
|
||||||
revealAfterMount.setSelected(vaultSettings.revealAfterMount().get());
|
revealAfterMount.setSelected(vaultSettings.revealAfterMount().get());
|
||||||
useOwnMountPath.setSelected(vaultSettings.usesIndividualMountPath().get());
|
useOwnMountPath.setSelected(vaultSettings.usesIndividualMountPath().get());
|
||||||
|
|
||||||
vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set));
|
vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set));
|
||||||
vaultSubs = vaultSubs.and(EasyBind.subscribe(mountAfterUnlock.selectedProperty(), vaultSettings.mountAfterUnlock()::set));
|
|
||||||
vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set));
|
vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set));
|
||||||
vaultSubs = vaultSubs.and(EasyBind.subscribe(useOwnMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set));
|
vaultSubs = vaultSubs.and(EasyBind.subscribe(useOwnMountPath.selectedProperty(), vaultSettings.usesIndividualMountPath()::set));
|
||||||
|
|
||||||
@@ -430,7 +423,7 @@ public class UnlockController implements ViewController {
|
|||||||
progressIndicator.setVisible(true);
|
progressIndicator.setVisible(true);
|
||||||
|
|
||||||
CharSequence password = passwordField.getCharacters();
|
CharSequence password = passwordField.getCharacters();
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
Tasks.create(() -> {
|
||||||
vault.unlock(password);
|
vault.unlock(password);
|
||||||
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
||||||
keychainAccess.get().storePassphrase(vault.getId(), password);
|
keychainAccess.get().storePassphrase(vault.getId(), password);
|
||||||
@@ -457,16 +450,16 @@ public class UnlockController implements ViewController {
|
|||||||
}).onError(ServerLifecycleException.class, e -> {
|
}).onError(ServerLifecycleException.class, e -> {
|
||||||
LOG.error("Unlock failed for technical reasons.", e);
|
LOG.error("Unlock failed for technical reasons.", e);
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||||
}).onError(IOException.class, e -> {
|
}).onError(Exception.class, e -> {
|
||||||
LOG.error("Unlock failed for technical reasons.", e);
|
LOG.error("Unlock failed for technical reasons.", e);
|
||||||
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
messageText.setText(localization.getString("unlock.errorMessage.unlockFailed"));
|
||||||
}).andFinally(() -> {
|
}).andFinally(() -> {
|
||||||
if (!savePassword.isSelected()) {
|
if (!savePassword.isSelected()) {
|
||||||
passwordField.swipe();
|
passwordField.swipe();
|
||||||
}
|
}
|
||||||
advancedOptions.setDisable(false);
|
advancedOptions.setDisable(false);
|
||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
}).run();
|
}).runOnce(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* callback */
|
/* callback */
|
||||||
|
|||||||
@@ -8,29 +8,13 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
import org.cryptomator.frontend.webdav.ServerLifecycleException;
|
import java.util.concurrent.ExecutorService;
|
||||||
import org.cryptomator.frontend.webdav.mount.Mounter.CommandFailedException;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.util.AsyncTaskService;
|
|
||||||
import org.cryptomator.ui.util.DialogBuilderUtil;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.Runnables;
|
|
||||||
|
|
||||||
import javafx.animation.Animation;
|
import javafx.animation.Animation;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.beans.binding.ObjectExpression;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
@@ -46,13 +30,19 @@ import javafx.scene.control.Alert;
|
|||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.input.Clipboard;
|
|
||||||
import javafx.scene.input.ClipboardContent;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.PopupWindow.AnchorLocation;
|
import javafx.stage.PopupWindow.AnchorLocation;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.util.DialogBuilderUtil;
|
||||||
|
import org.cryptomator.ui.util.Tasks;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
public class UnlockedController implements ViewController {
|
public class UnlockedController implements ViewController {
|
||||||
|
|
||||||
@@ -62,9 +52,8 @@ public class UnlockedController implements ViewController {
|
|||||||
private static final double IO_SAMPLING_INTERVAL = 0.25;
|
private static final double IO_SAMPLING_INTERVAL = 0.25;
|
||||||
|
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final AsyncTaskService asyncTaskService;
|
private final ExecutorService executor;
|
||||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||||
private final ObjectExpression<Vault.State> vaultState = ObjectExpression.objectExpression(EasyBind.select(vault).selectObject(Vault::stateProperty));
|
|
||||||
private Optional<LockListener> listener = Optional.empty();
|
private Optional<LockListener> listener = Optional.empty();
|
||||||
private Timeline ioAnimation;
|
private Timeline ioAnimation;
|
||||||
|
|
||||||
@@ -83,30 +72,17 @@ public class UnlockedController implements ViewController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ContextMenu moreOptionsMenu;
|
private ContextMenu moreOptionsMenu;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private MenuItem mountVaultMenuItem;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private MenuItem unmountVaultMenuItem;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private MenuItem revealVaultMenuItem;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private VBox root;
|
private VBox root;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UnlockedController(Localization localization, AsyncTaskService asyncTaskService) {
|
public UnlockedController(Localization localization, ExecutorService executor) {
|
||||||
this.localization = localization;
|
this.localization = localization;
|
||||||
this.asyncTaskService = asyncTaskService;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
mountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.UNLOCKED).not()); // enable when unlocked
|
|
||||||
unmountVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
|
|
||||||
revealVaultMenuItem.disableProperty().bind(vaultState.isEqualTo(Vault.State.MOUNTED).not()); // enable when mounted
|
|
||||||
|
|
||||||
EasyBind.subscribe(vault, this::vaultChanged);
|
EasyBind.subscribe(vault, this::vaultChanged);
|
||||||
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
|
EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected);
|
||||||
}
|
}
|
||||||
@@ -121,10 +97,6 @@ public class UnlockedController implements ViewController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newVault.getState() == Vault.State.UNLOCKED && newVault.getVaultSettings().mountAfterUnlock().get()) {
|
|
||||||
mountVault(newVault);
|
|
||||||
}
|
|
||||||
|
|
||||||
// (re)start throughput statistics:
|
// (re)start throughput statistics:
|
||||||
stopIoSampling();
|
stopIoSampling();
|
||||||
startIoSampling();
|
startIoSampling();
|
||||||
@@ -132,75 +104,22 @@ public class UnlockedController implements ViewController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickLockVault(ActionEvent event) {
|
private void didClickLockVault(ActionEvent event) {
|
||||||
regularUnmountVault(this::lockVault);
|
regularLockVault(this::lockVaultSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lockVault() {
|
private void lockVaultSucceeded() {
|
||||||
try {
|
|
||||||
vault.get().lock();
|
|
||||||
} catch (ServerLifecycleException | IOException e) {
|
|
||||||
LOG.error("Lock failed", e);
|
|
||||||
}
|
|
||||||
listener.ifPresent(listener -> listener.didLock(this));
|
listener.ifPresent(listener -> listener.didLock(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
private void regularLockVault(Runnable onSuccess) {
|
||||||
private void didClickMoreOptions(ActionEvent event) {
|
Tasks.create(() -> {
|
||||||
if (moreOptionsMenu.isShowing()) {
|
vault.get().lock(false);
|
||||||
moreOptionsMenu.hide();
|
|
||||||
} else {
|
|
||||||
moreOptionsMenu.setAnchorLocation(AnchorLocation.CONTENT_TOP_RIGHT);
|
|
||||||
moreOptionsMenu.show(moreOptionsButton, Side.BOTTOM, moreOptionsButton.getWidth(), 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void didClickMountVault(ActionEvent event) {
|
|
||||||
mountVault(vault.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mountVault(Vault vault) {
|
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
|
||||||
vault.mount();
|
|
||||||
}).onSuccess(() -> {
|
|
||||||
LOG.trace("Mount succeeded.");
|
|
||||||
messageLabel.setText(null);
|
|
||||||
if (vault.getVaultSettings().revealAfterMount().get()) {
|
|
||||||
revealVault(vault);
|
|
||||||
}
|
|
||||||
}).onError(CommandFailedException.class, e -> {
|
|
||||||
LOG.error("Mount failed.", e);
|
|
||||||
// TODO Markus Kreusch #393: hyperlink auf FAQ oder sowas?
|
|
||||||
messageLabel.setText(localization.getString("unlocked.label.mountFailed"));
|
|
||||||
}).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void didClickUnmountVault(ActionEvent event) {
|
|
||||||
regularUnmountVault(Runnables.doNothing());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void regularUnmountVault(Runnable onSuccess) {
|
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
|
||||||
vault.get().unmount();
|
|
||||||
}).onSuccess(() -> {
|
}).onSuccess(() -> {
|
||||||
LOG.trace("Regular unmount succeeded.");
|
LOG.trace("Regular unmount succeeded.");
|
||||||
onSuccess.run();
|
onSuccess.run();
|
||||||
}).onError(Exception.class, e -> {
|
}).onError(Exception.class, e -> {
|
||||||
onRegularUnmountVaultFailed(e, onSuccess);
|
onRegularUnmountVaultFailed(e, onSuccess);
|
||||||
}).run();
|
}).runOnce(executor);
|
||||||
}
|
|
||||||
|
|
||||||
private void forcedUnmountVault(Runnable onSuccess) {
|
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
|
||||||
vault.get().unmountForced();
|
|
||||||
}).onSuccess(() -> {
|
|
||||||
LOG.trace("Forced unmount succeeded.");
|
|
||||||
onSuccess.run();
|
|
||||||
}).onError(Exception.class, e -> {
|
|
||||||
LOG.error("Forced unmount failed.", e);
|
|
||||||
messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
|
|
||||||
}).run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRegularUnmountVaultFailed(Exception e, Runnable onSuccess) {
|
private void onRegularUnmountVaultFailed(Exception e, Runnable onSuccess) {
|
||||||
@@ -214,7 +133,7 @@ public class UnlockedController implements ViewController {
|
|||||||
|
|
||||||
Optional<ButtonType> choice = confirmDialog.showAndWait();
|
Optional<ButtonType> choice = confirmDialog.showAndWait();
|
||||||
if (ButtonType.YES.equals(choice.get())) {
|
if (ButtonType.YES.equals(choice.get())) {
|
||||||
forcedUnmountVault(onSuccess);
|
forcedLockVault(onSuccess);
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Unmount cancelled.", e);
|
LOG.trace("Unmount cancelled.", e);
|
||||||
}
|
}
|
||||||
@@ -224,29 +143,43 @@ public class UnlockedController implements ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void forcedLockVault(Runnable onSuccess) {
|
||||||
|
Tasks.create(() -> {
|
||||||
|
vault.get().lock(true);
|
||||||
|
}).onSuccess(() -> {
|
||||||
|
LOG.trace("Forced unmount succeeded.");
|
||||||
|
onSuccess.run();
|
||||||
|
}).onError(Exception.class, e -> {
|
||||||
|
LOG.error("Forced unmount failed.", e);
|
||||||
|
messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
|
||||||
|
}).runOnce(executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void didClickMoreOptions(ActionEvent event) {
|
||||||
|
if (moreOptionsMenu.isShowing()) {
|
||||||
|
moreOptionsMenu.hide();
|
||||||
|
} else {
|
||||||
|
moreOptionsMenu.setAnchorLocation(AnchorLocation.CONTENT_TOP_RIGHT);
|
||||||
|
moreOptionsMenu.show(moreOptionsButton, Side.BOTTOM, moreOptionsButton.getWidth(), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void didClickRevealVault(ActionEvent event) {
|
private void didClickRevealVault(ActionEvent event) {
|
||||||
revealVault(vault.get());
|
revealVault(vault.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revealVault(Vault vault) {
|
void revealVault(Vault vault) {
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
Tasks.create(() -> {
|
||||||
vault.reveal();
|
vault.reveal();
|
||||||
}).onSuccess(() -> {
|
}).onSuccess(() -> {
|
||||||
LOG.trace("Reveal succeeded.");
|
LOG.trace("Reveal succeeded.");
|
||||||
messageLabel.setText(null);
|
messageLabel.setText(null);
|
||||||
}).onError(CommandFailedException.class, e -> {
|
}).onError(Exception.class, e -> {
|
||||||
LOG.error("Reveal failed.", e);
|
LOG.error("Reveal failed.", e);
|
||||||
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
|
||||||
}).run();
|
}).runOnce(executor);
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void didClickCopyUrl(ActionEvent event) {
|
|
||||||
ClipboardContent clipboardContent = new ClipboardContent();
|
|
||||||
clipboardContent.putUrl(vault.get().getFilesystemRootUrl());
|
|
||||||
clipboardContent.putString(vault.get().getFilesystemRootUrl());
|
|
||||||
Clipboard.getSystemClipboard().setContent(clipboardContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****************************************
|
// ****************************************
|
||||||
|
|||||||
@@ -5,19 +5,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.ui.controllers;
|
package org.cryptomator.ui.controllers;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.cryptomator.ui.controls.SecPasswordField;
|
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
|
||||||
import org.cryptomator.ui.model.UpgradeStrategies;
|
|
||||||
import org.cryptomator.ui.model.UpgradeStrategy;
|
|
||||||
import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException;
|
|
||||||
import org.cryptomator.ui.model.Vault;
|
|
||||||
import org.cryptomator.ui.util.AsyncTaskService;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
|
|
||||||
import javafx.beans.binding.BooleanExpression;
|
import javafx.beans.binding.BooleanExpression;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
@@ -30,19 +21,26 @@ import javafx.scene.control.CheckBox;
|
|||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ProgressIndicator;
|
import javafx.scene.control.ProgressIndicator;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import org.cryptomator.ui.controls.SecPasswordField;
|
||||||
|
import org.cryptomator.ui.model.UpgradeStrategies;
|
||||||
|
import org.cryptomator.ui.model.UpgradeStrategy;
|
||||||
|
import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException;
|
||||||
|
import org.cryptomator.ui.model.Vault;
|
||||||
|
import org.cryptomator.ui.util.Tasks;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
|
||||||
public class UpgradeController implements ViewController {
|
public class UpgradeController implements ViewController {
|
||||||
|
|
||||||
private final ObjectProperty<UpgradeStrategy> strategy = new SimpleObjectProperty<>();
|
private final ObjectProperty<UpgradeStrategy> strategy = new SimpleObjectProperty<>();
|
||||||
private final UpgradeStrategies strategies;
|
private final UpgradeStrategies strategies;
|
||||||
private final AsyncTaskService asyncTaskService;
|
private final ExecutorService executor;
|
||||||
private Optional<UpgradeListener> listener = Optional.empty();
|
private Optional<UpgradeListener> listener = Optional.empty();
|
||||||
private Vault vault;
|
private Vault vault;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UpgradeController(Localization localization, UpgradeStrategies strategies, AsyncTaskService asyncTaskService) {
|
public UpgradeController(UpgradeStrategies strategies, ExecutorService executor) {
|
||||||
this.strategies = strategies;
|
this.strategies = strategies;
|
||||||
this.asyncTaskService = asyncTaskService;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -122,8 +120,8 @@ public class UpgradeController implements ViewController {
|
|||||||
private void upgrade(UpgradeStrategy instruction) {
|
private void upgrade(UpgradeStrategy instruction) {
|
||||||
passwordField.setDisable(true);
|
passwordField.setDisable(true);
|
||||||
progressIndicator.setVisible(true);
|
progressIndicator.setVisible(true);
|
||||||
asyncTaskService //
|
Tasks //
|
||||||
.asyncTaskOf(() -> {
|
.create(() -> {
|
||||||
if (!instruction.isApplicable(vault)) {
|
if (!instruction.isApplicable(vault)) {
|
||||||
throw new IllegalStateException("No ugprade needed for " + vault.getPath());
|
throw new IllegalStateException("No ugprade needed for " + vault.getPath());
|
||||||
}
|
}
|
||||||
@@ -137,7 +135,7 @@ public class UpgradeController implements ViewController {
|
|||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
passwordField.setDisable(false);
|
passwordField.setDisable(false);
|
||||||
passwordField.swipe();
|
passwordField.swipe();
|
||||||
}).run();
|
}).runOnce(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNextUpgrade() {
|
private void showNextUpgrade() {
|
||||||
|
|||||||
@@ -2,24 +2,27 @@
|
|||||||
* 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 javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.inject.Inject;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
@@ -30,20 +33,20 @@ import javafx.event.ActionEvent;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.Hyperlink;
|
import javafx.scene.control.Hyperlink;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ProgressIndicator;
|
import javafx.scene.control.ProgressIndicator;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import jdk.incubator.http.HttpClient;
|
|
||||||
import jdk.incubator.http.HttpRequest;
|
|
||||||
import jdk.incubator.http.HttpResponse;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.ui.l10n.Localization;
|
import org.cryptomator.ui.l10n.Localization;
|
||||||
import org.cryptomator.ui.util.AsyncTaskService;
|
import org.cryptomator.ui.util.Tasks;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.cryptomator.ui.util.DialogBuilderUtil.buildYesNoDialog;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class WelcomeController implements ViewController {
|
public class WelcomeController implements ViewController {
|
||||||
|
|
||||||
@@ -54,17 +57,17 @@ public class WelcomeController implements ViewController {
|
|||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final Comparator<String> semVerComparator;
|
private final Comparator<String> semVerComparator;
|
||||||
private final AsyncTaskService asyncTaskService;
|
private final ScheduledExecutorService executor;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WelcomeController(Application app, @Named("applicationVersion") Optional<String> applicationVersion, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator,
|
public WelcomeController(Application app, @Named("applicationVersion") Optional<String> applicationVersion, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator,
|
||||||
AsyncTaskService asyncTaskService) {
|
ScheduledExecutorService executor) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.applicationVersion = applicationVersion;
|
this.applicationVersion = applicationVersion;
|
||||||
this.localization = localization;
|
this.localization = localization;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.semVerComparator = semVerComparator;
|
this.semVerComparator = semVerComparator;
|
||||||
this.asyncTaskService = asyncTaskService;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -86,6 +89,8 @@ public class WelcomeController implements ViewController {
|
|||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
if (areUpdatesManagedExternally()) {
|
if (areUpdatesManagedExternally()) {
|
||||||
checkForUpdatesContainer.setVisible(false);
|
checkForUpdatesContainer.setVisible(false);
|
||||||
|
} else if (!settings.askedForUpdateCheck().get()) {
|
||||||
|
this.askForUpdateCheck();
|
||||||
} else if (settings.checkForUpdates().get()) {
|
} else if (settings.checkForUpdates().get()) {
|
||||||
this.checkForUpdates();
|
this.checkForUpdates();
|
||||||
}
|
}
|
||||||
@@ -104,34 +109,63 @@ public class WelcomeController implements ViewController {
|
|||||||
return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false"));
|
return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void askForUpdateCheck() {
|
||||||
|
Tasks.create(() -> {}).onSuccess(() -> {
|
||||||
|
Optional<ButtonType> result = buildYesNoDialog(
|
||||||
|
localization.getString("welcome.askForUpdateCheck.dialog.title"),
|
||||||
|
localization.getString("welcome.askForUpdateCheck.dialog.header"),
|
||||||
|
localization.getString("welcome.askForUpdateCheck.dialog.content"),
|
||||||
|
ButtonType.YES).showAndWait();
|
||||||
|
if (result.isPresent()) {
|
||||||
|
settings.askedForUpdateCheck().set(true);
|
||||||
|
settings.checkForUpdates().set(result.get().equals(ButtonType.YES));
|
||||||
|
}
|
||||||
|
if (settings.checkForUpdates().get()) {
|
||||||
|
this.checkForUpdates();
|
||||||
|
}
|
||||||
|
}).scheduleOnce(executor, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkForUpdates() {
|
private void checkForUpdates() {
|
||||||
checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking"));
|
checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking"));
|
||||||
checkForUpdatesIndicator.setVisible(true);
|
checkForUpdatesIndicator.setVisible(true);
|
||||||
asyncTaskService.asyncTaskOf(() -> {
|
Tasks.create(() -> {
|
||||||
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
URL url = URI.create("https://api.cryptomator.org/updates/latestVersion.json").toURL();
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
.GET()
|
conn.addRequestProperty("User-Agent", userAgent);
|
||||||
.uri(new URI("https://api.cryptomator.org/updates/latestVersion.json"))
|
conn.connect();
|
||||||
.header("User-Agent", userAgent)
|
try {
|
||||||
.timeout(Duration.ofSeconds(5))
|
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
.build();
|
return Optional.<byte[]>empty();
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));
|
}
|
||||||
if (response.statusCode() == 200) {
|
try (InputStream in = conn.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
|
in.transferTo(out);
|
||||||
|
return Optional.of(out.toByteArray());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}).onSuccess(response -> {
|
||||||
|
response.ifPresent(bytes -> {
|
||||||
Gson gson = new GsonBuilder().setLenient().create();
|
Gson gson = new GsonBuilder().setLenient().create();
|
||||||
Map<String, String> map = gson.fromJson(response.body(), new TypeToken<Map<String, String>>() {
|
String json = new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
Map<String, String> map = gson.fromJson(json, new TypeToken<Map<String, String>>() {
|
||||||
}.getType());
|
}.getType());
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
this.compareVersions(map);
|
this.compareVersions(map);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}).onError(Exception.class, e -> {
|
||||||
|
LOG.warn("Error checking for updates", e);
|
||||||
}).andFinally(() -> {
|
}).andFinally(() -> {
|
||||||
checkForUpdatesStatus.setText("");
|
checkForUpdatesStatus.setText("");
|
||||||
checkForUpdatesIndicator.setVisible(false);
|
checkForUpdatesIndicator.setVisible(false);
|
||||||
}).run();
|
}).runOnce(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compareVersions(final Map<String, String> latestVersions) {
|
private void compareVersions(final Map<String, String> latestVersions) {
|
||||||
|
assert Platform.isFxApplicationThread();
|
||||||
final String latestVersion;
|
final String latestVersion;
|
||||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||||
latestVersion = latestVersions.get("mac");
|
latestVersion = latestVersions.get("mac");
|
||||||
@@ -147,11 +181,9 @@ public class WelcomeController implements ViewController {
|
|||||||
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
|
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
|
||||||
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
||||||
final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
|
final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
|
||||||
Platform.runLater(() -> {
|
this.updateLink.setText(msg);
|
||||||
this.updateLink.setText(msg);
|
this.updateLink.setVisible(true);
|
||||||
this.updateLink.setVisible(true);
|
this.updateLink.setDisable(false);
|
||||||
this.updateLink.setDisable(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
|
|||||||
}
|
}
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UNLOCKED:
|
case UNLOCKED:
|
||||||
case MOUNTED:
|
case PROCESSING:
|
||||||
case MOUNTING:
|
|
||||||
case UNMOUNTING:
|
|
||||||
return "\uf09c";
|
return "\uf09c";
|
||||||
case LOCKED:
|
case LOCKED:
|
||||||
default:
|
default:
|
||||||
@@ -84,9 +82,7 @@ public class DirectoryListCell extends DraggableListCell<Vault> {
|
|||||||
}
|
}
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UNLOCKED:
|
case UNLOCKED:
|
||||||
case MOUNTED:
|
case PROCESSING:
|
||||||
case MOUNTING:
|
|
||||||
case UNMOUNTING:
|
|
||||||
return UNLOCKED_ICON_COLOR;
|
return UNLOCKED_ICON_COLOR;
|
||||||
case LOCKED:
|
case LOCKED:
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -73,33 +73,21 @@ public class AutoUnlocker {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
vault.unlock(CharBuffer.wrap(storedPw));
|
vault.unlock(CharBuffer.wrap(storedPw));
|
||||||
mountSilently(vault);
|
revealSilently(vault);
|
||||||
} catch (IOException | CryptoException e) {
|
} catch (IOException | CryptoException | Volume.VolumeException e) {
|
||||||
LOG.error("Auto unlock failed.", e);
|
LOG.error("Auto unlock failed.", e);
|
||||||
} finally {
|
} finally {
|
||||||
Arrays.fill(storedPw, ' ');
|
Arrays.fill(storedPw, ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mountSilently(Vault unlockedVault) {
|
|
||||||
if (!unlockedVault.getVaultSettings().mountAfterUnlock().get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
unlockedVault.mount();
|
|
||||||
revealSilently(unlockedVault);
|
|
||||||
} catch (CommandFailedException e) {
|
|
||||||
LOG.error("Auto unlock succeded, but mounting the drive failed.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void revealSilently(Vault mountedVault) {
|
private void revealSilently(Vault mountedVault) {
|
||||||
if (!mountedVault.getVaultSettings().revealAfterMount().get()) {
|
if (!mountedVault.getVaultSettings().revealAfterMount().get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mountedVault.reveal();
|
mountedVault.reveal();
|
||||||
} catch (CommandFailedException e) {
|
} catch (Volume.VolumeException e) {
|
||||||
LOG.error("Auto unlock succeded, but revealing the drive failed.", e);
|
LOG.error("Auto unlock succeded, but revealing the drive failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.cryptomator.ui.model;
|
|
||||||
|
|
||||||
public class CommandFailedException extends Exception {
|
|
||||||
|
|
||||||
public CommandFailedException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandFailedException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandFailedException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.cryptomator.ui.model;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
|
import org.cryptomator.frontend.dokany.Mount;
|
||||||
|
import org.cryptomator.frontend.dokany.MountFactory;
|
||||||
|
|
||||||
|
public class DokanyVolume implements Volume {
|
||||||
|
|
||||||
|
private static final String FS_TYPE_NAME = "Cryptomator File System";
|
||||||
|
|
||||||
|
private final VaultSettings vaultSettings;
|
||||||
|
private final MountFactory mountFactory;
|
||||||
|
private final WindowsDriveLetters windowsDriveLetters;
|
||||||
|
private Mount mount;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, WindowsDriveLetters windowsDriveLetters) {
|
||||||
|
this.vaultSettings = vaultSettings;
|
||||||
|
this.mountFactory = new MountFactory(executorService);
|
||||||
|
this.windowsDriveLetters = windowsDriveLetters;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return MountFactory.isApplicable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Drive letter 'A' as mount point is invalid in dokany. maybe we should do already here something against it
|
||||||
|
@Override
|
||||||
|
public void mount(CryptoFileSystem fs) throws VolumeException {
|
||||||
|
char driveLetter;
|
||||||
|
if (!vaultSettings.winDriveLetter().getValueSafe().equals("")) {
|
||||||
|
driveLetter = vaultSettings.winDriveLetter().get().charAt(0);
|
||||||
|
} else {
|
||||||
|
//auto assign drive letter
|
||||||
|
//TODO: can we assume the we have at least one free drive letter?
|
||||||
|
|
||||||
|
//this is a temporary fix for 'A' being an invalid drive letter
|
||||||
|
if(!windowsDriveLetters.getAvailableDriveLetters().isEmpty()){
|
||||||
|
Iterator<Character> winDriveLetterIt = windowsDriveLetters.getAvailableDriveLetters().iterator();
|
||||||
|
do{
|
||||||
|
driveLetter = winDriveLetterIt.next();
|
||||||
|
}while (winDriveLetterIt.hasNext() && driveLetter == 65);
|
||||||
|
// if (!windowsDriveLetters.getAvailableDriveLetters().isEmpty()) {
|
||||||
|
// driveLetter = windowsDriveLetters.getAvailableDriveLetters().iterator().next();
|
||||||
|
} else {
|
||||||
|
throw new VolumeException("No free drive letter available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String mountName = vaultSettings.mountName().get();
|
||||||
|
this.mount = mountFactory.mount(fs.getPath("/"), driveLetter, mountName, FS_TYPE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reveal() throws VolumeException {
|
||||||
|
boolean success = mount.reveal();
|
||||||
|
if (!success) {
|
||||||
|
throw new VolumeException("Reveal failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unmount() {
|
||||||
|
mount.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.model;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.DirectoryNotEmptyException;
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -11,6 +12,7 @@ import javax.inject.Inject;
|
|||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
|
import org.cryptomator.frontend.fuse.mount.CommandFailedException;
|
||||||
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
|
||||||
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
|
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
|
||||||
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
|
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
|
||||||
@@ -18,7 +20,6 @@ import org.cryptomator.frontend.fuse.mount.Mount;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@VaultModule.PerVault
|
|
||||||
public class FuseVolume implements Volume {
|
public class FuseVolume implements Volume {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class);
|
||||||
@@ -30,34 +31,21 @@ public class FuseVolume implements Volume {
|
|||||||
private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator";
|
private static final String DEFAULT_MOUNTROOTPATH_LINUX = System.getProperty("user.home") + "/.Cryptomator";
|
||||||
|
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
private final WindowsDriveLetters windowsDriveLetters;
|
|
||||||
|
|
||||||
private Mount fuseMnt;
|
private Mount fuseMnt;
|
||||||
private CryptoFileSystem cfs;
|
|
||||||
private Path mountPath;
|
private Path mountPath;
|
||||||
private boolean extraDirCreated;
|
private boolean extraDirCreated;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FuseVolume(VaultSettings vaultSettings, WindowsDriveLetters windowsDriveLetters) {
|
public FuseVolume(VaultSettings vaultSettings) {
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.windowsDriveLetters = windowsDriveLetters;
|
|
||||||
this.extraDirCreated = false;
|
this.extraDirCreated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepare(CryptoFileSystem fs) throws IOException, FuseNotSupportedException {
|
public void mount(CryptoFileSystem fs) throws IOException, FuseNotSupportedException, VolumeException {
|
||||||
this.cfs = fs;
|
|
||||||
String mountPath;
|
String mountPath;
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (vaultSettings.usesIndividualMountPath().get()) {
|
||||||
//windows case
|
|
||||||
if (vaultSettings.winDriveLetter().get() != null) {
|
|
||||||
// specific drive letter selected
|
|
||||||
mountPath = vaultSettings.winDriveLetter().get() + ":\\";
|
|
||||||
} else {
|
|
||||||
// auto assign drive letter
|
|
||||||
mountPath = windowsDriveLetters.getAvailableDriveLetters().iterator().next() + ":\\";
|
|
||||||
}
|
|
||||||
} else if (vaultSettings.usesIndividualMountPath().get()) {
|
|
||||||
//specific path given
|
//specific path given
|
||||||
mountPath = vaultSettings.individualMountPath().get();
|
mountPath = vaultSettings.individualMountPath().get();
|
||||||
} else {
|
} else {
|
||||||
@@ -66,57 +54,60 @@ public class FuseVolume implements Volume {
|
|||||||
extraDirCreated = true;
|
extraDirCreated = true;
|
||||||
}
|
}
|
||||||
this.mountPath = Paths.get(mountPath).toAbsolutePath();
|
this.mountPath = Paths.get(mountPath).toAbsolutePath();
|
||||||
|
mount(fs.getPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createDirIfNotExist(String prefix, String dirName) throws IOException {
|
private String createDirIfNotExist(String prefix, String dirName) throws IOException {
|
||||||
Path p = Paths.get(prefix, dirName + vaultSettings.getId());
|
Path p = Paths.get(prefix, dirName + vaultSettings.getId());
|
||||||
if (Files.isDirectory(p) && !Files.newDirectoryStream(p).iterator().hasNext()) {
|
if (Files.isDirectory(p)) {
|
||||||
throw new DirectoryNotEmptyException("Mount point is not empty.");
|
try(DirectoryStream<Path> emptyCheck = Files.newDirectoryStream(p)){
|
||||||
|
if(emptyCheck.iterator().hasNext()){
|
||||||
|
throw new DirectoryNotEmptyException("Mount point is not empty.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG.info("Directory already exists and is empty. Using it as mount point.");
|
||||||
|
return p.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Files.createDirectory(p);
|
Files.createDirectory(p);
|
||||||
return p.toString();
|
return p.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void mount(Path root) throws VolumeException {
|
||||||
public void mount() throws CommandFailedException {
|
|
||||||
try {
|
try {
|
||||||
EnvironmentVariables envVars = EnvironmentVariables.create()
|
EnvironmentVariables envVars = EnvironmentVariables.create()
|
||||||
.withMountName(vaultSettings.mountName().getValue())
|
.withMountName(vaultSettings.mountName().getValue())
|
||||||
.withMountPath(mountPath)
|
.withMountPath(mountPath)
|
||||||
.build();
|
.build();
|
||||||
this.fuseMnt = FuseMountFactory.getMounter().mount(cfs.getPath("/"), envVars);
|
this.fuseMnt = FuseMountFactory.getMounter().mount(root, envVars);
|
||||||
} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
throw new CommandFailedException("Unable to mount Filesystem", e);
|
throw new VolumeException("Unable to mount Filesystem", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reveal() throws CommandFailedException {
|
public void reveal() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
fuseMnt.revealInFileManager();
|
fuseMnt.revealInFileManager();
|
||||||
} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
LOG.info("Revealing the vault in file manger failed: " + e.getMessage());
|
LOG.info("Revealing the vault in file manger failed: " + e.getMessage());
|
||||||
throw new CommandFailedException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmount() throws CommandFailedException {
|
public synchronized void unmount() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
fuseMnt.close();
|
fuseMnt.close();
|
||||||
} catch (org.cryptomator.frontend.fuse.mount.CommandFailedException e) {
|
} catch (CommandFailedException e) {
|
||||||
throw new CommandFailedException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void cleanup() {
|
||||||
public synchronized void unmountForced() throws CommandFailedException {
|
|
||||||
unmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
if (extraDirCreated) {
|
if (extraDirCreated) {
|
||||||
try {
|
try {
|
||||||
Files.delete(mountPath);
|
Files.delete(mountPath);
|
||||||
@@ -126,19 +117,9 @@ public class FuseVolume implements Volume {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMountUri() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return FuseMountFactory.isFuseSupported();
|
return (SystemUtils.IS_OS_MAC_OSX || SystemUtils.IS_OS_LINUX) && FuseMountFactory.isFuseSupported();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsForcedUnmount() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
import org.cryptomator.common.LazyInitializer;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
|
||||||
import org.cryptomator.cryptolib.api.CryptoException;
|
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
|
||||||
import org.cryptomator.ui.model.VaultModule.PerVault;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
@@ -39,6 +30,15 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.LazyInitializer;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||||
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
|
import org.cryptomator.cryptolib.api.CryptoException;
|
||||||
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.ui.model.VaultModule.PerVault;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -53,20 +53,21 @@ public class Vault {
|
|||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
|
private final Provider<Volume> volumeProvider;
|
||||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
||||||
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
|
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
|
||||||
|
|
||||||
private Volume volume;
|
private Volume volume;
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
LOCKED, UNLOCKED, MOUNTING, MOUNTED, UNMOUNTING
|
LOCKED, PROCESSING, UNLOCKED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vault(Settings settings, VaultSettings vaultSettings, Volume volume) {
|
Vault(Settings settings, VaultSettings vaultSettings, Provider<Volume> volumeProvider) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.volume = volume;
|
this.volumeProvider = volumeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
@@ -98,51 +99,37 @@ public class Vault {
|
|||||||
CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
|
CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException {
|
public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, Volume.VolumeException {
|
||||||
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
|
Platform.runLater(() -> state.set(State.PROCESSING));
|
||||||
volume.prepare(fs);
|
try {
|
||||||
Platform.runLater(() -> {
|
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
|
||||||
state.set(State.UNLOCKED);
|
volume = volumeProvider.get();
|
||||||
});
|
volume.mount(fs);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
state.set(State.UNLOCKED);
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> state.set(State.LOCKED));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void mount() throws CommandFailedException {
|
public synchronized void lock(boolean forced) throws Volume.VolumeException {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
state.set(State.MOUNTING);
|
state.set(State.PROCESSING);
|
||||||
});
|
|
||||||
volume.mount();
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
state.set(State.MOUNTED);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void unmountForced() throws CommandFailedException {
|
|
||||||
unmount(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void unmount() throws CommandFailedException {
|
|
||||||
unmount(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void unmount(boolean forced) throws CommandFailedException {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
state.set(State.UNMOUNTING);
|
|
||||||
});
|
});
|
||||||
if (forced && volume.supportsForcedUnmount()) {
|
if (forced && volume.supportsForcedUnmount()) {
|
||||||
volume.unmountForced();
|
volume.unmountForced();
|
||||||
} else {
|
} else {
|
||||||
volume.unmount();
|
volume.unmount();
|
||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
|
||||||
state.set(State.UNLOCKED);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void lock() throws IOException {
|
|
||||||
volume.stop();
|
|
||||||
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
|
CryptoFileSystem fs = cryptoFileSystem.getAndSet(null);
|
||||||
if (fs != null) {
|
if (fs != null) {
|
||||||
fs.close();
|
try {
|
||||||
|
fs.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Error closing file system.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
state.set(State.LOCKED);
|
state.set(State.LOCKED);
|
||||||
@@ -154,26 +141,21 @@ public class Vault {
|
|||||||
*/
|
*/
|
||||||
public void prepareForShutdown() {
|
public void prepareForShutdown() {
|
||||||
try {
|
try {
|
||||||
unmount();
|
lock(false);
|
||||||
} catch (CommandFailedException e) {
|
} catch (Volume.VolumeException e) {
|
||||||
if (volume.supportsForcedUnmount()) {
|
if (volume.supportsForcedUnmount()) {
|
||||||
try {
|
try {
|
||||||
unmountForced();
|
lock(true);
|
||||||
} catch (CommandFailedException e1) {
|
} catch (Volume.VolumeException e1) {
|
||||||
LOG.warn("Failed to force unmount vault.");
|
LOG.warn("Failed to force lock vault.", e1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("Failed to gracefully unmount vault.");
|
LOG.warn("Failed to gracefully lock vault.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
lock();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.warn("Failed to lock vault.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reveal() throws CommandFailedException {
|
public void reveal() throws Volume.VolumeException {
|
||||||
volume.reveal();
|
volume.reveal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,10 +271,6 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilesystemRootUrl() {
|
|
||||||
return volume.getMountUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return vaultSettings.getId();
|
return vaultSettings.getId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ import org.cryptomator.common.settings.VaultSettings;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class VaultModule {
|
public class VaultModule {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(VaultModule.class);
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
|
|
||||||
public VaultModule(VaultSettings vaultSettings) {
|
public VaultModule(VaultSettings vaultSettings) {
|
||||||
@@ -42,21 +45,18 @@ public class VaultModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@PerVault
|
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
|
||||||
public Volume provideNioAdpater(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume) {
|
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
||||||
VolumeImpl impl = settings.volumeImpl().get();
|
if (VolumeImpl.DOKANY == preferredImpl && dokanyVolume.isSupported()) {
|
||||||
switch (impl) {
|
return dokanyVolume;
|
||||||
case FUSE:
|
} else if (VolumeImpl.FUSE == preferredImpl && fuseVolume.isSupported()) {
|
||||||
if (fuseVolume.isSupported()) {
|
return fuseVolume;
|
||||||
return fuseVolume;
|
} else {
|
||||||
} else {
|
if (VolumeImpl.WEBDAV != preferredImpl) {
|
||||||
settings.volumeImpl().set(VolumeImpl.WEBDAV);
|
LOG.warn("Using WebDAV, because {} is not supported.", preferredImpl.getDisplayName());
|
||||||
// fallthrough to WEBDAV
|
}
|
||||||
}
|
assert webDavVolume.isSupported();
|
||||||
case WEBDAV:
|
return webDavVolume;
|
||||||
return webDavVolume;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unsupported NioAdapter: " + impl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,30 +9,50 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public interface Volume {
|
public interface Volume {
|
||||||
|
|
||||||
void prepare(CryptoFileSystem fs) throws IOException;
|
/**
|
||||||
|
* Checks in constant time whether this volume type is supported on the system running Cryptomator.
|
||||||
|
* @return true if this volume can be mounted
|
||||||
|
*/
|
||||||
|
boolean isSupported();
|
||||||
|
|
||||||
void mount() throws CommandFailedException;
|
/**
|
||||||
|
*
|
||||||
|
* @param fs
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void mount(CryptoFileSystem fs) throws IOException, VolumeException;
|
||||||
|
|
||||||
default void reveal() throws CommandFailedException {
|
void reveal() throws VolumeException;
|
||||||
throw new CommandFailedException("Not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void unmount() throws CommandFailedException;
|
void unmount() throws VolumeException;
|
||||||
|
|
||||||
default void unmountForced() throws CommandFailedException {
|
// optional forced unmounting:
|
||||||
throw new CommandFailedException("Operation not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
String getMountUri();
|
|
||||||
|
|
||||||
default boolean isSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean supportsForcedUnmount() {
|
default boolean supportsForcedUnmount() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void unmountForced() throws VolumeException {
|
||||||
|
throw new VolumeException("Operation not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when a volume-specific command such as mount/unmount/reveal failed.
|
||||||
|
*/
|
||||||
|
class VolumeException extends Exception {
|
||||||
|
|
||||||
|
public VolumeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VolumeException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VolumeException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import javax.inject.Provider;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
@VaultModule.PerVault
|
|
||||||
public class WebDavVolume implements Volume {
|
public class WebDavVolume implements Volume {
|
||||||
|
|
||||||
private static final String LOCALHOST_ALIAS = "cryptomator-vault";
|
private static final String LOCALHOST_ALIAS = "cryptomator-vault";
|
||||||
@@ -36,7 +35,7 @@ public class WebDavVolume implements Volume {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepare(CryptoFileSystem fs) {
|
public void mount(CryptoFileSystem fs) throws VolumeException {
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
server = serverProvider.get();
|
server = serverProvider.get();
|
||||||
}
|
}
|
||||||
@@ -45,10 +44,10 @@ public class WebDavVolume implements Volume {
|
|||||||
}
|
}
|
||||||
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
|
servlet = server.createWebDavServlet(fs.getPath("/"), vaultSettings.getId() + "/" + vaultSettings.mountName().get());
|
||||||
servlet.start();
|
servlet.start();
|
||||||
|
mount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void mount() throws VolumeException {
|
||||||
public void mount() throws CommandFailedException {
|
|
||||||
if (servlet == null) {
|
if (servlet == null) {
|
||||||
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
|
throw new IllegalStateException("Mounting requires unlocked WebDAV servlet.");
|
||||||
}
|
}
|
||||||
@@ -61,32 +60,38 @@ public class WebDavVolume implements Volume {
|
|||||||
this.mount = servlet.mount(mountParams); // might block this thread for a while
|
this.mount = servlet.mount(mountParams); // might block this thread for a while
|
||||||
} catch (Mounter.CommandFailedException e) {
|
} catch (Mounter.CommandFailedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new CommandFailedException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reveal() throws CommandFailedException {
|
public void reveal() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
mount.reveal();
|
mount.reveal();
|
||||||
} catch (Mounter.CommandFailedException e) {
|
} catch (Mounter.CommandFailedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new CommandFailedException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmount() throws CommandFailedException {
|
public synchronized void unmount() throws VolumeException {
|
||||||
try {
|
try {
|
||||||
mount.unmount();
|
mount.unmount();
|
||||||
} catch (Mounter.CommandFailedException e) {
|
} catch (Mounter.CommandFailedException e) {
|
||||||
throw new CommandFailedException(e);
|
throw new VolumeException(e);
|
||||||
}
|
}
|
||||||
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmountForced() {
|
public synchronized void unmountForced() throws VolumeException {
|
||||||
mount.forced();
|
try {
|
||||||
|
mount.forced().orElseThrow(IllegalStateException::new).unmount();
|
||||||
|
} catch (Mounter.CommandFailedException e) {
|
||||||
|
throw new VolumeException(e);
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLocalhostAliasOrNull() {
|
private String getLocalhostAliasOrNull() {
|
||||||
@@ -102,28 +107,19 @@ public class WebDavVolume implements Volume {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void cleanup() {
|
||||||
public void stop() {
|
|
||||||
if (servlet != null) {
|
if (servlet != null) {
|
||||||
servlet.stop();
|
servlet.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String getMountUri() {
|
|
||||||
return servlet.getServletRootUri().toString() + "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: what to check wether it is implemented?
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean supportsForcedUnmount() {
|
public boolean supportsForcedUnmount() {
|
||||||
return mount != null && mount.forced().isPresent();
|
return mount != null && mount.forced().isPresent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,219 +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.ui.util;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.cryptomator.common.ConsumerThrowingException;
|
|
||||||
import org.cryptomator.common.RunnableThrowingException;
|
|
||||||
import org.cryptomator.common.SupplierThrowingException;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class AsyncTaskService {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AsyncTaskService.class);
|
|
||||||
|
|
||||||
private final ExecutorService executor;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AsyncTaskService(ExecutorService executor) {
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new async task
|
|
||||||
*
|
|
||||||
* @param task Tasks to be invoked in a background thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
public AsyncTaskWithoutSuccessHandler<Void> asyncTaskOf(RunnableThrowingException<?> task) {
|
|
||||||
return new AsyncTaskImpl<>(() -> {
|
|
||||||
task.run();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new async task
|
|
||||||
*
|
|
||||||
* @param task Tasks to be invoked in a background thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
public <ResultType> AsyncTaskWithoutSuccessHandler<ResultType> asyncTaskOf(SupplierThrowingException<ResultType, ?> task) {
|
|
||||||
return new AsyncTaskImpl<>(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AsyncTaskImpl<ResultType> implements AsyncTaskWithoutSuccessHandler<ResultType> {
|
|
||||||
|
|
||||||
private final SupplierThrowingException<ResultType, ?> task;
|
|
||||||
|
|
||||||
private ConsumerThrowingException<ResultType, ?> successHandler = value -> {
|
|
||||||
};
|
|
||||||
private final List<ErrorHandler<Throwable>> errorHandlers = new ArrayList<>();
|
|
||||||
private RunnableThrowingException<?> finallyHandler = () -> {
|
|
||||||
};
|
|
||||||
|
|
||||||
public AsyncTaskImpl(SupplierThrowingException<ResultType, ?> task) {
|
|
||||||
this.task = task;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler) {
|
|
||||||
successHandler = handler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler) {
|
|
||||||
return onSuccess(result -> handler.run());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
@Override
|
|
||||||
public <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler) {
|
|
||||||
errorHandlers.add((ErrorHandler) new ErrorHandler<>(type, handler));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler) {
|
|
||||||
return onError(type, error -> handler.run());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsyncTask andFinally(RunnableThrowingException<?> handler) {
|
|
||||||
finallyHandler = handler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
errorHandlers.add(ErrorHandler.LOGGING_HANDLER);
|
|
||||||
executor.execute(() -> logExceptions(() -> {
|
|
||||||
try {
|
|
||||||
ResultType result = task.get();
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
try {
|
|
||||||
successHandler.accept(result);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOG.error("Uncaught exception", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable e) {
|
|
||||||
ErrorHandler<Throwable> errorHandler = errorHandlerFor(e);
|
|
||||||
Platform.runLater(toRunnableLoggingException(() -> errorHandler.accept(e)));
|
|
||||||
} finally {
|
|
||||||
Platform.runLater(toRunnableLoggingException(finallyHandler));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ErrorHandler<Throwable> errorHandlerFor(Throwable e) {
|
|
||||||
return errorHandlers.stream().filter(handler -> handler.handles(e)).findFirst().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Runnable toRunnableLoggingException(RunnableThrowingException<?> delegate) {
|
|
||||||
return () -> logExceptions(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void logExceptions(RunnableThrowingException<?> delegate) {
|
|
||||||
try {
|
|
||||||
delegate.run();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOG.error("Uncaught exception", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ErrorHandler<ErrorType> implements ConsumerThrowingException<ErrorType, Throwable> {
|
|
||||||
|
|
||||||
public static final ErrorHandler<Throwable> LOGGING_HANDLER = new ErrorHandler<Throwable>(Throwable.class, error -> {
|
|
||||||
LOG.error("Uncaught exception", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
private final Class<ErrorType> type;
|
|
||||||
private final ConsumerThrowingException<ErrorType, ?> delegate;
|
|
||||||
|
|
||||||
public ErrorHandler(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> delegate) {
|
|
||||||
this.type = type;
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean handles(Throwable error) {
|
|
||||||
return type.isInstance(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(ErrorType error) throws Throwable {
|
|
||||||
delegate.accept(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AsyncTaskWithoutSuccessHandler<ResultType> extends AsyncTaskWithoutErrorHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
AsyncTaskWithoutErrorHandler onSuccess(ConsumerThrowingException<ResultType, ?> handler);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
AsyncTaskWithoutErrorHandler onSuccess(RunnableThrowingException<?> handler);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AsyncTaskWithoutErrorHandler extends AsyncTaskWithoutFinallyHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type Exception type to catch
|
|
||||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, ConsumerThrowingException<ErrorType, ?> handler);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type Exception type to catch
|
|
||||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
<ErrorType extends Throwable> AsyncTaskWithoutErrorHandler onError(Class<ErrorType> type, RunnableThrowingException<?> handler);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AsyncTaskWithoutFinallyHandler extends AsyncTask {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param handler Tasks to be invoked on the JavaFX application thread.
|
|
||||||
* @return The async task
|
|
||||||
*/
|
|
||||||
AsyncTask andFinally(RunnableThrowingException<?> handler);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface AsyncTask extends Runnable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the async task.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void run();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -28,6 +28,8 @@ import javafx.scene.paint.Color;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class PasswordStrengthUtil {
|
public class PasswordStrengthUtil {
|
||||||
|
|
||||||
|
private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length
|
||||||
|
|
||||||
private final Zxcvbn zxcvbn;
|
private final Zxcvbn zxcvbn;
|
||||||
private final List<String> sanitizedInputs;
|
private final List<String> sanitizedInputs;
|
||||||
private final Localization localization;
|
private final Localization localization;
|
||||||
@@ -43,10 +45,9 @@ public class PasswordStrengthUtil {
|
|||||||
public int computeRate(String password) {
|
public int computeRate(String password) {
|
||||||
if (Strings.isNullOrEmpty(password)) {
|
if (Strings.isNullOrEmpty(password)) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (password.length() > 100) {
|
|
||||||
return 4; // assume this is strong. zxcvbn memory and runtime depends vastly on the password length
|
|
||||||
} else {
|
} else {
|
||||||
return zxcvbn.measure(password, sanitizedInputs).getScore();
|
int numCharsToRate = Math.min(PW_TRUNC_LEN, password.length());
|
||||||
|
return zxcvbn.measure(password.substring(0, numCharsToRate), sanitizedInputs).getScore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2018 Skymatic UG (haftungsbeschränkt).
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the accompanying LICENSE file.
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.cryptomator.ui.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.concurrent.ScheduledService;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class Tasks {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Tasks.class);
|
||||||
|
public static <T> TaskBuilder<T> create(Callable<T> callable) {
|
||||||
|
return new TaskBuilder<>(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskBuilder<Void> create(VoidCallable callable) {
|
||||||
|
return create(() -> {
|
||||||
|
callable.call();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TaskBuilder<T> {
|
||||||
|
|
||||||
|
private final Callable<T> callable;
|
||||||
|
private Consumer<T> successHandler = x -> {
|
||||||
|
};
|
||||||
|
private List<ErrorHandler<?>> errorHandlers = new ArrayList<>();
|
||||||
|
private Runnable finallyHandler = () -> {};
|
||||||
|
|
||||||
|
TaskBuilder(Callable<T> callable) {
|
||||||
|
this.callable = callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskBuilder<T> onSuccess(Runnable successHandler) {
|
||||||
|
this.successHandler = x -> {
|
||||||
|
successHandler.run();
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskBuilder<T> onSuccess(Consumer<T> successHandler) {
|
||||||
|
this.successHandler = successHandler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Throwable> TaskBuilder<T> onError(Class<E> type, Consumer<E> exceptionHandler) {
|
||||||
|
errorHandlers.add(new ErrorHandler<>(type, exceptionHandler));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskBuilder<T> andFinally(Runnable finallyHandler) {
|
||||||
|
this.finallyHandler = finallyHandler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<T> build() {
|
||||||
|
return new TaskImpl<>(callable, successHandler, errorHandlers, finallyHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> runOnce(ExecutorService executorService) {
|
||||||
|
Task<T> task = build();
|
||||||
|
executorService.submit(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> scheduleOnce(ScheduledExecutorService executorService, long delay, TimeUnit unit) {
|
||||||
|
Task<T> task = build();
|
||||||
|
executorService.schedule(task, delay, unit);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduledService<T> schedulePeriodically(ExecutorService executorService, Duration initialDelay, Duration period) {
|
||||||
|
ScheduledService<T> service = new RestartingService<>(this::build);
|
||||||
|
service.setExecutor(executorService);
|
||||||
|
service.setDelay(initialDelay);
|
||||||
|
service.setPeriod(period);
|
||||||
|
Platform.runLater(service::start);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ErrorHandler<ErrorType extends Throwable> {
|
||||||
|
|
||||||
|
private final Class<ErrorType> type;
|
||||||
|
private final Consumer<ErrorType> errorHandler;
|
||||||
|
|
||||||
|
public ErrorHandler(Class<ErrorType> type, Consumer<ErrorType> errorHandler) {
|
||||||
|
this.type = type;
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handles(Throwable error) {
|
||||||
|
return type.isInstance(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(Throwable error) throws ClassCastException {
|
||||||
|
ErrorType typedError = type.cast(error);
|
||||||
|
errorHandler.accept(typedError);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter between java.util.function.* and javafx.concurrent.*
|
||||||
|
*/
|
||||||
|
private static class TaskImpl<T> extends Task<T> {
|
||||||
|
|
||||||
|
private final Callable<T> callable;
|
||||||
|
private final Consumer<T> successHandler;
|
||||||
|
private List<ErrorHandler<?>> errorHandlers;
|
||||||
|
private final Runnable finallyHandler;
|
||||||
|
|
||||||
|
TaskImpl(Callable<T> callable, Consumer<T> successHandler, List<ErrorHandler<?>> errorHandlers, Runnable finallyHandler) {
|
||||||
|
this.callable = callable;
|
||||||
|
this.successHandler = successHandler;
|
||||||
|
this.errorHandlers = errorHandlers;
|
||||||
|
this.finallyHandler = finallyHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T call() throws Exception {
|
||||||
|
return callable.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
try {
|
||||||
|
successHandler.accept(getValue());
|
||||||
|
} finally {
|
||||||
|
finallyHandler.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
try {
|
||||||
|
Throwable exception = getException();
|
||||||
|
errorHandlers.stream().filter(handler -> handler.handles(exception)).findFirst().ifPresentOrElse(exceptionHandler -> {
|
||||||
|
exceptionHandler.handle(exception);
|
||||||
|
}, () -> {
|
||||||
|
LOG.error("Unhandled exception", exception);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
finallyHandler.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that keeps executing the next task long as tasks are succeeding.
|
||||||
|
*/
|
||||||
|
private static class RestartingService<T> extends ScheduledService<T> {
|
||||||
|
|
||||||
|
private final Supplier<Task<T>> taskFactory;
|
||||||
|
|
||||||
|
RestartingService(Supplier<Task<T>> taskFactory) {
|
||||||
|
this.taskFactory = taskFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Task<T> createTask() {
|
||||||
|
return taskFactory.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface VoidCallable {
|
||||||
|
|
||||||
|
void call() throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -75,28 +75,25 @@
|
|||||||
|
|
||||||
<!-- Row 3.2 -->
|
<!-- Row 3.2 -->
|
||||||
<CheckBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="unlockAfterStartup" text="%unlock.label.unlockAfterStartup" cacheShape="true" cache="true" />
|
<CheckBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="unlockAfterStartup" text="%unlock.label.unlockAfterStartup" cacheShape="true" cache="true" />
|
||||||
|
|
||||||
<!-- Row 3.3 -->
|
<!-- Row 3.3 -->
|
||||||
<CheckBox GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="mountAfterUnlock" text="%unlock.label.mountAfterUnlock" cacheShape="true" cache="true" />
|
<Label GridPane.rowIndex="3" GridPane.columnIndex="0" text="%unlock.label.mountName" cacheShape="true" cache="true" />
|
||||||
|
<TextField GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||||
|
|
||||||
<!-- Row 3.4 -->
|
<!-- Row 3.4 -->
|
||||||
<Label GridPane.rowIndex="4" GridPane.columnIndex="0" text="%unlock.label.mountName" cacheShape="true" cache="true" />
|
<CheckBox GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
|
||||||
<TextField GridPane.rowIndex="4" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
|
||||||
|
|
||||||
<!-- Row 3.5 -->
|
<!-- Row 3.5 Alt1 -->
|
||||||
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
|
<Label GridPane.rowIndex="5" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
|
||||||
|
<ChoiceBox GridPane.rowIndex="5" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||||
<!-- Row 3.6 Alt1 -->
|
|
||||||
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
|
|
||||||
<ChoiceBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
|
||||||
|
|
||||||
<!-- Row 3.6 Alt2 -->
|
<!-- Row 3.5 Alt2 -->
|
||||||
<CheckBox GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="useOwnMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
|
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" fx:id="useOwnMountPath" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
|
||||||
|
|
||||||
<Label GridPane.rowIndex="7" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
|
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" fx:id="mountPathLabel" text="%unlock.label.mountPath" cacheShape="true" cache="true" />
|
||||||
|
|
||||||
<HBox GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="mountPathBox" spacing="6.0">
|
<HBox GridPane.rowIndex="6" GridPane.columnIndex="1" fx:id="mountPathBox" spacing="6.0">
|
||||||
<TextField GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="mountPath" cacheShape="true" cache="true" />
|
<TextField fx:id="mountPath" cacheShape="true" cache="true" />
|
||||||
<Button text="%unlock.label.mountPathButton" fx:id="changeMountPathButton" onAction="#didClickchangeMountPathButton"/>
|
<Button text="%unlock.label.mountPathButton" fx:id="changeMountPathButton" onAction="#didClickchangeMountPathButton"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
@@ -28,18 +28,9 @@
|
|||||||
<fx:define>
|
<fx:define>
|
||||||
<ContextMenu fx:id="moreOptionsMenu">
|
<ContextMenu fx:id="moreOptionsMenu">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem fx:id="mountVaultMenuItem" text="%unlocked.moreOptions.mount" onAction="#didClickMountVault">
|
|
||||||
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem fx:id="unmountVaultMenuItem" text="%unlocked.moreOptions.unmount" onAction="#didClickUnmountVault">
|
|
||||||
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem fx:id="revealVaultMenuItem" text="%unlocked.moreOptions.reveal" onAction="#didClickRevealVault">
|
<MenuItem fx:id="revealVaultMenuItem" text="%unlocked.moreOptions.reveal" onAction="#didClickRevealVault">
|
||||||
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem text="%unlocked.moreOptions.copyUrl" onAction="#didClickCopyUrl">
|
|
||||||
<graphic><Label text="" styleClass="ionicons"/></graphic>
|
|
||||||
</MenuItem>
|
|
||||||
</items>
|
</items>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</fx:define>
|
</fx:define>
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ main.createVault.nonEmptyDir.content=The selected directory already contains fil
|
|||||||
# welcome.fxml
|
# welcome.fxml
|
||||||
welcome.checkForUpdates.label.currentlyChecking=Checking for Updates...
|
welcome.checkForUpdates.label.currentlyChecking=Checking for Updates...
|
||||||
welcome.newVersionMessage=Version %1$s can be downloaded.\nThis is %2$s.
|
welcome.newVersionMessage=Version %1$s can be downloaded.\nThis is %2$s.
|
||||||
|
welcome.askForUpdateCheck.dialog.title=Update check
|
||||||
|
welcome.askForUpdateCheck.dialog.header=Enable the integrated update check?
|
||||||
|
welcome.askForUpdateCheck.dialog.content=To check for updates, Cryptomator will fetch the current version from the Cryptomator servers and display a hint to you if a newer version is available.\n\nWe recommend to enable the update check to always be sure you have the newest version of Cryptomator, with all security patches, installed. If you do not enable the update check you may check and download the current version from https://cryptomator.org/downloads/.\n\nYou can change this at any time from within the settings.
|
||||||
|
|
||||||
# initialize.fxml
|
# initialize.fxml
|
||||||
initialize.label.password=Password
|
initialize.label.password=Password
|
||||||
@@ -62,7 +65,6 @@ upgrade.version5toX.msg=This vault needs to be migrated to a newer format.\nPlea
|
|||||||
# unlock.fxml
|
# unlock.fxml
|
||||||
unlock.label.password=Password
|
unlock.label.password=Password
|
||||||
unlock.label.savePassword=Save Password
|
unlock.label.savePassword=Save Password
|
||||||
unlock.label.mountAfterUnlock=Mount Drive
|
|
||||||
unlock.label.mountName=Drive Name
|
unlock.label.mountName=Drive Name
|
||||||
unlock.label.unlockAfterStartup=Auto-Unlock on Start (Experimental)
|
unlock.label.unlockAfterStartup=Auto-Unlock on Start (Experimental)
|
||||||
unlock.label.revealAfterMount=Reveal Drive
|
unlock.label.revealAfterMount=Reveal Drive
|
||||||
@@ -100,10 +102,7 @@ changePassword.errorMessage.decryptionFailed=Decryption failed
|
|||||||
|
|
||||||
# unlocked.fxml
|
# unlocked.fxml
|
||||||
unlocked.button.lock=Lock Vault
|
unlocked.button.lock=Lock Vault
|
||||||
unlocked.moreOptions.mount=Mount Drive
|
|
||||||
unlocked.moreOptions.unmount=Eject Drive
|
|
||||||
unlocked.moreOptions.reveal=Reveal Drive
|
unlocked.moreOptions.reveal=Reveal Drive
|
||||||
unlocked.moreOptions.copyUrl=Copy Filesystem URL
|
|
||||||
unlocked.label.mountFailed=Connecting drive failed
|
unlocked.label.mountFailed=Connecting drive failed
|
||||||
unlocked.label.revealFailed=Command failed
|
unlocked.label.revealFailed=Command failed
|
||||||
unlocked.label.unmountFailed=Ejecting drive failed
|
unlocked.label.unmountFailed=Ejecting drive failed
|
||||||
@@ -123,9 +122,10 @@ settings.webdav.port.apply=Apply
|
|||||||
settings.webdav.prefGvfsScheme.label=WebDAV Scheme
|
settings.webdav.prefGvfsScheme.label=WebDAV Scheme
|
||||||
settings.debugMode.label=Debug Mode *
|
settings.debugMode.label=Debug Mode *
|
||||||
settings.requiresRestartLabel=* Cryptomator needs to restart
|
settings.requiresRestartLabel=* Cryptomator needs to restart
|
||||||
settings.volume.label= Mount-Methode *
|
settings.volume.label=Preferred Volume Type
|
||||||
settings.volume.webdav=WebDAV
|
settings.volume.webdav=WebDAV
|
||||||
settings.volume.fuse=FUSE
|
settings.volume.fuse=FUSE
|
||||||
|
settings.volume.dokany=Dokany
|
||||||
|
|
||||||
# tray icon
|
# tray icon
|
||||||
tray.menu.open=Open
|
tray.menu.open=Open
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 699 B |
Binary file not shown.
|
After Width: | Height: | Size: 353 B |
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
Binary file not shown.
|
After Width: | Height: | Size: 378 B |
Binary file not shown.
|
After Width: | Height: | Size: 760 B |
@@ -7,15 +7,14 @@ import org.mockito.Mockito;
|
|||||||
|
|
||||||
public class PasswordStrengthUtilTest {
|
public class PasswordStrengthUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test(timeout = 5000)
|
||||||
public void testLongPasswordsWillBeRatedAsStrong() {
|
public void testLongPasswords() {
|
||||||
PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(Localization.class));
|
PasswordStrengthUtil util = new PasswordStrengthUtil(Mockito.mock(Localization.class));
|
||||||
StringBuilder longPwBuilder = new StringBuilder();
|
StringBuilder longPwBuilder = new StringBuilder();
|
||||||
for (int i = 0; i < 101; i++) {
|
for (int i = 0; i < 10000; i++) {
|
||||||
longPwBuilder.append('x');
|
longPwBuilder.append('x');
|
||||||
}
|
}
|
||||||
int strength = util.computeRate(longPwBuilder.toString());
|
util.computeRate(longPwBuilder.toString());
|
||||||
Assert.assertEquals(4, strength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user