Compare commits

..
Author SHA1 Message Date
Armin Schrenk d013497c48 cleanup 2023-05-09 09:23:20 +02:00
Armin Schrenk 8691eec42b enable directory listings in jar files 2023-05-08 21:21:43 +02:00
Armin Schrenk 0f9d572677 remove static language and read i18dir in constructor 2023-05-08 20:28:56 +02:00
Armin Schrenk baee23b6d4 Merge branch 'develop' into feature/dynamic-languages
# Conflicts:
#	src/main/java/org/cryptomator/launcher/SupportedLanguages.java
#	src/main/java/org/cryptomator/ui/preferences/InterfacePreferencesController.java
2023-05-08 19:56:38 +02:00
Armin Schrenk 7bfd0ed341 load supported languages on app start 2023-04-24 18:52:09 +02:00
240 changed files with 2573 additions and 7002 deletions
+1 -2
View File
@@ -43,7 +43,6 @@ body:
- WinFsp (Local Drive)
- FUSE-T
- macFUSE
- FUSE
- WebDAV (Windows Explorer)
- WebDAV (AppleScript)
- WebDAV (gio)
@@ -96,4 +95,4 @@ body:
id: further-info
attributes:
label: Anything else?
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
+117 -19
View File
@@ -2,25 +2,123 @@
## Reporting a Vulnerability
We take security seriously at Cryptomator. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
For reporting security-related vulnerabilities or exploits that [haven't been reported yet](https://github.com/cryptomator/cryptomator/labels/type%3Asecurity-issue), contact us at: security@cryptomator.org
To report a security vulnerability, please use the [GitHub Security Advisory feature](https://github.com/cryptomator/cryptomator/security/advisories). This feature allows you to privately discuss, fix, and publish information about security vulnerabilities.
<details>
<summary>PGP Key</summary>
If you prefer to report the vulnerability via email, please send an email to security@cryptomator.org.
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
PGP key fingerprint: `3647 9903 B23A E0A5 9359  9A3E 23B5 DBEF 94D4 D81D` ([public key](https://gist.github.com/cryptobot/864300b6b44ae2d2a15abedfe14bd040))
## Expectations
When reporting a vulnerability, please provide us with a detailed report that includes:
- A description of the vulnerability
- Steps to reproduce the vulnerability
- Possible impact of the vulnerability
- Any additional information that may be helpful
We ask that you do not publicly disclose the vulnerability until we have had a chance to address it.
## Thank You
We appreciate your help in keeping Cryptomator secure. Thank you for your contributions to the security of our project.
mQINBFbgeicBEADM9AcU6DTgM5KZnBaJc6x9DBLr+TCMHntTt7YM9GLTlO2Z43Jt
oYoyqdRWAY28veqpLEFgRvvVD3fdBj/KUOxF1cr2JsErwXqbjwaLq0o/0KIXz7UK
a6pQSemZKfpOtJrfacofOTwvG6AuG9uakBYNMyxuojyOkoh3xsYS1KZ7TwPgCdET
t8/zva41Pa5kh5+GeSZJdCuygG6ynPBJEpmK5V7Qizvics5fziXecF+QaFZijafv
YahfxokvF9pXCQTmV4m57NQma9uK0w83U9nJCPjEd+x3wK0Hxrc1ojy8ZFTA1YND
AQg/MTABgHbQQkXDQhjS/TloOObqtbMBqNSbcSXpaR4teaCWKBl1MSq00nJLj8db
vPJGqfg7UbXhlALggp029/kskYlR5SmbxWquLbl0Xre3fDHuHEiWcJL6MS3454Wt
Mno13/4UhOlRFh5g0pLmPz7seOTJjDqc9abn/RXOLq0+3qX0gC0bDm5aCE5dQ2MV
FMbrrlw/dZESNLZvtB3gOsramSry1R3HVZ0QJ2vMaF2cxewebqcYbuecUNj6bxpv
5LEhEmqz6dG1meLLWDsvQLPEUWEIJnfpBiDSm342yxJq4pXnVF+aqAQsCL3FpmvZ
2j0FgFOs7iXOcFUJIiR0xUmWPk1NWYcUowqmRW8pMM9nFUzFF99iggPznwARAQAB
tC1DcnlwdG9tYXRvciBTdXBwb3J0IDxzdXBwb3J0QGNyeXB0b21hdG9yLm9yZz6J
AkAEEwEKACoCGwMFCQcrKAAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAleu2cQC
GQEACgkQI7Xb75TU2B3+7A/7BKRWdo5/moCCEbBzYQ7vRMLFdwmjFFlSZ7aGC0fP
YHdeUwxPbO0cATwmNpGMma7rBn1FDg3Vto6/wottGxm+XIRwlyY84CD1VZAihZ/e
WvjOO28/7VgRy6PGKzlhpDSoT8GwFOgO69e7bEff1Zj562RZe7nXc4tDivILMB++
KgmmSgtddygmNQCS3RD3KssGo+l+cSjsg09F5WAJ6nQe8Jq2hICq+o/P6UXPI5lX
bhvWYDn4/8sRHsIlGpQYYDDe0fz7IQKuSLAHpF5upNDxj6dYb05F8PPVrk6MW6nL
/kf1fZ27DlLN5/NFvhhBRuwxxoAFqPS7Iel3z7L0JkRUYmGLVB5m9Cqiw6FK8JRv
OtvakdDoKb5lVAoN5NeBfNBSqEcXVF/EdfTfIyyo7hZRA6xFMEVbmYbzt0sj0djV
ZOey2TOFrTCpkHfUUDgKvk5sn+F3u8mmPIbqquEzlFJSFjcyiYYDv22rg1In+zKV
Xmw4BFZRDS6IVSQRGlskRGJBixCaGyDYxHXXT2cg4Rk9uiCX11+0E9qlAsg6xPe6
rnaYDT8dU0AFyVpDpshflXH3kVQSpiqZS3jkAk1/54ODO8pE80Zrnd5m5AMuNcmX
+9MkZKE+h0882UskDs1dyt26GU2hoy4lAeRUaut7zIK/WO6nnuLaTvGWT95RDz+q
kD2JAiIEEwEKAAwFAleu2iYFgweGH4AACgkQZnuGbqgkCgnmCA//U22uhyEC/Tp3
Cbt5lctQmqbgMbjRBaHQyW52tPFMaq8vXMbo/5TTtVC6xsp2PJT84cxAd8KX8hWq
cPtF4wWCJGng/AzyxQ5dWfGvA/ll32ygjtJN3P/AvA9KlhG+6XYmS8cPkBkJBi6B
2yCdZT1cXc/TPAFzjgAwz7K9g3awG0OeOc/CXymH0DD/snkiwKQoucStolYywZGc
GszjMQgeT4zOc1wtEz24uL3dMNDlDcQMAh56YvK2oB0iMYmAFyX/IS+f2bM9paXi
HX+mg/z53iwgf5ZXbslNDbMTJ5GNksjEGjCFfDHAdNdgT+lcW4l2U7q4PYUaN4LA
DE9j2OlOlQ9qjucOgoCStirnTP7XHd4p31lgdz8+THOQowB5Ji95OkiNQAFCfxBt
mcA/bWnJZQDm7L8RVzHovBpAaK6vUjxEvR+DXdESSzyZwkpsZwGZcyqGRT26R1/L
JE5WvjKufNc5v3Cat320MjyrLZwVGRgvEpDMoCw3nTWl9AtOj5vgaakEWr7AnqET
xk7UFbYmdTlQqkWuLKubz9Rx/FbrBmvd6vwTHy1Dfl6QyMWNCClatgN00Hxped/6
CErg+R/RXd8apGxnOuWDqoujPn5LOHzgJolp1Ox16nTiZe2G+LbDr3hqRFi1wW6w
ioMB4KpkdA03uyxJSWmDEMiR1l3Oxom0KUNyeXB0b21hdG9yIFByZXNzIDxwcmVz
c0BjcnlwdG9tYXRvci5vcmc+iQI9BBMBCgAnBQJXrtnDAhsDBQkHKygABQsJCAcD
BRUKCQgLBRYCAwEAAh4BAheAAAoJECO12++U1NgdQYMQAKCIzNJF8rURQcFLSv3J
sPBjRy2HCzCWm21MuhU+bsaZx7U9M9dgEjzLfxN9s19VsBH3WKLgok2FgiYSGka3
6Oy/P8VFLFmHs7dS9i2fro2eF7i4zj/ZD/9t0jM4ZIgLpbzr5sTBld292nsfXGob
xOJeOx3oWYyR2FO9VQxXjC3JvJyZkFgoy0tauS4Mvii4cF56wJGcxDTbe1s7UaRC
a/fh4zgISZSBE3rYhCawkN4mqMDM5RDjrdtjKUPWk345HcjjQ4Wos8xw4YbGbNr9
Pc7m2URYJJ0jFM4tnoRF6cmA3bT9tm8pcOFg+K/ycVrltVEy+A8Wj8UGjyP1uI1t
EqWHN3LZpIGfW0w9AGrw7OUI9czXcukfngj/DsOU3WMBDIM8pW9+zBpr75yIS6lz
C0IqksLXSqX0b/Rby4O+wb6UZ1ZFkaim2GGtAZV+nGXtdnEXSNFiP7ykzjZ02m/1
7CKyj3VmdAgT56zEIypFSfxm9gOWsJPmfhSyuE8bFyoitgNxpheZk6xZy4upVMPR
WK3hutScU0yDv2HVCiA3o3Ggy42nmz9HpGF6W2DmBx4bhMaVs6I2VFyKdQzmJD/3
FCWjwz8PiEgVGHGPnD+WdPFLhrc/44gF4h/VuLjkubtULGuTVvgjeTIJ5LR1Gmwc
YOk6eD7MAJPzJVj5/PYFtIbKiQIiBBMBCgAMBQJXrtonBYMHhh+AAAoJEGZ7hm6o
JAoJBh4P/1w88YMTKUHpFTfJEwH2hK36BZN96Bf/k+vP7n1Xxp3NheInJblHFOt/
ccsup6am+APrk8gGtlIVmtVc3nO8WMsWxfJxGDecyRsNbessnODv/llyg3tzVU/H
tLk7gLiK0TcIsOLfeNXGTxRRSKWjVFsNfuixNCzzHa7tFq6ddVn9VRZ8fqJB2p21
OogWSDqUo9q9Wfb4RkYHguDx+8Jzoo/MxR1TSt8gUO2xDvEbqgeQiMCLF8R0lO3Y
zz0FrpyOsFU1CxVp+wo55bWv1UdwgQKQt4o0m5/zDJ2RAtscXpd4YcTE+XxKeK+4
qhihhkhLGpKsxzK5m9/qwMbodHwoBCBzfalkUR9xOq9yQIeEoC8XYL62NqB3BCSU
KfWFIHxUkE9WH5zHWaV+bhrlNgk7nz3xBfPf1P2mNIc1VUHoNqOZOmWwz2VaKLSW
f3GIqx9wGythFbLdXmUoC3W//DDYgQnvImvkncMqQ5nRHPf8uHcLQK5WZyIxpgWT
eKon5G/cj0BTptcBhapMwSIyfaC5FV7so0/CkOA6R9Fyq2VpGoHy7XPhFS+6ieLi
KUWhCvbuf2deWbSaJ0peMdzy1p72UXwrsEM0M3Fz+Jd8zvCaFzf5Fx27+pAAdlfg
4bT3/2gSf7S+cU3+DnYOH0NeRt2Z2mjEKg9OwttTO/oDboQHdZlrtDRDcnlwdG9t
YXRvciBTZWN1cml0eS1UZWFtIDxzZWN1cml0eUBjcnlwdG9tYXRvci5vcmc+iQI9
BBMBCgAnBQJXrtnWAhsDBQkHKygABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJ
ECO12++U1NgddzoQAI78+Nvm6VvNuptXJjEmrpHRyKCnHF9wH5kxvF8WZCgpOkJ4
vONmyS+9ZlepnT83MpGm/MzdIMCnDJmDmqmA5ISBRcD7k9Gjzz5rPKwE2zDyo0M0
wF1L2UEUqAlcvE0e4twZcP2DGoNqdSf6IaWsXhQMb1a/rTMsoGZLuTB8kCbv6Ntl
ULahcRToTB2shsbZjzE896P6X5hDCfGWl0Jhcbf53pnXX1dOsEw3et9AGru1IUMs
UGM+wpgTwagRj+XB/WY1x9IznKtiHTq83Fvt+3bkg0+NIcV3GDqXDIUtqIwy8gDd
4KgBU+LkyxXFDa4OxLc53n6b+Iy1nDosM+SiqSzdCCgEs/dY1tQBn/7P1GT18dEe
tFgeH/c6wLvEpDIc9urAsYXf8H+1uy7glWpWTq8DE0yhCr4adjCqlIsVHQQO4UUW
NfqMGEFpJ+3HjSSwnvDGY78lLQh5d4vqWV435aNaMqZg0gJIA0FtiP1fRtmT73BG
N/tBNiBxretFR4B+x/TWqPd5iJV7/MAn/pa1WSOcaxzJrVUsjXdgLQCqcHWd4/w1
f4DU9cJjl3sxZlMdAlg8Q1bF+pmjQQ4WKZkqMtwpoUilfVXmL42ay1LBCgW68/uJ
OTyGfp8ntUsbbm5raGsny3TLqnacyG9hxcPGNTzD1+MrbUvfsc7+4U0dCZTuiQIi
BBMBCgAMBQJXrtonBYMHhh+AAAoJEGZ7hm6oJAoJ1DQP/R+1drZiZQU45ChMbfTb
XQjJRsUOGZp3PTWtx4KrVFvE8ea0PF+DZX5gLJYIU+iZmPXRpzFu6dKPbcZ7RfRt
5RRH102zDZzijt2CQd7YLO8wxUFoWX9X7DGgxXEcNjl9kFVmnyHgiTwTzuZ0Zy4y
PvoiwrhcZmXEYbOeV40gLFie6wuzz5IIcs01e30xIs+1/1gwmgI5UnG3jveUgmcj
f/lvg3POKiwrY5Uzw1FSruJx21X06wTpDcfOACID4L7aY9eg2B/qL2Xj8nuhejqG
+1AVTMk2o6pxkvevHmxYQfEpuWGCw0RCBn9ObWwz6Zn5J9pjGbMrM+b1/M2Ouv3N
cpoGgCSahKNsRMKO7RMrBG0jtLcasPSgZFYPJSZAAb+YhxKUbpPHzDIwTEjgM7CL
gKSyRTKyp5IoFK53bpXL/ZIjkAhMvyDult6+BL6vI0+h3BBA9I0FF2Qhe139xLv/
DS7aDiYAE9vGMGoeCBfxJPwUsDU3hrGe/wgL7fR6nmN7R2QffisBHKHsklORy9t3
w3YFRd5sBAxv+EOcdkgXEmqKOfVQ8KU9adQcxPDGMAK/esjVwxUxsaf2PF5noxxW
3zL2ureUO/mMoH5Cwr0BuM3HFb82t1JJd4IXlLEyNvDMFMwD2d7h37bGK7Y5hEsl
zL7Dm+wQRY8sxg4QOZHbJjQXuQINBFbgeicBEADnkxGSEL1zwACaiVqADKC6/pgO
MMWjxoENBT6r8Vnp1D5hfNDkEi9iXUpCEO6nzywBf3/4c4Yk1wBOBZ7YWyWXMf4v
2g1evxELO5z1UlAwna6HSl7G0omIBqzz1Er5IS7C9WEZM8ZggwcuswCrbxfz4+fN
t7cCL5QyOvuxez+vrn+VIgLQzKm+LV4Wc+OFbHIys+0saQUhItKO0/CsXGc8R314
jdN5UsZk/MUdPPAs+6OCr8d3PpJvR6IST76TtN8aDjSS9T6em7dwdGFEwCGww3Jc
xrAkvvUmSlscz+rnvHA5DYQGK6NXLenB40sVQVfch1r1VqwvlzA0u7OovjwM8+7u
+DaBQ0YejbdnC7yfeE91LmZkG6jRKfvTJkv18tjNsgZsVmM13xzP67fCFIB9M+lN
t9zEldGKHVwm+06FHIWJsBDRgrquNb9xd1vgHHeIbJvKf+LqZhVrbKVEneG34Km+
ndtb+mvcGc0fOoMU9lYrFaxAWl8oU9BchC9IyjcPZB445R+AhfTuoHSUViSCo6IO
TG0hQsJuNoKmDAU8l5sTsiFXuXBOo1wK8gTkRnhZHduZrZIjJXvT7efz1knLQ6eG
prZHf4CtbgHyAe2XZabetWtCsFcPbOjC7ezNK57UvVH98h2GkckxOM00BESMCTee
kYy7uG0v0rrajzHY1wARAQABiQIlBBgBCgAPBQJW4HonAhsMBQkHKygAAAoJECO1
2++U1NgdyAsQAKZUVA6pY225BASkeNiW31L7K4VeRYpAdFkiRex2zQFtj9Vovfi1
JeTs0fRm35dUsQraf1bkhsjEdPVZ3gD324/baauFO04KX+soyQvK/tUq8KO+5ALt
Ul5aAljuSwxfJWFpApv+Mbf7gOjm+77jirs7pgG/gCow/mkRlmKTwAmn2DXjkckC
2EH0mqmh5pdoNWKO7WeTFFbUmESsPcnB2FwTpEjHFvgHll+rmKpXZTgFYN4dDhhm
HsL/SCf/Nw+YIsuvErQ9TJVdJDLG8ZYatruk7dZZMPtFxvxM1Q36gDIpPEOKPkvm
dMXg6jHaIdYIaoMpzXFaXsQMdRuMtzbcA+CdwXVY55qGLtfmM/QuEiIJdDeeh7iB
+VAMyEFOOpi8IFhixaeMoZAmrKDqOkzPcMJVklLYq8N+b9p5JszYNwZEbpyWCACM
6K+iJzlWzW/OPZttGLJBgYuSYIJIuG80Cx5m5m1e5RAgQ1iT8nbfrS+gYttwP48J
V7SXQg7QugxG9l1vlK4VjnXiOFulJ7V0e/VyUBpJp3qHcCxFq3RnxVwlIqKZh+jm
Q1bk0H0Xodd27nQITfDP5ullByGW2Jrjs6SsXeR3jl9+t0XQfInU1L9d/wSOkMjL
9IMUt06lV4vB/WP2xioqLZiZ4eAi0E+lWkFxjZsgNs2xbOAYRThMB8a5
=W1Ri
-----END PGP PUBLIC KEY BLOCK-----
```
</details>
-24
View File
@@ -1,24 +0,0 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "UTC"
groups:
maven-dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/" # even for `.github/workflows`
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"
labels:
- "misc:ci"
+26 -57
View File
@@ -10,8 +10,7 @@ on:
required: false
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 19
jobs:
get-version:
@@ -21,50 +20,26 @@ jobs:
build:
name: Build AppImage
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
needs: [get-version]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
- os: [self-hosted, Linux, ARM64]
appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
java-package: 'jdk+fx'
cache: 'maven'
- name: Download OpenJFX jmods
id: download-jmods
- name: Ensure major jfx version in pom equals in jdk
shell: pwsh
run: |
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
fi
}
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
@@ -78,8 +53,8 @@ jobs:
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.zipfs
--strip-native-commands
--no-header-files
--no-man-pages
@@ -105,20 +80,18 @@ jobs:
--copyright "(C) 2016 - 2023 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources
@@ -129,10 +102,6 @@ jobs:
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
cp dist/linux/common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg
cp dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg
cp dist/linux/common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
cp dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
cp dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
@@ -143,7 +112,7 @@ jobs:
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit
run: |
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o appimagetool.AppImage
chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -155,8 +124,8 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Create detached GPG signatures
run: |
@@ -165,7 +134,7 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: appimage-${{ matrix.appimage-suffix }}
name: appimage
path: |
cryptomator-*.AppImage
cryptomator-*.AppImage.zsync
+4 -6
View File
@@ -6,8 +6,7 @@ on:
types: [labeled]
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 19
defaults:
run:
@@ -18,10 +17,10 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
@@ -54,9 +53,8 @@ jobs:
body: |-
:construction: Work in Progress
Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments.
Please be patient, the builds are still running. We will publish new versions of Cryptomator here in a few moments.
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
---
<!-- Don't forget to include the 💾 SHA-256 checksums of release artifacts: -->
+20 -17
View File
@@ -3,6 +3,9 @@ name: Build Debian Package
on:
workflow_dispatch:
inputs:
ref:
description: 'GitHub Ref (e.g. refs/tags/1.6.16)'
required: true
semver:
description: 'SemVer String (e.g. 1.7.0-beta1)'
required: true
@@ -16,20 +19,19 @@ on:
type: boolean
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
COFFEELIBS_JDK_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
JAVA_VERSION: 19
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-aarch64_bin-jmods.zip'
jobs:
build:
name: Build Debian Package
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
- id: versions
name: Get version information
run: |
@@ -43,13 +45,12 @@ jobs:
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
@@ -57,11 +58,9 @@ jobs:
id: download-jmods
run: |
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
mkdir -p jmods/amd64
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
mkdir -p jmods/aarch64
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
- name: Ensure major jfx version in pom and in jmods is the same
@@ -98,8 +97,7 @@ jobs:
run: |
cp -r dist/linux/debian/ pkgdir
export RFC2822_TIMESTAMP=`date --rfc-2822`
export DISABLE_UPDATE_CHECK=${{ inputs.dput }}
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
mv pkgdir cryptomator_${{ inputs.ppaver }}
@@ -143,10 +141,15 @@ jobs:
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
# If ref is a tag, also upload to GitHub Releases:
- name: Determine tag name
if: startsWith(inputs.ref, 'refs/tags/')
run: |
REF=${{ inputs.ref }}
echo "TAG_NAME=${REF##*/}" >> $GITHUB_ENV
- name: Publish Debian package on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(inputs.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
run: |
artifacts=$(ls | grep cryptomator*.deb)
gh release upload ${{ github.ref_name }} $artifacts
gh release upload ${{ env.TAG_NAME }} $artifacts
+3 -9
View File
@@ -2,7 +2,7 @@ name: Update Error Database
on:
discussion:
types: [created, edited, deleted, category_changed, answered, unanswered]
types: [created, edited, category_changed, answered, unanswered]
discussion_comment:
types: [created, edited, deleted]
@@ -12,7 +12,6 @@ jobs:
if: github.event.discussion.category.name == 'Errors'
steps:
- name: Query Discussion Data
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
id: query-data
uses: actions/github-script@v6
with:
@@ -48,13 +47,8 @@ jobs:
- name: Merge Error Code Data
run: |
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
if [ ! -z "$DISCUSSION" ]
then
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
jq -s '.[0] * .[1]' original.json new.json > merged.json
else
cat original.json | jq 'del(.[] | select(.url=="https://github.com/cryptomator/cryptomator/discussions/${{ github.event.discussion.number }}"))' > merged.json
fi
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
jq -s '.[0] * .[1]' original.json new.json > merged.json
env:
DISCUSSION: ${{ steps.query-data.outputs.result }}
- name: Patch Gist
+5 -4
View File
@@ -22,8 +22,9 @@ on:
value: ${{ jobs.determine-version.outputs.type }}
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 19
JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
jobs:
determine-version:
@@ -35,7 +36,7 @@ jobs:
revNum: ${{ steps.versions.outputs.revNum }}
type: ${{ steps.versions.outputs.type}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
@@ -43,7 +44,7 @@ jobs:
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
cache: ${{ env.JAVA_CACHE }}
- id: versions
name: Get version information
run: |
+22 -42
View File
@@ -8,15 +8,9 @@ on:
version:
description: 'Version'
required: false
notarize:
description: 'Notarize'
required: true
default: false
type: boolean
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 19
jobs:
get-version:
@@ -37,45 +31,31 @@ jobs:
output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip'
openjfx-sha: '55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c'
- os: [self-hosted, macOS, ARM64]
architecture: aarch64
output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
architecture: ${{ matrix.architecture }}
check-latest: true
cache: 'maven'
- name: Download OpenJFX jmods
id: download-jmods
- name: Ensure major jfx version in pom equals in jdk
if: ${{ !contains(matrix.os, 'self-hosted') }}
shell: pwsh
run: |
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
mkdir -p openjfx-jmods/
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
fi
}
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
@@ -89,8 +69,8 @@ jobs:
${JAVA_HOME}/bin/jlink
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs
--strip-native-commands
--no-header-files
--no-man-pages
@@ -115,17 +95,16 @@ jobs:
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
@@ -222,12 +201,13 @@ jobs:
--app-drop-link 512 245
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".fseventsd" 320 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
if: startsWith(github.ref, 'refs/tags/')
uses: cocoalibs/xcode-notarization-action@v1
with:
app-path: 'Cryptomator-*.dmg'
+3 -4
View File
@@ -4,8 +4,7 @@ on:
pull_request:
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
JAVA_VERSION: 19
defaults:
run:
@@ -17,10 +16,10 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
+4 -1
View File
@@ -6,6 +6,9 @@ on:
- 'release/**'
- 'hotfix/**'
env:
JAVA_VERSION: 19
defaults:
run:
shell: bash
@@ -15,7 +18,7 @@ jobs:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- id: validate-pom-version
name: Validate POM version
run: |
+29 -61
View File
@@ -10,14 +10,15 @@ on:
required: false
isDebug:
description: 'Build debug version with console output'
type: boolean
type: boolean
env:
JAVA_DIST: 'zulu'
JAVA_VERSION: 21
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
JAVA_VERSION: 19
JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_windows-x64_bin-jmods.zip'
JFX_JMODS_HASH: 'B7CF2CAD2468842B3B78D99F6C0555771499A36FA1F1EE3DD1B9A4597F1FAB86'
defaults:
run:
@@ -37,20 +38,20 @@ jobs:
LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
java-package: 'jdk'
cache: ${{ env.JAVA_CACHE }}
- name: Download and extract JavaFX jmods from Gluon
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
run: |
curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
exit 1;
}
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
@@ -82,7 +83,7 @@ jobs:
--verbose
--output runtime
--module-path "jfxjmods;${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs
--strip-native-commands
--no-header-files
--no-man-pages
@@ -111,18 +112,17 @@ jobs:
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
--resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico
@@ -143,29 +143,9 @@ jobs:
- name: Fix permissions
run: attrib -r appdir/Cryptomator/Cryptomator.exe
shell: pwsh
- name: Extract jars with DLLs for Codesigning
- name: Extract integrations DLL for code signing
shell: pwsh
run: |
Add-Type -AssemblyName "System.io.compression.filesystem"
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
$jarExtractDir = New-Item -Path ".\appdir\jar-extract" -ItemType Directory
#for all jars inspect
Get-ChildItem -Path $jarFolder -Filter "*.jar" | ForEach-Object {
$jar = [Io.compression.zipfile]::OpenRead($_.FullName)
if (@($jar.Entries | Where-Object {$_.Name.ToString().EndsWith(".dll")} | Select-Object -First 1).Count -gt 0) {
#jars containing dlls extract
Set-Location $jarExtractDir
Expand-Archive -Path $_.FullName
}
$jar.Dispose()
}
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
shell: pwsh
run: |
New-Item -Path appdir/jpackage-jmod -ItemType Directory
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
run: gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --extract integrations.dll }
- name: Codesign
uses: skymatic/code-sign-action@v2
with:
@@ -174,22 +154,12 @@ jobs:
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator
timestampUrl: 'http://timestamp.digicert.com'
folder: appdir
folder: appdir/Cryptomator
recursive: true
- name: Replace DLLs inside jars with signed ones
- name: Repack signed DLL into jar
shell: pwsh
run: |
$jarExtractDir = Resolve-Path ".\appdir\jar-extract"
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
Get-ChildItem -Path $jarExtractDir | ForEach-Object {
$jarName = $_.Name
$jarFile = "${jarFolder}\${jarName}.jar"
Set-Location $_
Get-ChildItem -Path $_ -Recurse -File "*.dll" | ForEach-Object {
# update jar with signed dll
jar --file="$jarFile" --update $(Resolve-Path -Relative -Path $_)
}
}
gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --update integrations.dll; Remove-Item integrations.dll}
- name: Generate license for MSI
run: >
mvn -B license:add-third-party
@@ -223,7 +193,6 @@ jobs:
--file-associations dist/win/resources/FAvaultFile.properties
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Codesign MSI
uses: skymatic/code-sign-action@v2
with:
@@ -265,7 +234,7 @@ jobs:
runs-on: windows-latest
needs: [get-version, build-msi]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Download .msi
uses: actions/download-artifact@v3
with:
@@ -277,8 +246,7 @@ jobs:
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
cache: ${{ env.JAVA_CACHE }}
- name: Generate license for exe
run: >
mvn -B license:add-third-party
@@ -390,7 +358,7 @@ jobs:
cp msi/*.msi files
cp exe/*.exe files
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
uses: SamKirkland/FTP-Deploy-Action@4.3.3
with:
protocol: ftps
server: allowlist.kaspersky-labs.com
@@ -399,7 +367,7 @@ jobs:
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
local-dir: files/
- name: Upload to Avast
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
protocol: ftp
server: whitelisting.avast.com
+1 -2
View File
@@ -53,10 +53,9 @@
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="ENUM_CONSTANTS_WRAP" value="2" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
+12 -11
View File
@@ -14,10 +14,10 @@
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.48.1/dagger-compiler-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.45/dagger-compiler-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.45/dagger-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.45/dagger-producers-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -26,19 +26,20 @@
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.45/dagger-spi-2.45.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.7.0-1.0.6/symbol-processing-api-1.7.0-1.0.6.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.7.0/kotlin-stdlib-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.0/kotlin-stdlib-jdk8-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.0/kotlin-stdlib-jdk7-1.7.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.5.0/kotlinx-metadata-jvm-0.5.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
+1 -1
View File
@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.p12Path=&quot;~/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;~/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+14 -21
View File
@@ -1,5 +1,4 @@
#!/bin/bash
set -e
cd $(dirname $0)
REVISION_NO=`git rev-list --count HEAD`
@@ -11,13 +10,13 @@ command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
SEMVER_STR=${VERSION}
MACHINE_TYPE=$(uname -m)
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
cp ../../../LICENSE.txt ../../../target
cp ../launcher.sh ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods
# add runtime
@@ -25,7 +24,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.zipfs \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -45,21 +44,19 @@ ${JAVA_HOME}/bin/jpackage \
--name Cryptomator \
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" \
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources
@@ -71,10 +68,6 @@ envsubst '${REVISION_NO}' < resources/AppDir/bin/cryptomator.sh > Cryptomator.Ap
cp ../common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
cp ../common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
cp ../common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
cp ../common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg
cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg
cp ../common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
@@ -85,17 +78,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage
/tmp/appimagetool.AppImage \
Cryptomator.AppDir \
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
cryptomator-${SEMVER_STR}-x86_64.AppImage \
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
echo ""
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-x86_64.AppImage"
echo ""
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.AppImage"
echo ""
@@ -66,13 +66,6 @@
</content_rating>
<releases>
<release date="2023-09-20" version="1.10.1"/>
<release date="2023-09-11" version="1.10.0"/>
<release date="2023-08-11" version="1.9.4"/>
<release date="2023-08-07" version="1.9.3"/>
<release date="2023-07-24" version="1.9.2"/>
<release date="2023-06-07" version="1.9.1"/>
<release date="2023-05-30" version="1.9.0"/>
<release date="2023-04-25" version="1.8.0"/>
<release date="2023-04-07" version="1.7.5"/>
<release date="2023-04-05" version="1.7.4"/>
@@ -1,12 +0,0 @@
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
<style
id="current-color-scheme" type="text/css">
.ColorScheme-Text {
color:#232629;
}
</style>
<g fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor">
<path d="m15.591 35.824c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.043c.051 1.03-.161 2.644-.509 2.834zm6.409-6.824h-2l.5-5a2 2 0 1 1 1 0zm-14.544-3.241.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm13.544-10.759a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm14.473 6a8.067 8.067 0 0 0 -8.08 8v2.141a3.891 3.891 0 0 0 -2.893 3.734v5.125a23.166 23.166 0 0 1 -4.174-1.623 7.857 7.857 0 0 1 -.027.878 3.263 3.263 0 0 1 -.729 2.074l-1.794 1.483a.379.379 0 0 1 -.276.188h-4c-1.324 0-2.346-1.336-2.653-3.343a7.058 7.058 0 0 1 .234-3.18 3.477 3.477 0 0 1 1.636-2.157 1.868 1.868 0 0 1 .783-.32h1.5a8.035 8.035 0 0 1 -1.5-5 11.1 11.1 0 0 1 .5-3 2.519 2.519 0 0 0 0-1.5 13.272 13.272 0 0 1 -.5-3.5c6.687-1.936 11 0 11 0s4.319-1.955 11 0"/>
<path d="m39 28h-10v-4a3.13 3.13 0 0 1 3-3 3.087 3.087 0 0 1 3 3v1a1.034 1.034 0 0 0 1 1h1a1.034 1.034 0 0 0 1-1v-1a6 6 0 0 0 -12 0v4h-1a2.073 2.073 0 0 0 -2 2v6a2.073 2.073 0 0 0 2 2h14a2.073 2.073 0 0 0 2-2v-6a2.073 2.073 0 0 0 -2-2zm-5.391 5.94a1.609 1.609 0 0 1 -3.217 0v-1.876a1.609 1.609 0 0 1 3.217 0z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,8 +0,0 @@
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text {
color:#232629;
}
</style>
<path d="m32.66 29.319a1.432 1.432 0 0 0 -.66-.319h-1.5a8.125 8.125 0 0 0 1.5-5 11.027 11.027 0 0 0 -.5-3 2.519 2.519 0 0 1 0-1.5 12.987 12.987 0 0 0 .5-3.5c-6.681-1.955-11 0-11 0s-4.313-1.936-11 0a13.272 13.272 0 0 0 .5 3.5 2.519 2.519 0 0 1 0 1.5 11.1 11.1 0 0 0 -.5 3 8.035 8.035 0 0 0 1.5 5h-1.5a1.868 1.868 0 0 0 -.783.319 3.477 3.477 0 0 0 -1.636 2.157 7.058 7.058 0 0 0 -.234 3.18c.307 2.008 1.329 3.344 2.653 3.344h4a.379.379 0 0 0 .277-.187l1.793-1.483a3.263 3.263 0 0 0 .729-2.074 7.857 7.857 0 0 0 .027-.878 23.166 23.166 0 0 0 4.174 1.622 24.4 24.4 0 0 0 4.051-1.614 7.848 7.848 0 0 0 .027.869 3.263 3.263 0 0 0 .729 2.074l1.793 1.484a.61.61 0 0 0 .4.187h4c1.324 0 2.223-1.336 2.529-3.343a7.057 7.057 0 0 0 -.234-3.18 3.477 3.477 0 0 0 -1.635-2.158zm-17.069 6.5c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.048c.051 1.03-.161 2.644-.509 2.834zm6.409-6.819h-2l.5-5a2 2 0 1 1 1 0zm6.38 7.921a.418.418 0 0 1 -.627.111c-.522-.433-1.439-1.2-1.458-1.208-.348-.189-.56-1.8-.505-2.828a15.84 15.84 0 0 0 2.9-2.037 9.322 9.322 0 0 1 -.31 5.962zm-20.924-11.162.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm33.217 1.2a3.021 3.021 0 0 0 -2.658-1.525 1.574 1.574 0 0 0 -.107-1.283l-.745-1.367a1.779 1.779 0 0 0 -1.317-.891l-.7-1.278a2.409 2.409 0 0 0 .229-2.661 2.283 2.283 0 0 0 -2.375-1.454 7.039 7.039 0 0 1 0 3 3.272 3.272 0 0 0 0 1.5c.047.152-.047.3 0 .5.227.04-.069.156.165.14l.653 1.2a1.579 1.579 0 0 0 -.019 1.557l.745 1.367a1.753 1.753 0 0 0 1.045.832 2.66 2.66 0 0 0 -.238 2.916 2.989 2.989 0 0 0 2.326 1.509.79.79 0 0 0 .082 0 .707.707 0 0 0 .717-.6.688.688 0 0 0 -.636-.749 1.546 1.546 0 0 1 -1.2-.78 1.408 1.408 0 0 1 .672-1.95 1.628 1.628 0 0 1 1.179-.089 1.512 1.512 0 0 1 .9.719 1.37 1.37 0 0 1 -.023 1.361.656.656 0 0 0 .284.92.748.748 0 0 0 .981-.266 2.648 2.648 0 0 0 .04-2.633zm-19.673-11.959a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2z" fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-21, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), coffeelibs-jdk-19, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git
-2
View File
@@ -1,8 +1,6 @@
cryptomator usr/lib
common/org.cryptomator.Cryptomator.desktop usr/share/applications
common/org.cryptomator.Cryptomator.svg usr/share/icons/hicolor/scalable/apps
common/org.cryptomator.Cryptomator.tray.svg usr/share/icons/hicolor/scalable/apps
common/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/scalable/apps
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
-2
View File
@@ -1,3 +1 @@
usr/lib/cryptomator/bin/cryptomator usr/bin/cryptomator
usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
+10 -13
View File
@@ -4,7 +4,7 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
@@ -27,7 +27,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \
--output runtime \
--module-path "${JMODS_PATH}" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.zipfs \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -43,23 +43,20 @@ override_dh_auto_build:
--name cryptomator \
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" \
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \
--verbose
+9 -34
View File
@@ -29,14 +29,6 @@ REVISION_NO=`git rev-list --count HEAD`
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
FUSE_LIB="FUSE-T"
ARCH="undefined"
if [ "$(machine)" = "arm64e" ]; then
ARCH="aarch64"
else
ARCH="x64"
fi
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-${ARCH}_bin-jmods.zip"
# check preconditions
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; }
@@ -46,32 +38,15 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
if [[ ! `security find-identity -v -p codesigning | grep -w "${CODESIGN_IDENTITY}"` ]]; then echo "Given codesign identity is invalid."; exit 1; fi
fi
# download and check jmods
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
mkdir -p openjfx-jmods/
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
exit 1
fi
# compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs \
--strip-native-commands \
--no-header-files \
--no-man-pages \
@@ -96,17 +71,16 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/${APP_NAME}/key.p12\"" \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
--java-options "-Dcryptomator.mountPointsDir=\"~/${APP_NAME}\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
@@ -169,5 +143,6 @@ create-dmg \
--app-drop-link 512 245 \
--eula "resources/license.rtf" \
--icon ".background" 128 758 \
--icon ".fseventsd" 320 758 \
--icon ".VolumeIcon.icns" 512 758 \
${APP_NAME}-${VERSION_NO}.dmg dmg
Binary file not shown.
+1 -1
View File
@@ -11,7 +11,7 @@ SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
SET LOOPBACK_ALIAS="cryptomator-vault"
powershell -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
powershell -NoLogo -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
+12 -35
View File
@@ -12,9 +12,6 @@ Param(
[bool] $clean
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
# check preconditions
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
{
@@ -29,7 +26,7 @@ if ((Get-Command "mvn" -ErrorAction SilentlyContinue) -eq $null)
$buildDir = Split-Path -Parent $PSCommandPath
$version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout)
$semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1'
$semVerNo = $version -replace '(\d\.\d\.\d).*','$1'
$revisionNo = $(git rev-list --count HEAD)
Write-Output "`$version=$version"
@@ -50,31 +47,11 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
Remove-Item -Path $runtimeImagePath -Force -Recurse
}
## download jfx jmods
$jmodsVersion='20.0.2'
$jmodsUrl = "https://download2.gluonhq.com/openjfx/${jmodsVersion}/openjfx-${jmodsVersion}_windows-x64_bin-jmods.zip"
$jfxJmodsChecksum = '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
$jfxJmodsZip = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $jfxJmodsZip) ) {
Write-Output "Downloading ${jmodsUrl}..."
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
}
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
exit 1;
}
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
& "$Env:JAVA_HOME\bin\jlink" `
--verbose `
--output runtime `
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--module-path "$Env:JAVA_HOME/jmods" `
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,jdk.zipfs `
--strip-native-commands `
--no-header-files `
--no-man-pages `
@@ -105,16 +82,15 @@ if ($clean -and (Test-Path -Path $appPath)) {
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Djava.net.useSystemProxies=true" `
--java-options "-Dcryptomator.logDir=`"@{localappdata}/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$AppName/keychain.json;@{userhome}/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"~/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources `
@@ -144,7 +120,6 @@ try {
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
$Env:JP_WIXHELPER_DIR = "."
& "$Env:JAVA_HOME\bin\jpackage" `
--verbose `
--type msi `
@@ -176,6 +151,8 @@ $Env:JP_WIXHELPER_DIR = "."
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
+1 -1
View File
@@ -3,5 +3,5 @@
::REPLACE ME
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS%
+1 -1
View File
@@ -2,4 +2,4 @@
:: see comments in file ./version170-migrate-settings.ps1
cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\version170-migrate-settings.ps1
+4 -18
View File
@@ -70,9 +70,6 @@
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>
<Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll"/>
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
<?ifndef SkipCryptomatorLegacyCheck ?>
<!-- Block installation if innosetup entry of Cryptomator is found -->
<Property Id="OLDEXEINSTALLER">
@@ -132,17 +129,11 @@
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
<?endif?>
<Property Id="WixQuietExec64CmdTimeout" Value="20" />
<!-- Note for custom actions: Immediate CAs run BEFORE the files are installed, hence if you depend on installed files, the CAs must be deferred.-->
<!-- WebDAV patches -->
<SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot;"
Sequence="execute" Before="PatchWebDAV" />
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
<SetProperty Id="V170MigrateSettings" Value="&quot;[INSTALLDIR]version170-migrate-settings.bat&quot;"
Sequence="execute" Before="V170MigrateSettings" />
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<CustomAction Id="V170MigrateSettings" Impersonate="no" ExeCommand="[INSTALLDIR]version170-migrate-settings.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
<!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
@@ -181,12 +172,11 @@
<?endif?>
<?ifndef JpAllowUpgrades ?>
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<?endif?>
<?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
<!-- Check and fail if Cryptomator is running -->
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
@@ -198,10 +188,6 @@
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallUISequence>
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
</Product>
+39 -63
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.11.0</version>
<version>1.9.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,52 +26,45 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>21</project.jdk.version>
<project.jdk.version>19</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.6.7</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>4.0.0-beta4</cryptomator.fuse.version>
<cryptomator.cryptofs.version>2.6.2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>2.0.5</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
<cryptomator.webdav.version>2.0.2</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<commons-lang3.version>3.13.0</commons-lang3.version>
<dagger.version>2.48.1</dagger.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<dagger.version>2.45</dagger.version>
<easybind.version>2.2</easybind.version>
<guava.version>32.1.3-jre</guava.version>
<jackson.version>2.15.3</jackson.version>
<javafx.version>20.0.2</javafx.version>
<jwt.version>4.4.0</jwt.version>
<nimbus-jose.version>9.37</nimbus-jose.version>
<logback.version>1.4.11</logback.version>
<slf4j.version>2.0.9</slf4j.version>
<tinyoauth2.version>0.7.0</tinyoauth2.version>
<zxcvbn.version>1.8.2</zxcvbn.version>
<guava.version>31.1-jre</guava.version>
<gson.version>2.10.1</gson.version>
<javafx.version>19.0.2.1</javafx.version>
<jwt.version>4.3.0</jwt.version>
<nimbus-jose.version>9.31</nimbus-jose.version>
<logback.version>1.4.5</logback.version>
<slf4j.version>2.0.6</slf4j.version>
<tinyoauth2.version>0.5.1</tinyoauth2.version>
<zxcvbn.version>1.7.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<mockito.version>5.6.0</mockito.version>
<junit.jupiter.version>5.9.2</junit.jupiter.version>
<mockito.version>5.1.1</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>24.0.1</jetbrains.annotations.version>
<dependency-check.version>8.4.0</dependency-check.version>
<jacoco.version>0.8.11</jacoco.version>
<license-generator.version>2.2.0</license-generator.version>
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
<mvn-compiler.version>3.11.0</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.6.0</mvn-dependency.version>
<mvn-surefire.version>3.1.2</mvn-surefire.version>
<mvn-jar.version>3.3.0</mvn-jar.version>
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>8.1.0</dependency-check.version>
<jacoco.version>0.8.8</jacoco.version>
</properties>
<dependencies>
@@ -164,11 +157,6 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- EasyBind -->
<dependency>
@@ -218,6 +206,11 @@
<artifactId>dagger</artifactId>
<version>${dagger.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- JUnit / Mockito / Hamcrest -->
<dependency>
@@ -247,7 +240,7 @@
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.3.0</version>
<version>1.2</version>
<scope>test</scope>
</dependency>
@@ -265,32 +258,32 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${mvn-compiler.version}</version>
<version>3.10.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>${mvn-resources.version}</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>${mvn-dependency.version}</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${mvn-surefire.version}</version>
<version>3.0.0-M7</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>${license-generator.version}</version>
<version>2.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${mvn-jar.version}</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
@@ -339,23 +332,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>me.fabriciorby</groupId>
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
<version>${junit-tree-reporter.version}</version>
</dependency>
</dependencies>
<configuration>
<argLine>--enable-preview</argLine>
<reportFormat>plain</reportFormat>
<consoleOutputReporter>
<disable>true</disable>
</consoleOutputReporter>
<statelessTestsetInfoReporter
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
</statelessTestsetInfoReporter>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+1 -24
View File
@@ -1,17 +1,4 @@
import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.GoogleDriveLocationPresetsProvider;
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.LeitzcloudLocationPresetsProvider;
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
@@ -38,7 +25,7 @@ open module org.cryptomator.desktop {
requires ch.qos.logback.core;
requires com.auth0.jwt;
requires com.google.common;
requires com.fasterxml.jackson.databind;
requires com.google.gson;
requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind;
@@ -50,16 +37,6 @@ open module org.cryptomator.desktop {
/* TODO: filename-based modules: */
requires static javax.inject; /* ugly dagger/guava crap */
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
provides LocationPresetsProvider with //
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
GoogleDriveLocationPresetsProvider, //
ICloudWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
LeitzcloudLocationPresetsProvider, //
MegaLocationPresetsProvider, //
OneDriveWindowsLocationPresetsProvider, OneDriveMacLocationPresetsProvider, OneDriveLinuxLocationPresetsProvider, //
PCloudLocationPresetsProvider;
}
@@ -139,9 +139,9 @@ public abstract class CommonsModule {
@Provides
@Singleton
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
return settings.port.map(port -> {
return settings.port().map(port -> {
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
return InetSocketAddress.createUnresolved(host, settings.port.intValue());
return InetSocketAddress.createUnresolved(host, settings.port().intValue());
});
}
@@ -2,7 +2,6 @@ package org.cryptomator.common;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,6 +18,8 @@ import java.util.stream.StreamSupport;
public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
private static final char PATH_LIST_SEP = ':';
private static final int DEFAULT_MIN_PW_LENGTH = 8;
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
@@ -32,7 +33,6 @@ public class Environment {
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
private Environment() {}
@@ -45,16 +45,15 @@ public class Environment {
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
}
public static Environment getInstance() {
@@ -77,7 +76,11 @@ public class Environment {
return getPaths(SETTINGS_PATH_PROP_NAME);
}
public Stream<Path> getIpcSocketPath() {
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Stream<Path> ipcSocketPath() {
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
}
@@ -85,24 +88,20 @@ public class Environment {
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
}
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Optional<Path> getLogDir() {
return getPath(LOG_DIR_PROP_NAME);
return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public Optional<String> getLoopbackAlias() {
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
}
public Optional<Path> getMountPointsDir() {
return getPath(MOUNTPOINT_DIR_PROP_NAME);
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public int getMinPwLength() {
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
public Optional<Path> getMountPointsDir() {
return getPath(MOUNTPOINT_DIR_PROP_NAME).map(this::replaceHomeDir);
}
/**
@@ -118,27 +117,36 @@ public class Environment {
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
}
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME);
public int getMinPwLength() {
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
}
public boolean showTrayIcon() {
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
}
public boolean disableUpdateCheck() {
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
}
private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);
}
@VisibleForTesting
Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
// visible for testing
public Path getHomeDir() {
return getPath("user.home").orElseThrow();
}
// visible for testing
public Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
}
private Path replaceHomeDir(Path path) {
if (path.startsWith(RELATIVE_HOME_DIR)) {
return getHomeDir().resolve(RELATIVE_HOME_DIR.relativize(path));
} else {
return path;
}
}
private Stream<String> getRawList(String propertyName, char separator) {
@@ -3,7 +3,6 @@ package org.cryptomator.common;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.Locale;
import java.util.Objects;
@@ -33,15 +32,18 @@ public class ErrorCode {
this.rootCauseSpecificFrames = rootCauseSpecificFrames;
}
public String methodCode() {
// visible for testing
String methodCode() {
return format(traceCode(rootCause, LATEST_FRAME));
}
public String rootCauseCode() {
// visible for testing
String rootCauseCode() {
return format(traceCode(rootCause, rootCauseSpecificFrames));
}
public String throwableCode() {
// visible for testing
String throwableCode() {
return format(traceCode(throwable, ALL_FRAMES));
}
@@ -115,7 +117,7 @@ public class ErrorCode {
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
*/
@VisibleForTesting
// visible for testing
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
if (allFrames.length < bottomFrames.length) {
// if frames had been stacked on top of bottomFrames, allFrames would be larger
@@ -125,7 +127,7 @@ public class ErrorCode {
}
}
@VisibleForTesting
// visible for testing
static <T> int commonSuffixLength(T[] set, T[] subset) {
Preconditions.checkArgument(set.length >= subset.length);
// iterate items backwards as long as they are identical
@@ -30,7 +30,7 @@ public class LicenseHolder {
this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject);
this.validLicenseProperty = validJwtClaims.isNotNull();
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey.get());
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
validJwtClaims.set(claims.orElse(null));
}
@@ -38,7 +38,7 @@ public class LicenseHolder {
Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
validJwtClaims.set(claims.orElse(null));
if (claims.isPresent()) {
settings.licenseKey.set(licenseKey);
settings.licenseKey().set(licenseKey);
return true;
} else {
return false;
@@ -0,0 +1,64 @@
package org.cryptomator.common;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
/**
* Enum of common cloud providers and their default local storage location path.
*/
public enum LocationPreset {
DROPBOX("Dropbox", "~/Dropbox"),
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
MEGA("MEGA", "~/MEGA"),
ONEDRIVE("OneDrive", "~/OneDrive"),
PCLOUD("pCloud", "~/pCloudDrive"),
LOCAL("local");
private final String name;
private final List<Path> candidates;
LocationPreset(String name, String... candidates) {
this.name = name;
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
}
/**
* Checks for this LocationPreset if any of the associated paths exist.
*
* @return the first existing path or null, if none exists.
*/
public Path existingPath() {
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
}
public String getDisplayName() {
return name;
}
@Override
public String toString() {
return getDisplayName();
}
//this contruct is needed, since static members are initialized after every enum member is initialized
//TODO: refactor this to normal class and use this also in different parts of the project
private static class UserHome {
private static final String USER_HOME = System.getProperty("user.home");
private static String resolve(String path) {
if (path.startsWith("~/")) {
return UserHome.USER_HOME + path.substring(1);
} else {
return path;
}
}
}
}
@@ -1,171 +0,0 @@
package org.cryptomator.common;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Enumeration;
import java.util.InvalidPropertiesFormatException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
class PropertiesDecorator extends Properties {
protected final Properties delegate;
PropertiesDecorator(Properties delegate) {
this.delegate = delegate;
}
@Override
public String getProperty(String key) {return delegate.getProperty(key);}
@Override
public String getProperty(String key, String defaultValue) {return delegate.getProperty(key, defaultValue);}
@Override
public synchronized Object setProperty(String key, String value) {
return delegate.setProperty(key, value);
}
@Override
public synchronized void load(Reader reader) throws IOException {delegate.load(reader);}
@Override
public synchronized void load(InputStream inStream) throws IOException {delegate.load(inStream);}
@Override
public void store(Writer writer, String comments) throws IOException {delegate.store(writer, comments);}
@Override
public void store(OutputStream out, @Nullable String comments) throws IOException {delegate.store(out, comments);}
@Override
public synchronized void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException {delegate.loadFromXML(in);}
@Override
public void storeToXML(OutputStream os, String comment) throws IOException {delegate.storeToXML(os, comment);}
@Override
public void storeToXML(OutputStream os, String comment, String encoding) throws IOException {delegate.storeToXML(os, comment, encoding);}
@Override
public void storeToXML(OutputStream os, String comment, Charset charset) throws IOException {delegate.storeToXML(os, comment, charset);}
@Override
public Enumeration<?> propertyNames() {return delegate.propertyNames();}
@Override
public Set<String> stringPropertyNames() {return delegate.stringPropertyNames();}
@Override
public void list(PrintStream out) {delegate.list(out);}
@Override
public void list(PrintWriter out) {delegate.list(out);}
@Override
public int size() {return delegate.size();}
@Override
public boolean isEmpty() {return delegate.isEmpty();}
@Override
public Enumeration<Object> keys() {return delegate.keys();}
@Override
public Enumeration<Object> elements() {return delegate.elements();}
@Override
public boolean contains(Object value) {return delegate.contains(value);}
@Override
public boolean containsValue(Object value) {return delegate.containsValue(value);}
@Override
public boolean containsKey(Object key) {return delegate.containsKey(key);}
@Override
public Object get(Object key) {return delegate.get(key);}
@Override
public synchronized Object put(Object key, Object value) {return delegate.put(key, value);}
@Override
public synchronized Object remove(Object key) {return delegate.remove(key);}
@Override
public synchronized void putAll(Map<?, ?> t) {delegate.putAll(t);}
@Override
public synchronized void clear() {delegate.clear();}
@Override
public synchronized String toString() {return delegate.toString();}
@Override
public Set<Object> keySet() {return delegate.keySet();}
@Override
public Collection<Object> values() {return delegate.values();}
@Override
public Set<Map.Entry<Object, Object>> entrySet() {return delegate.entrySet();}
@Override
public synchronized boolean equals(Object o) {return delegate.equals(o);}
@Override
public synchronized int hashCode() {return delegate.hashCode();}
@Override
public Object getOrDefault(Object key, Object defaultValue) {return delegate.getOrDefault(key, defaultValue);}
@Override
public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {delegate.forEach(action);}
@Override
public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {delegate.replaceAll(function);}
@Override
public synchronized Object putIfAbsent(Object key, Object value) {return delegate.putIfAbsent(key, value);}
@Override
public synchronized boolean remove(Object key, Object value) {return delegate.remove(key, value);}
@Override
public synchronized boolean replace(Object key, Object oldValue, Object newValue) {return delegate.replace(key, oldValue, newValue);}
@Override
public synchronized Object replace(Object key, Object value) {return delegate.replace(key, value);}
@Override
public synchronized Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) {return delegate.computeIfAbsent(key, mappingFunction);}
@Override
public synchronized Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.computeIfPresent(key, remappingFunction);}
@Override
public synchronized Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.compute(key, remappingFunction);}
@Override
public synchronized Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) {return delegate.merge(key, value, remappingFunction);}
@Override
public synchronized Object clone() {
var delegateClone = (Properties) delegate.clone();
return new PropertiesDecorator(delegateClone);
}
}
@@ -1,70 +0,0 @@
package org.cryptomator.common;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
public class SubstitutingProperties extends PropertiesDecorator {
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
private final Map<String, String> env;
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment) {
super(props);
this.env = systemEnvironment;
}
@Override
public String getProperty(String key) {
var value = delegate.getProperty(key);
if (key.startsWith("cryptomator.") && value != null) {
return process(value);
} else {
return value;
}
}
@Override
public String getProperty(String key, String defaultValue) {
var result = getProperty(key);
return result != null ? result : defaultValue;
}
@VisibleForTesting
String process(String value) {
return TEMPLATE.matcher(value).replaceAll(match -> //
switch (match.group(1)) {
case "appdir" -> resolveFrom("APPDIR", Source.ENV);
case "appdata" -> resolveFrom("APPDATA", Source.ENV);
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
case "userhome" -> resolveFrom("user.home", Source.PROPS);
default -> {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value);
yield match.group();
}
});
}
private String resolveFrom(String key, Source src) {
var val = switch (src) {
case ENV -> env.get(key);
case PROPS -> delegate.getProperty(key);
};
if (val == null) {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
return "";
} else {
return val.replace("\\", "\\\\");
}
}
private enum Source {
ENV,
PROPS;
}
}
@@ -23,14 +23,14 @@ public class KeychainModule {
@Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> {
if (!settings.useKeychain.get()) {
if (!settings.useKeychain().get()) {
return null;
}
var selectedProviderClass = settings.keychainProvider.get();
var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findFirst().orElse(null);
return selectedProvider.orElse(fallbackProvider);
}, settings.keychainProvider, settings.useKeychain);
}, settings.keychainProvider(), settings.useKeychain());
}
}
@@ -1,32 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.OperatingSystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
@OperatingSystem(LINUX)
public final class DropboxLinuxLocationPresetsProvider implements LocationPresetsProvider {
private static final Path USER_HOME = LocationPresetsProvider.resolveLocation("~/.").toAbsolutePath();
private static final Predicate<String> PATTERN = Pattern.compile("Dropbox \\(.+\\)").asMatchPredicate();
@Override
public Stream<LocationPreset> getLocations() {
try (var dirStream = Files.list(USER_HOME)) {
var presets = dirStream.filter(p -> Files.isDirectory(p) && PATTERN.test(p.getFileName().toString())) //
.map(p -> new LocationPreset(p.getFileName().toString(), p)) //
.toList();
return presets.stream(); //workaround to ensure that the directory stream is always closed
} catch (IOException | UncheckedIOException e) { //UncheckedIOException thrown by the stream of Files.list()
return Stream.of();
}
}
}
@@ -1,35 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
@OperatingSystem(MAC)
@CheckAvailability
public final class DropboxMacLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/Dropbox");
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION) || Files.isDirectory(FALLBACK_LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
if(Files.isDirectory(LOCATION)) {
return Stream.of(new LocationPreset("Dropbox", LOCATION));
} else if(Files.isDirectory(FALLBACK_LOCATION)) {
return Stream.of(new LocationPreset("Dropbox", FALLBACK_LOCATION));
} else {
return Stream.of();
}
}
}
@@ -1,28 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@CheckAvailability
public final class DropboxWindowsLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Dropbox");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("Dropbox", LOCATION));
}
}
@@ -1,41 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@OperatingSystem(MAC)
@CheckAvailability
public final class GoogleDriveLocationPresetsProvider implements LocationPresetsProvider {
private static final List<Path> LOCATIONS = Arrays.asList( //
LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive"), //
LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"), //
LocationPresetsProvider.resolveLocation("~/GoogleDrive"), //
LocationPresetsProvider.resolveLocation("~/Google Drive") //
);
@CheckAvailability
public static boolean isPresent() {
return LOCATIONS.stream().anyMatch(Files::isDirectory);
}
@Override
public Stream<LocationPreset> getLocations() {
return LOCATIONS.stream() //
.filter(Files::isDirectory) //
.map(location -> new LocationPreset("Google Drive", location)) //
.findFirst() //
.stream();
}
}
@@ -1,27 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
@OperatingSystem(MAC)
@CheckAvailability
public final class ICloudMacLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/Library/Mobile Documents/com~apple~CloudDocs");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
}
}
@@ -1,27 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@CheckAvailability
public final class ICloudWindowsLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/iCloudDrive");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("iCloud Drive", LOCATION));
}
}
@@ -1,30 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@OperatingSystem(MAC)
@CheckAvailability
public final class LeitzcloudLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/leitzcloud");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("leitzcloud", LOCATION));
}
}
@@ -1,9 +0,0 @@
package org.cryptomator.common.locationpresets;
import java.nio.file.Path;
public record LocationPreset(String name, Path path) {
}
@@ -1,97 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.IntegrationsLoader;
import org.cryptomator.integrations.common.OperatingSystem;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.ServiceLoader;
import java.util.stream.Stream;
public interface LocationPresetsProvider {
Logger LOG = LoggerFactory.getLogger(LocationPresetsProvider.class);
String USER_HOME = System.getProperty("user.home");
/**
* Streams account-separated location presets found by this provider
* @return Stream of LocationPresets
*/
Stream<LocationPreset> getLocations();
static Path resolveLocation(String p) {
if (p.startsWith("~/")) {
return Path.of(USER_HOME, p.substring(2));
} else {
return Path.of(p);
}
}
//copied from org.cryptomator.integrations.common.IntegrationsLoader
//TODO: delete, once migrated to integrations-api
static <T> Stream<T> loadAll(Class<T> clazz) {
return ServiceLoader.load(clazz)
.stream()
.filter(LocationPresetsProvider::isSupportedOperatingSystem)
.filter(LocationPresetsProvider::passesStaticAvailabilityCheck)
.map(ServiceLoader.Provider::get)
.peek(impl -> logServiceIsAvailable(clazz, impl.getClass()));
}
private static boolean isSupportedOperatingSystem(ServiceLoader.Provider<?> provider) {
var annotations = provider.type().getAnnotationsByType(OperatingSystem.class);
return annotations.length == 0 || Arrays.stream(annotations).anyMatch(OperatingSystem.Value::isCurrent);
}
private static boolean passesStaticAvailabilityCheck(ServiceLoader.Provider<?> provider) {
return passesStaticAvailabilityCheck(provider.type());
}
static boolean passesStaticAvailabilityCheck(Class<?> type) {
return passesAvailabilityCheck(type, null);
}
private static void logServiceIsAvailable(Class<?> apiType, Class<?> implType) {
if (LOG.isDebugEnabled()) {
LOG.debug("{}: Implementation is available: {}", apiType.getSimpleName(), implType.getName());
}
}
private static <T> boolean passesAvailabilityCheck(Class<? extends T> type, @Nullable T instance) {
if (!type.isAnnotationPresent(CheckAvailability.class)) {
return true; // if type is not annotated, skip tests
}
if (!type.getModule().isExported(type.getPackageName(), IntegrationsLoader.class.getModule())) {
LOG.error("Can't run @CheckAvailability tests for class {}. Make sure to export {} to {}!", type.getName(), type.getPackageName(), IntegrationsLoader.class.getPackageName());
return false;
}
return Arrays.stream(type.getMethods())
.filter(m -> isAvailabilityCheck(m, instance == null))
.allMatch(m -> passesAvailabilityCheck(m, instance));
}
private static boolean passesAvailabilityCheck(Method m, @Nullable Object instance) {
assert Boolean.TYPE.equals(m.getReturnType());
try {
return (boolean) m.invoke(instance);
} catch (ReflectiveOperationException e) {
LOG.warn("Failed to invoke @CheckAvailability test {}#{}", m.getDeclaringClass(), m.getName(), e);
return false;
}
}
private static boolean isAvailabilityCheck(Method m, boolean isStatic) {
return m.isAnnotationPresent(CheckAvailability.class)
&& Boolean.TYPE.equals(m.getReturnType())
&& m.getParameterCount() == 0
&& Modifier.isStatic(m.getModifiers()) == isStatic;
}
}
@@ -1,29 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@OperatingSystem(MAC)
@CheckAvailability
public final class MegaLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/MEGA");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("MEGA", LOCATION));
}
}
@@ -1,28 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.LINUX;
@OperatingSystem(LINUX)
@CheckAvailability
public final class OneDriveLinuxLocationPresetsProvider implements LocationPresetsProvider {
private static final Path LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
@CheckAvailability
public static boolean isPresent() {
return Files.isDirectory(LOCATION);
}
@Override
public Stream<LocationPreset> getLocations() {
return Stream.of(new LocationPreset("OneDrive", LOCATION));
}
}
@@ -1,44 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.OperatingSystem;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
@OperatingSystem(MAC)
public final class OneDriveMacLocationPresetsProvider implements LocationPresetsProvider {
private static final Path FALLBACK_LOCATION = LocationPresetsProvider.resolveLocation("~/OneDrive");
private static final Path PARENT_LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage");
@Override
public Stream<LocationPreset> getLocations() {
var newLocations = getNewLocations().toList();
if (newLocations.size() >= 1) {
return newLocations.stream();
} else {
return getOldLocation();
}
}
private Stream<LocationPreset> getNewLocations() {
try (var dirStream = Files.newDirectoryStream(PARENT_LOCATION, "OneDrive*")) {
return StreamSupport.stream(dirStream.spliterator(), false) //
.filter(Files::isDirectory) //
.map(p -> new LocationPreset(String.join(" - ", p.getFileName().toString().split("-")), p));
} catch (IOException e) {
return Stream.of();
}
}
private Stream<LocationPreset> getOldLocation() {
return Stream.of(new LocationPreset("OneDrive", FALLBACK_LOCATION)).filter(preset -> Files.isDirectory(preset.path()));
}
}
@@ -1,108 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.OperatingSystem;
import org.jetbrains.annotations.Blocking;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
public final class OneDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
private static final Logger LOG = LoggerFactory.getLogger(OneDriveWindowsLocationPresetsProvider.class);
private static final String REGSTR_TOKEN = "REG_SZ";
private static final String REG_ONEDRIVE_ACCOUNTS = "HKEY_CURRENT_USER\\Software\\Microsoft\\OneDrive\\Accounts\\";
@Override
public Stream<LocationPreset> getLocations() {
try {
var accountRegKeys = queryRegistry(REG_ONEDRIVE_ACCOUNTS, List.of(), l -> l.startsWith(REG_ONEDRIVE_ACCOUNTS)).toList();
var cloudLocations = new ArrayList<LocationPreset>();
for (var accountRegKey : accountRegKeys) {
var path = queryRegistry(accountRegKey, List.of("/v", "UserFolder"), l -> l.contains("UserFolder")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
.map(Path::of) //
.findFirst().orElseThrow();
var name = "OneDrive"; //we assume personal oneDrive account by default
if (!accountRegKey.endsWith("Personal")) {
name = queryRegistry(accountRegKey, List.of("/v", "DisplayName"), l -> l.contains("DisplayName")).map(result -> result.substring(result.indexOf(REGSTR_TOKEN) + REGSTR_TOKEN.length()).trim()) //
.map("OneDrive - "::concat) //
.findFirst().orElseThrow();
}
cloudLocations.add(new LocationPreset(name, path));
}
return cloudLocations.stream();
} catch (IOException | CommandFailedException | TimeoutException e) {
LOG.error("Unable to determine OneDrive location", e);
return Stream.of();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Determination of OneDrive location interrupted", e);
return Stream.of();
}
}
private Stream<String> queryRegistry(String keyname, List<String> moreArgs, Predicate<String> outputFilter) throws InterruptedException, CommandFailedException, TimeoutException, IOException {
var args = new ArrayList<String>();
args.add("reg");
args.add("query");
args.add(keyname);
args.addAll(moreArgs);
ProcessBuilder command = new ProcessBuilder(args);
Process p = command.start();
waitForSuccess(p, 3, "`reg query`");
return p.inputReader(StandardCharsets.ISO_8859_1).lines().filter(outputFilter);
}
/**
* Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}.
*
* @param process The process to wait for
* @param timeoutSeconds How long to wait (in seconds)
* @param cmdDescription A short description of the process used to generate log and exception messages
* @throws TimeoutException Thrown when the process doesn't finish in time
* @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish
* @throws CommandFailedException Thrown when the process exit code is non-zero
*/
@Blocking
private static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException {
boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
if (!exited) {
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
}
if (process.exitValue() != 0) {
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.ISO_8859_1).lines().collect(Collectors.joining("\n"));
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.ISO_8859_1).lines().collect(Collectors.joining("\n"));
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
}
}
private static class CommandFailedException extends Exception {
int exitCode;
String stdout;
String stderr;
private CommandFailedException(String cmdDescription, int exitCode, String stdout, String stderr) {
super(cmdDescription + " returned with non-zero exit code " + exitCode);
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
}
}
}
@@ -1,39 +0,0 @@
package org.cryptomator.common.locationpresets;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.OperatingSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
@OperatingSystem(WINDOWS)
@OperatingSystem(MAC)
@CheckAvailability
public final class PCloudLocationPresetsProvider implements LocationPresetsProvider {
private static final List<Path> LOCATIONS = Arrays.asList( //
LocationPresetsProvider.resolveLocation("~/pCloudDrive"), //
LocationPresetsProvider.resolveLocation("~/pCloud Drive") //
);
@CheckAvailability
public static boolean isPresent() {
return LOCATIONS.stream().anyMatch(Files::isDirectory);
}
@Override
public Stream<LocationPreset> getLocations() {
return LOCATIONS.stream() //
.filter(Files::isDirectory) //
.map(location -> new LocationPreset("pCloud", location)) //
.findFirst() //
.stream();
}
}
@@ -1,17 +0,0 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class HideawayNotDirectoryException extends IllegalMountPointException {
private final Path hideaway;
public HideawayNotDirectoryException(Path path, Path hideaway) {
super(path, "Existing hideaway (" + hideaway.toString() + ") for mountpoint is not a directory: " + path.toString());
this.hideaway = hideaway;
}
public Path getHideaway() {
return hideaway;
}
}
@@ -1,25 +1,9 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
/**
* Indicates that validation or preparation of a mountpoint failed due to a configuration error or an invalid system state.<br>
* Instances of this exception are usually caught and displayed to the user in an appropriate fashion, e.g. by {@link org.cryptomator.ui.unlock.UnlockInvalidMountPointController UnlockInvalidMountPointController.}
*/
public class IllegalMountPointException extends IllegalArgumentException {
private final Path mountpoint;
public IllegalMountPointException(Path mountpoint) {
this(mountpoint, "The provided mountpoint has a problem: " + mountpoint.toString());
}
public IllegalMountPointException(Path mountpoint, String msg) {
public IllegalMountPointException(String msg) {
super(msg);
this.mountpoint = mountpoint;
}
public Path getMountpoint() {
return mountpoint;
}
}
}
@@ -37,7 +37,7 @@ public class MountModule {
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService(), //
desiredServiceImpl -> { //
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
var targetedService = serviceFromSettings.orElse(fallbackProvider);
@@ -1,10 +0,0 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointCleanupFailedException extends IllegalMountPointException {
public MountPointCleanupFailedException(Path path) {
super(path, "Mountpoint could not be cleared: " + path.toString());
}
}
@@ -1,10 +0,0 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointInUseException extends IllegalMountPointException {
public MountPointInUseException(Path path) {
super(path);
}
}
@@ -1,10 +0,0 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointNotEmptyDirectoryException extends IllegalMountPointException {
public MountPointNotEmptyDirectoryException(Path path, String msg) {
super(path, msg);
}
}
@@ -1,14 +0,0 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointNotExistingException extends IllegalMountPointException {
public MountPointNotExistingException(Path path, String msg) {
super(path, msg);
}
public MountPointNotExistingException(Path path) {
super(path, "Mountpoint does not exist: " + path);
}
}
@@ -0,0 +1,8 @@
package org.cryptomator.common.mount;
public class MountPointNotExistsException extends IllegalMountPointException {
public MountPointNotExistsException(String msg) {
super(msg);
}
}
@@ -1,10 +1,8 @@
package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointNotSupportedException extends IllegalMountPointException {
public MountPointNotSupportedException(Path path, String msg) {
super(path, msg);
public MountPointNotSupportedException(String msg) {
super(msg);
}
}
@@ -0,0 +1,12 @@
package org.cryptomator.common.mount;
public class MountPointPreparationException extends RuntimeException {
public MountPointPreparationException(String msg) {
super(msg);
}
public MountPointPreparationException(Throwable cause) {
super(cause);
}
}
@@ -1,16 +1,17 @@
package org.cryptomator.common.mount;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public final class MountWithinParentUtil {
@@ -21,33 +22,31 @@ public final class MountWithinParentUtil {
private MountWithinParentUtil() {}
static void prepareParentNoMountPoint(Path mountPoint) throws IllegalMountPointException, IOException {
static void prepareParentNoMountPoint(Path mountPoint) throws MountPointPreparationException {
Path hideaway = getHideaway(mountPoint);
var mpState = getMountPointState(mountPoint);
var mpExists = Files.exists(mountPoint, LinkOption.NOFOLLOW_LINKS);
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
if (mpState == MountPointState.BROKEN_JUNCTION) {
LOG.info("Mountpoint \"{}\" is still a junction. Deleting it.", mountPoint);
Files.delete(mountPoint); //Throws if mountPoint is also a non-empty folder
mpState = MountPointState.NOT_EXISTING;
}
if (mpState == MountPointState.NOT_EXISTING && !hideExists) { //neither mountpoint nor hideaway exist
throw new MountPointNotExistingException(mountPoint);
} else if (mpState == MountPointState.NOT_EXISTING) { //only hideaway exists
checkIsHideawayDirectory(mountPoint, hideaway);
//TODO: possible improvement by just deleting an _empty_ hideaway
if (mpExists && hideExists) { //both resources exist (whatever type)
throw new MountPointPreparationException(new FileAlreadyExistsException(hideaway.toString()));
} else if (!mpExists && !hideExists) { //neither mountpoint nor hideaway exist
throw new MountPointPreparationException(new NoSuchFileException(mountPoint.toString()));
} else if (!mpExists) { //only hideaway exists
checkIsDirectory(hideaway);
LOG.info("Mountpoint {} seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
}
} else {
assert mpState == MountPointState.EMPTY_DIR;
try {
if (hideExists) { //... with hideaway
removeResidualHideaway(mountPoint, hideaway);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
}
} catch (IOException e) {
throw new MountPointPreparationException(e);
}
} else { //only mountpoint exists
try {
checkIsDirectory(mountPoint);
checkIsEmpty(mountPoint);
//... (now) without hideaway
Files.move(mountPoint, hideaway);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
@@ -55,66 +54,30 @@ public final class MountWithinParentUtil {
int attempts = 0;
while (!Files.notExists(mountPoint)) {
if (attempts >= 10) {
throw new MountPointCleanupFailedException(mountPoint);
throw new MountPointPreparationException("Path " + mountPoint + " could not be cleared");
}
Thread.sleep(1000);
attempts++;
}
} catch (IOException e) {
throw new MountPointPreparationException(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
throw new MountPointPreparationException(e);
}
}
}
@VisibleForTesting
static MountPointState getMountPointState(Path path) throws IOException, IllegalMountPointException {
if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
return MountPointState.NOT_EXISTING;
}
if (!Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isOther()) {
checkIsMountPointDirectory(path);
checkIsMountPointEmpty(path);
return MountPointState.EMPTY_DIR;
}
if (Files.exists(path /* FOLLOW_LINKS */)) { //Both junction and target exist
throw new MountPointInUseException(path);
}
return MountPointState.BROKEN_JUNCTION;
}
@VisibleForTesting
enum MountPointState {
NOT_EXISTING,
EMPTY_DIR,
BROKEN_JUNCTION;
}
@VisibleForTesting
static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException {
checkIsHideawayDirectory(mountPoint, hideaway);
Files.delete(hideaway); //Fails if not empty
}
static void cleanup(Path mountPoint) {
Path hideaway = getHideaway(mountPoint);
try {
waitForMountpointRestoration(mountPoint);
if (Files.notExists(hideaway, LinkOption.NOFOLLOW_LINKS)) {
LOG.error("Unable to restore hidden directory to mountpoint \"{}\": Directory does not exist.", mountPoint);
return;
}
Files.move(hideaway, mountPoint);
if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false);
}
} catch (IOException e) {
LOG.error("Unable to restore hidden directory to mountpoint \"{}\".", mountPoint, e);
LOG.error("Unable to restore hidden directory to mountpoint {}.", mountPoint, e);
}
}
@@ -136,27 +99,21 @@ public final class MountWithinParentUtil {
}
}
private static void checkIsMountPointDirectory(Path toCheck) throws IllegalMountPointException {
private static void checkIsDirectory(Path toCheck) throws MountPointPreparationException {
if (!Files.isDirectory(toCheck, LinkOption.NOFOLLOW_LINKS)) {
throw new MountPointNotEmptyDirectoryException(toCheck, "Mountpoint is not a directory: " + toCheck);
throw new MountPointPreparationException(new NotDirectoryException(toCheck.toString()));
}
}
private static void checkIsHideawayDirectory(Path mountPoint, Path hideawayToCheck) {
if (!Files.isDirectory(hideawayToCheck, LinkOption.NOFOLLOW_LINKS)) {
throw new HideawayNotDirectoryException(mountPoint, hideawayToCheck);
}
}
private static void checkIsMountPointEmpty(Path toCheck) throws IllegalMountPointException, IOException {
private static void checkIsEmpty(Path toCheck) throws MountPointPreparationException, IOException {
try (var dirStream = Files.list(toCheck)) {
if (dirStream.findFirst().isPresent()) {
throw new MountPointNotEmptyDirectoryException(toCheck, "Mountpoint directory is not empty: " + toCheck);
throw new MountPointPreparationException(new DirectoryNotEmptyException(toCheck.toString()));
}
}
}
@VisibleForTesting
//visible for testing
static Path getHideaway(Path mountPoint) {
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
}
@@ -54,19 +54,19 @@ public class Mounter {
switch (capability) {
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
case LOOPBACK_PORT ->
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
case MOUNT_FLAGS -> {
var mountFlags = vaultSettings.mountFlags.get();
var mountFlags = vaultSettings.mountFlags().get();
if (mountFlags == null || mountFlags.isBlank()) {
builder.setMountFlags(service.getDefaultMountFlags());
} else {
builder.setMountFlags(mountFlags);
}
}
case VOLUME_ID -> builder.setVolumeId(vaultSettings.id);
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName.get());
case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId());
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
}
}
@@ -75,7 +75,7 @@ public class Mounter {
private Runnable prepareMountPoint() throws IOException {
Runnable cleanup = () -> {};
var userChosenMountPoint = vaultSettings.mountPoint.get();
var userChosenMountPoint = vaultSettings.getMountPoint();
var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
@@ -91,17 +91,13 @@ public class Mounter {
Files.createDirectories(defaultMountPointBase);
builder.setMountpoint(defaultMountPointBase);
} else if (canMountToDir) {
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName.get());
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
Files.createDirectories(mountPoint);
builder.setMountpoint(mountPoint);
}
} else {
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
if (mpIsDriveLetter) {
if (driveLetters.getOccupied().contains(userChosenMountPoint)) {
throw new MountPointInUseException(userChosenMountPoint);
}
} else if (canMountToParent && !canMountToDir) {
if (!mpIsDriveLetter && canMountToParent && !canMountToDir) {
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
cleanup = () -> {
MountWithinParentUtil.cleanup(userChosenMountPoint);
@@ -115,13 +111,13 @@ public class Mounter {
|| (!canMountToParent && !mpIsDriveLetter) //
|| (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter);
if (configNotSupported) {
throw new MountPointNotSupportedException(userChosenMountPoint, e.getMessage());
throw new MountPointNotSupportedException(e.getMessage());
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
//mountpoint must exist
throw new MountPointNotExistingException(userChosenMountPoint, e.getMessage());
throw new MountPointNotExistsException(e.getMessage());
} else {
//TODO: add specific exception for !canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)
throw new IllegalMountPointException(userChosenMountPoint, e.getMessage());
throw new IllegalMountPointException(e.getMessage());
}
}
}
@@ -10,8 +10,6 @@ package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
@@ -29,86 +27,59 @@ import java.util.function.Consumer;
public class Settings {
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
static final boolean DEFAULT_START_HIDDEN = false;
static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
static final boolean DEFAULT_USE_KEYCHAIN = true;
static final int DEFAULT_PORT = 42427;
static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
static final boolean DEFAULT_DEBUG_MODE = false;
static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
public static final boolean DEFAULT_START_HIDDEN = false;
public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
public static final boolean DEFAULT_USE_KEYCHAIN = true;
public static final int DEFAULT_PORT = 42427;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final boolean DEFAULT_DEBUG_MODE = false;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually
static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01";
public static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
public static final String DEFAULT_LANGUAGE = null;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
private final BooleanProperty askedForUpdateCheck = new SimpleBooleanProperty(DEFAULT_ASKED_FOR_UPDATE_CHECK);
private final BooleanProperty checkForUpdates = new SimpleBooleanProperty(DEFAULT_CHECK_FOR_UPDATES);
private final BooleanProperty startHidden = new SimpleBooleanProperty(DEFAULT_START_HIDDEN);
private final BooleanProperty autoCloseVaults = new SimpleBooleanProperty(DEFAULT_AUTO_CLOSE_VAULTS);
private final BooleanProperty useKeychain = new SimpleBooleanProperty(DEFAULT_USE_KEYCHAIN);
private final IntegerProperty port = new SimpleIntegerProperty(DEFAULT_PORT);
private final IntegerProperty numTrayNotifications = new SimpleIntegerProperty(DEFAULT_NUM_TRAY_NOTIFICATIONS);
private final BooleanProperty debugMode = new SimpleBooleanProperty(DEFAULT_DEBUG_MODE);
private final ObjectProperty<UiTheme> theme = new SimpleObjectProperty<>(DEFAULT_THEME);
private final ObjectProperty<String> keychainProvider = new SimpleObjectProperty<>(DEFAULT_KEYCHAIN_PROVIDER);
private final ObjectProperty<NodeOrientation> userInterfaceOrientation = new SimpleObjectProperty<>(DEFAULT_USER_INTERFACE_ORIENTATION);
private final StringProperty licenseKey = new SimpleStringProperty(DEFAULT_LICENSE_KEY);
private final BooleanProperty showMinimizeButton = new SimpleBooleanProperty(DEFAULT_SHOW_MINIMIZE_BUTTON);
private final BooleanProperty showTrayIcon;
private final IntegerProperty windowXPosition = new SimpleIntegerProperty();
private final IntegerProperty windowYPosition = new SimpleIntegerProperty();
private final IntegerProperty windowWidth = new SimpleIntegerProperty();
private final IntegerProperty windowHeight = new SimpleIntegerProperty();
private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
private final StringProperty mountService = new SimpleStringProperty();
public final ObservableList<VaultSettings> directories;
public final BooleanProperty askedForUpdateCheck;
public final BooleanProperty checkForUpdates;
public final BooleanProperty startHidden;
public final BooleanProperty autoCloseVaults;
public final BooleanProperty useKeychain;
public final IntegerProperty port;
public final IntegerProperty numTrayNotifications;
public final BooleanProperty debugMode;
public final ObjectProperty<UiTheme> theme;
public final StringProperty keychainProvider;
public final ObjectProperty<NodeOrientation> userInterfaceOrientation;
public final StringProperty licenseKey;
public final BooleanProperty showMinimizeButton;
public final BooleanProperty showTrayIcon;
public final IntegerProperty windowXPosition;
public final IntegerProperty windowYPosition;
public final IntegerProperty windowWidth;
public final IntegerProperty windowHeight;
public final StringProperty language;
public final StringProperty mountService;
public final StringProperty lastUpdateCheck;
private Consumer<Settings> saveCmd;
public static Settings create(Environment env) {
var defaults = new SettingsJson();
defaults.showTrayIcon = env.showTrayIcon();
return new Settings(defaults);
}
/**
* Recreate settings from json
*
* @param json The parsed settings.json
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings(SettingsJson json) {
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
this.askedForUpdateCheck = new SimpleBooleanProperty(this, "askedForUpdateCheck", json.askedForUpdateCheck);
this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
this.useKeychain = new SimpleBooleanProperty(this, "useKeychain", json.useKeychain);
this.port = new SimpleIntegerProperty(this, "webDavPort", json.port);
this.numTrayNotifications = new SimpleIntegerProperty(this, "numTrayNotifications", json.numTrayNotifications);
this.debugMode = new SimpleBooleanProperty(this, "debugMode", json.debugMode);
this.theme = new SimpleObjectProperty<>(this, "theme", json.theme);
this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider);
this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT));
this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey);
this.showMinimizeButton = new SimpleBooleanProperty(this, "showMinimizeButton", json.showMinimizeButton);
this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon);
this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition);
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
this.language = new SimpleStringProperty(this, "language", json.language);
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
migrateLegacySettings(json);
Settings(Environment env) {
this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
directories.addListener(this::somethingChanged);
askedForUpdateCheck.addListener(this::somethingChanged);
@@ -129,77 +100,11 @@ public class Settings {
windowYPosition.addListener(this::somethingChanged);
windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged);
lastUpdateCheck.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
private void migrateLegacySettings(SettingsJson json) {
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
this.mountService.set(switch (json.preferredVolumeImpl) {
case "Dokany" -> "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
case "FUSE" -> {
if (SystemUtils.IS_OS_WINDOWS) {
yield "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
} else if (SystemUtils.IS_OS_MAC) {
yield "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
} else {
yield "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
}
}
default -> {
if (SystemUtils.IS_OS_WINDOWS) {
yield "org.cryptomator.frontend.webdav.mount.WindowsMounter";
} else if (SystemUtils.IS_OS_MAC) {
yield "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
} else {
yield "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
}
}
});
}
}
SettingsJson serialized() {
var json = new SettingsJson();
json.directories = directories.stream().map(VaultSettings::serialized).toList();
json.askedForUpdateCheck = askedForUpdateCheck.get();
json.checkForUpdatesEnabled = checkForUpdates.get();
json.startHidden = startHidden.get();
json.autoCloseVaults = autoCloseVaults.get();
json.useKeychain = useKeychain.get();
json.port = port.get();
json.numTrayNotifications = numTrayNotifications.get();
json.debugMode = debugMode.get();
json.theme = theme.get();
json.keychainProvider = keychainProvider.get();
json.uiOrientation = userInterfaceOrientation.get().name();
json.licenseKey = licenseKey.get();
json.showMinimizeButton = showMinimizeButton.get();
json.showTrayIcon = showTrayIcon.get();
json.windowXPosition = windowXPosition.get();
json.windowYPosition = windowYPosition.get();
json.windowWidth = windowWidth.get();
json.windowHeight = windowHeight.get();
json.language = language.get();
json.mountService = mountService.get();
json.lastUpdateCheck = lastUpdateCheck.get();
return json;
}
private <E extends Enum<E>> E parseEnum(String value, Class<E> clazz, E defaultValue) {
try {
return Enum.valueOf(clazz, value.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("No value {}.{}. Defaulting to {}.", clazz.getSimpleName(), value, defaultValue);
return defaultValue;
}
}
// TODO rename to setChangeListener
void setSaveCmd(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
@@ -214,4 +119,90 @@ public class Settings {
}
}
/* Getter/Setter */
public ObservableList<VaultSettings> getDirectories() {
return directories;
}
public BooleanProperty askedForUpdateCheck() {
return askedForUpdateCheck;
}
public BooleanProperty checkForUpdates() {
return checkForUpdates;
}
public BooleanProperty startHidden() {
return startHidden;
}
public BooleanProperty autoCloseVaults() {
return autoCloseVaults;
}
public BooleanProperty useKeychain() { return useKeychain; }
public IntegerProperty port() {
return port;
}
public IntegerProperty numTrayNotifications() {
return numTrayNotifications;
}
public BooleanProperty debugMode() {
return debugMode;
}
public StringProperty mountService() {
return mountService;
}
public ObjectProperty<UiTheme> theme() {
return theme;
}
public ObjectProperty<String> keychainProvider() {return keychainProvider;}
public ObjectProperty<NodeOrientation> userInterfaceOrientation() {
return userInterfaceOrientation;
}
public StringProperty licenseKey() {
return licenseKey;
}
public BooleanProperty showMinimizeButton() {
return showMinimizeButton;
}
public BooleanProperty showTrayIcon() {
return showTrayIcon;
}
public IntegerProperty windowXPositionProperty() {
return windowXPosition;
}
public IntegerProperty windowYPositionProperty() {
return windowYPosition;
}
public IntegerProperty windowWidthProperty() {
return windowWidth;
}
public IntegerProperty windowHeightProperty() {
return windowHeight;
}
public ObjectProperty<String> displayConfigurationProperty() {
return displayConfiguration;
}
public StringProperty languageProperty() {
return language;
}
}
@@ -1,86 +0,0 @@
package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class SettingsJson {
@JsonProperty("directories")
List<VaultSettingsJson> directories = List.of();
@JsonProperty("writtenByVersion")
String writtenByVersion;
@JsonProperty("askedForUpdateCheck")
boolean askedForUpdateCheck = Settings.DEFAULT_ASKED_FOR_UPDATE_CHECK;
@JsonProperty("autoCloseVaults")
boolean autoCloseVaults = Settings.DEFAULT_AUTO_CLOSE_VAULTS;
@JsonProperty("checkForUpdatesEnabled")
boolean checkForUpdatesEnabled = Settings.DEFAULT_CHECK_FOR_UPDATES;
@JsonProperty("debugMode")
boolean debugMode = Settings.DEFAULT_DEBUG_MODE;
@JsonProperty("theme")
UiTheme theme = Settings.DEFAULT_THEME;
@JsonProperty("keychainProvider")
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
@JsonProperty("language")
String language;
@JsonProperty("licenseKey")
String licenseKey;
@JsonProperty("mountService")
String mountService;
@JsonProperty("numTrayNotifications")
int numTrayNotifications = Settings.DEFAULT_NUM_TRAY_NOTIFICATIONS;
@JsonProperty("port")
int port = Settings.DEFAULT_PORT;
@JsonProperty("showMinimizeButton")
boolean showMinimizeButton = Settings.DEFAULT_SHOW_MINIMIZE_BUTTON;
@JsonProperty("showTrayIcon")
boolean showTrayIcon;
@JsonProperty("startHidden")
boolean startHidden = Settings.DEFAULT_START_HIDDEN;
@JsonProperty("uiOrientation")
String uiOrientation = Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
@JsonProperty("useKeychain")
boolean useKeychain = Settings.DEFAULT_USE_KEYCHAIN;
@JsonProperty("windowHeight")
int windowHeight;
@JsonProperty("windowWidth")
int windowWidth;
@JsonProperty("windowXPosition")
int windowXPosition;
@JsonProperty("windowYPosition")
int windowYPosition;
@Deprecated(since = "1.7.0")
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String preferredVolumeImpl;
@JsonProperty("lastUpdateCheck")
String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;
}
@@ -0,0 +1,183 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.geometry.NodeOrientation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class SettingsJsonAdapter extends TypeAdapter<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(SettingsJsonAdapter.class);
private final VaultSettingsJsonAdapter vaultSettingsJsonAdapter = new VaultSettingsJsonAdapter();
private final Environment env;
@Inject
public SettingsJsonAdapter(Environment env) {
this.env = env;
}
@Override
public void write(JsonWriter out, Settings value) throws IOException {
out.beginObject();
out.name("writtenByVersion").value(env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse(""));
out.name("directories");
writeVaultSettingsArray(out, value.getDirectories());
out.name("askedForUpdateCheck").value(value.askedForUpdateCheck().get());
out.name("autoCloseVaults").value(value.autoCloseVaults().get());
out.name("checkForUpdatesEnabled").value(value.checkForUpdates().get());
out.name("debugMode").value(value.debugMode().get());
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
out.name("keychainProvider").value(value.keychainProvider().get());
out.name("language").value((value.languageProperty().get()));
out.name("licenseKey").value(value.licenseKey().get());
out.name("mountService").value(value.mountService().get());
out.name("numTrayNotifications").value(value.numTrayNotifications().get());
out.name("port").value(value.port().get());
out.name("showMinimizeButton").value(value.showMinimizeButton().get());
out.name("showTrayIcon").value(value.showTrayIcon().get());
out.name("startHidden").value(value.startHidden().get());
out.name("theme").value(value.theme().get().name());
out.name("uiOrientation").value(value.userInterfaceOrientation().get().name());
out.name("useKeychain").value(value.useKeychain().get());
out.name("windowHeight").value((value.windowHeightProperty().get()));
out.name("windowWidth").value((value.windowWidthProperty().get()));
out.name("windowXPosition").value((value.windowXPositionProperty().get()));
out.name("windowYPosition").value((value.windowYPositionProperty().get()));
out.endObject();
}
private void writeVaultSettingsArray(JsonWriter out, Iterable<VaultSettings> vaultSettings) throws IOException {
out.beginArray();
for (VaultSettings value : vaultSettings) {
vaultSettingsJsonAdapter.write(out, value);
}
out.endArray();
}
@Override
public Settings read(JsonReader in) throws IOException {
Settings settings = new Settings(env);
//1.6.x legacy
String volumeImpl = null;
//legacy end
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "writtenByVersion" -> in.skipValue(); //noop
case "directories" -> settings.getDirectories().addAll(readVaultSettingsArray(in));
case "askedForUpdateCheck" -> settings.askedForUpdateCheck().set(in.nextBoolean());
case "autoCloseVaults" -> settings.autoCloseVaults().set(in.nextBoolean());
case "checkForUpdatesEnabled" -> settings.checkForUpdates().set(in.nextBoolean());
case "debugMode" -> settings.debugMode().set(in.nextBoolean());
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
case "keychainProvider" -> settings.keychainProvider().set(in.nextString());
case "language" -> settings.languageProperty().set(in.nextString());
case "licenseKey" -> settings.licenseKey().set(in.nextString());
case "mountService" -> {
var token = in.peek();
if (JsonToken.STRING == token) {
settings.mountService().set(in.nextString());
}
}
case "numTrayNotifications" -> settings.numTrayNotifications().set(in.nextInt());
case "port" -> settings.port().set(in.nextInt());
case "showMinimizeButton" -> settings.showMinimizeButton().set(in.nextBoolean());
case "showTrayIcon" -> settings.showTrayIcon().set(in.nextBoolean());
case "startHidden" -> settings.startHidden().set(in.nextBoolean());
case "theme" -> settings.theme().set(parseUiTheme(in.nextString()));
case "uiOrientation" -> settings.userInterfaceOrientation().set(parseUiOrientation(in.nextString()));
case "useKeychain" -> settings.useKeychain().set(in.nextBoolean());
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
case "windowXPosition" -> settings.windowXPositionProperty().set(in.nextInt());
case "windowYPosition" -> settings.windowYPositionProperty().set(in.nextInt());
//1.6.x legacy
case "preferredVolumeImpl" -> volumeImpl = in.nextString();
//legacy end
default -> {
LOG.warn("Unsupported vault setting found in JSON: {}", name);
in.skipValue();
}
}
}
in.endObject();
//1.6.x legacy
if (volumeImpl != null) {
settings.mountService().set(convertLegacyVolumeImplToMountService(volumeImpl));
}
//legacy end
return settings;
}
private String convertLegacyVolumeImplToMountService(String volumeImpl) {
if (volumeImpl.equals("Dokany")) {
return "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
} else if (volumeImpl.equals("FUSE")) {
if (SystemUtils.IS_OS_WINDOWS) {
return "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
} else if (SystemUtils.IS_OS_MAC) {
return "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
} else {
return "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
}
} else {
if (SystemUtils.IS_OS_WINDOWS) {
return "org.cryptomator.frontend.webdav.mount.WindowsMounter";
} else if (SystemUtils.IS_OS_MAC) {
return "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
} else {
return "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
}
}
}
private UiTheme parseUiTheme(String uiThemeName) {
try {
return UiTheme.valueOf(uiThemeName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid ui theme {}. Defaulting to {}.", uiThemeName, Settings.DEFAULT_THEME);
return Settings.DEFAULT_THEME;
}
}
private NodeOrientation parseUiOrientation(String uiOrientationName) {
try {
return NodeOrientation.valueOf(uiOrientationName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid ui orientation {}. Defaulting to {}.", uiOrientationName, Settings.DEFAULT_USER_INTERFACE_ORIENTATION);
return Settings.DEFAULT_USER_INTERFACE_ORIENTATION;
}
}
private List<VaultSettings> readVaultSettingsArray(JsonReader in) throws IOException {
List<VaultSettings> result = new ArrayList<>();
in.beginArray();
while (!JsonToken.END_ARRAY.equals(in.peek())) {
result.add(vaultSettingsJsonAdapter.read(in));
}
in.endArray();
return result;
}
}
@@ -8,9 +8,12 @@
*******************************************************************************/
package org.cryptomator.common.settings;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Suppliers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,7 +22,12 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -36,7 +44,6 @@ import java.util.stream.Stream;
@Singleton
public class SettingsProvider implements Supplier<Settings> {
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final long SAVE_DELAY_MS = 1000;
@@ -44,11 +51,16 @@ public class SettingsProvider implements Supplier<Settings> {
private final Supplier<Settings> settings = Suppliers.memoize(this::load);
private final Environment env;
private final ScheduledExecutorService scheduler;
private final Gson gson;
@Inject
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
this.env = env;
this.scheduler = scheduler;
this.gson = new GsonBuilder() //
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
.create();
}
@Override
@@ -57,25 +69,28 @@ public class SettingsProvider implements Supplier<Settings> {
}
private Settings load() {
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElseGet(() -> Settings.create(env));
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElse(new Settings(env));
settings.setSaveCmd(this::scheduleSave);
return settings;
}
private Stream<Settings> tryLoad(Path path) {
LOG.debug("Attempting to load settings from {}", path);
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
var json = JSON.reader().readValue(in, SettingsJson.class);
LOG.info("Settings loaded from {}", path);
var settings = new Settings(json);
return Stream.of(settings);
} catch (JacksonException e) {
LOG.warn("Failed to parse json file {}", path, e);
return Stream.empty();
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
JsonElement json = JsonParser.parseReader(reader);
if (json.isJsonObject()) {
Settings settings = gson.fromJson(json, Settings.class);
LOG.info("Settings loaded from {}", path);
return Stream.of(settings);
} else {
LOG.warn("Invalid json file {}", path);
return Stream.empty();
}
} catch (NoSuchFileException e) {
return Stream.empty();
} catch (IOException e) {
LOG.warn("Failed to load json file {}", path, e);
} catch (IOException | JsonParseException e) {
LOG.warn("Exception while loading settings from " + path, e);
return Stream.empty();
}
}
@@ -101,14 +116,13 @@ public class SettingsProvider implements Supplier<Settings> {
try {
Files.createDirectories(settingsPath.getParent());
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
var jsonObj = settings.serialized();
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
gson.toJson(settings, writer);
}
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
LOG.info("Settings saved to {}", settingsPath);
} catch (IOException e) {
} catch (IOException | JsonParseException e) {
LOG.error("Failed to save settings.", e);
}
}
@@ -1,12 +1,9 @@
package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.SystemUtils;
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum UiTheme {
@JsonEnumDefaultValue LIGHT("preferences.interface.theme.light"), //
LIGHT("preferences.interface.theme.light"), //
DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic");
@@ -6,10 +6,7 @@
package org.cryptomator.common.settings;
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
@@ -23,7 +20,6 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Random;
@@ -32,45 +28,33 @@ import java.util.Random;
*/
public class VaultSettings {
static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
static final boolean DEFAULT_USES_READONLY_MODE = false;
static final String DEFAULT_MOUNT_FLAGS = ""; // TODO: remove empty default mount flags and let this property be null if not used
static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
public static final boolean DEFAULT_USES_READONLY_MODE = false;
public static final String DEFAULT_MOUNT_FLAGS = "";
public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
private static final Random RNG = new Random();
public final String id;
public final ObjectProperty<Path> path;
public final StringProperty displayName;
public final BooleanProperty unlockAfterStartup;
public final BooleanProperty revealAfterMount;
public final BooleanProperty usesReadOnlyMode;
public final StringProperty mountFlags;
public final IntegerProperty maxCleartextFilenameLength;
public final ObjectProperty<WhenUnlocked> actionAfterUnlock;
public final BooleanProperty autoLockWhenIdle;
public final IntegerProperty autoLockIdleSeconds;
public final ObjectProperty<Path> mountPoint;
public final StringExpression mountName;
private final String id;
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
private final StringProperty displayName = new SimpleStringProperty();
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS); //TODO: remove empty default mount flags and let this property be null if not used
private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
private final StringExpression mountName;
private final ObjectProperty<Path> mountPoint = new SimpleObjectProperty<>();
VaultSettings(VaultSettingsJson json) {
this.id = json.id;
this.path = new SimpleObjectProperty<>(this, "path", json.path == null ? null : Paths.get(json.path));
this.displayName = new SimpleStringProperty(this, "displayName", json.displayName);
this.unlockAfterStartup = new SimpleBooleanProperty(this, "unlockAfterStartup", json.unlockAfterStartup);
this.revealAfterMount = new SimpleBooleanProperty(this, "revealAfterMount", json.revealAfterMount);
this.usesReadOnlyMode = new SimpleBooleanProperty(this, "usesReadOnlyMode", json.usesReadOnlyMode);
this.mountFlags = new SimpleStringProperty(this, "mountFlags", json.mountFlags);
this.maxCleartextFilenameLength = new SimpleIntegerProperty(this, "maxCleartextFilenameLength", json.maxCleartextFilenameLength);
this.actionAfterUnlock = new SimpleObjectProperty<>(this, "actionAfterUnlock", json.actionAfterUnlock);
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
public VaultSettings(String id) {
this.id = Objects.requireNonNull(id);
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name;
if (displayName.isEmpty().get()) {
@@ -80,18 +64,6 @@ public class VaultSettings {
}
return normalizeDisplayName(name);
}, displayName, path));
migrateLegacySettings(json);
}
@SuppressWarnings("deprecation")
private void migrateLegacySettings(VaultSettingsJson json) {
// implicit migration of 1.6.x legacy setting "customMountPath" / "winDriveLetter":
if (json.useCustomMountPath && !Strings.isNullOrEmpty(json.customMountPath)) {
this.mountPoint.set(Path.of(json.customMountPath));
} else if (!Strings.isNullOrEmpty(json.winDriveLetter)) {
this.mountPoint.set(Path.of(json.winDriveLetter + ":\\"));
}
}
Observable[] observables() {
@@ -99,9 +71,7 @@ public class VaultSettings {
}
public static VaultSettings withRandomId() {
var defaults = new VaultSettingsJson();
defaults.id = generateId();
return new VaultSettings(defaults);
return new VaultSettings(generateId());
}
private static String generateId() {
@@ -110,24 +80,7 @@ public class VaultSettings {
return BaseEncoding.base64Url().encode(randomBytes);
}
VaultSettingsJson serialized() {
var json = new VaultSettingsJson();
json.id = id;
json.path = path.map(Path::toString).getValue();
json.displayName = displayName.get();
json.unlockAfterStartup = unlockAfterStartup.get();
json.revealAfterMount = revealAfterMount.get();
json.usesReadOnlyMode = usesReadOnlyMode.get();
json.mountFlags = mountFlags.get();
json.maxCleartextFilenameLength = maxCleartextFilenameLength.get();
json.actionAfterUnlock = actionAfterUnlock.get();
json.autoLockWhenIdle = autoLockWhenIdle.get();
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
json.mountPoint = mountPoint.map(Path::toString).getValue();
return json;
}
@VisibleForTesting
//visible for testing
static String normalizeDisplayName(String original) {
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
return "_";
@@ -140,6 +93,68 @@ public class VaultSettings {
return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_');
}
/* Getter/Setter */
public String getId() {
return id;
}
public ObjectProperty<Path> path() {
return path;
}
public StringProperty displayName() {
return displayName;
}
public StringExpression mountName() {
return mountName;
}
public BooleanProperty unlockAfterStartup() {
return unlockAfterStartup;
}
public BooleanProperty revealAfterMount() {
return revealAfterMount;
}
public Path getMountPoint() {
return mountPoint.get();
}
public ObjectProperty<Path> mountPoint() {
return mountPoint;
}
public BooleanProperty usesReadOnlyMode() {
return usesReadOnlyMode;
}
public StringProperty mountFlags() {
return mountFlags;
}
public IntegerProperty maxCleartextFilenameLength() {
return maxCleartextFilenameLength;
}
public ObjectProperty<WhenUnlocked> actionAfterUnlock() {
return actionAfterUnlock;
}
public WhenUnlocked getActionAfterUnlock() {
return actionAfterUnlock.get();
}
public BooleanProperty autoLockWhenIdle() {
return autoLockWhenIdle;
}
public IntegerProperty autoLockIdleSeconds() {
return autoLockIdleSeconds;
}
/* Hashcode/Equals */
@Override
@@ -1,62 +0,0 @@
package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class VaultSettingsJson {
@JsonProperty(value = "id", required = true)
String id;
@JsonProperty(value = "path")
String path;
@JsonProperty("displayName")
String displayName;
@JsonProperty("unlockAfterStartup")
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
@JsonProperty("revealAfterMount")
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
@JsonProperty("mountPoint")
String mountPoint;
@JsonProperty("usesReadOnlyMode")
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
@JsonProperty("mountFlags")
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
@JsonProperty("maxCleartextFilenameLength")
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
@JsonProperty("actionAfterUnlock")
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
@JsonProperty("autoLockWhenIdle")
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
@JsonProperty("autoLockIdleSeconds")
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
@Deprecated(since = "1.7.0")
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
String winDriveLetter;
@Deprecated(since = "1.7.0")
@JsonProperty(value = "useCustomMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
@JsonAlias("usesIndividualMountPath")
boolean useCustomMountPath;
@Deprecated(since = "1.7.0")
@JsonProperty(value = "customMountPath", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
@JsonAlias("individualMountPath")
String customMountPath;
}
@@ -0,0 +1,142 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.common.settings;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
class VaultSettingsJsonAdapter {
private static final Logger LOG = LoggerFactory.getLogger(VaultSettingsJsonAdapter.class);
public void write(JsonWriter out, VaultSettings value) throws IOException {
out.beginObject();
out.name("id").value(value.getId());
out.name("path").value(value.path().get().toString());
out.name("displayName").value(value.displayName().get());
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
out.name("revealAfterMount").value(value.revealAfterMount().get());
var mountPoint = value.mountPoint().get();
out.name("mountPoint").value(mountPoint != null ? mountPoint.toAbsolutePath().toString() : null);
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
out.name("mountFlags").value(value.mountFlags().get());
out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
out.name("actionAfterUnlock").value(value.actionAfterUnlock().get().name());
out.name("autoLockWhenIdle").value(value.autoLockWhenIdle().get());
out.name("autoLockIdleSeconds").value(value.autoLockIdleSeconds().get());
out.endObject();
}
public VaultSettings read(JsonReader in) throws IOException {
String id = null;
String path = null;
String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
String displayName = null;
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
Path mountPoint = null;
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
//legacy from 1.6.x
boolean useCustomMountPath = false;
String customMountPath = "";
String winDriveLetter = "";
//legacy end
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
switch (name) {
case "id" -> id = in.nextString();
case "path" -> path = in.nextString();
case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
case "displayName" -> displayName = in.nextString();
case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
case "mountFlags" -> mountFlags = in.nextString();
case "mountPoint" -> {
if (JsonToken.NULL == in.peek()) {
in.nextNull();
} else {
mountPoint = parseMountPoint(in.nextString());
}
}
case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
case "autoLockIdleSeconds" -> autoLockIdleSeconds = in.nextInt();
//legacy from 1.6.x
case "winDriveLetter" -> winDriveLetter = in.nextString();
case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
//legacy end
default -> {
LOG.warn("Unsupported vault setting found in JSON: {}", name);
in.skipValue();
}
}
}
in.endObject();
VaultSettings vaultSettings = (id == null) ? VaultSettings.withRandomId() : new VaultSettings(id);
if (displayName != null) { //see https://github.com/cryptomator/cryptomator/pull/1318
vaultSettings.displayName().set(displayName);
} else {
vaultSettings.displayName().set(mountName);
}
vaultSettings.path().set(Paths.get(path));
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
vaultSettings.revealAfterMount().set(revealAfterMount);
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
vaultSettings.mountFlags().set(mountFlags);
vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
vaultSettings.mountPoint().set(mountPoint);
//legacy from 1.6.x
if(useCustomMountPath && !customMountPath.isBlank()) {
vaultSettings.mountPoint().set(parseMountPoint(customMountPath));
} else if(!winDriveLetter.isBlank() ) {
vaultSettings.mountPoint().set(parseMountPoint(winDriveLetter+":\\"));
}
//legacy end
return vaultSettings;
}
private Path parseMountPoint(String mountPoint) {
try {
return Path.of(mountPoint);
} catch (InvalidPathException e) {
LOG.warn("Invalid string as mount point. Defaulting to null.");
return null;
}
}
private WhenUnlocked parseActionAfterUnlock(String actionAfterUnlockName) {
try {
return WhenUnlocked.valueOf(actionAfterUnlockName.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("Invalid action after unlock {}. Defaulting to {}.", actionAfterUnlockName, VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK);
return VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
}
}
}
@@ -1,13 +1,9 @@
package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum WhenUnlocked {
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
@JsonEnumDefaultValue ASK("vaultOptions.general.actionAfterUnlock.ask");
ASK("vaultOptions.general.actionAfterUnlock.ask");
private String displayName;
@@ -50,8 +50,8 @@ public class AutoLocker {
private boolean exceedsIdleTime(Vault vault) {
assert vault.isUnlocked();
if (vault.getVaultSettings().autoLockWhenIdle.get()) {
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds.get();
if (vault.getVaultSettings().autoLockWhenIdle().get()) {
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
return deadline.isBefore(Instant.now());
} else {
@@ -70,7 +70,7 @@ public class Vault {
private final Mounter mounter;
private final BooleanProperty showingStats;
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
private AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@Inject
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
@@ -80,7 +80,7 @@ public class Vault {
this.state = state;
this.lastKnownException = lastKnownException;
this.stats = stats;
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path);
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
this.locked = Bindings.createBooleanBinding(this::isLocked, state);
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
@@ -98,29 +98,29 @@ public class Vault {
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
if (vaultSettings.usesReadOnlyMode.get()) {
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) {
} else if (vaultSettings.maxCleartextFilenameLength().get() == -1) {
LOG.debug("Determining cleartext filename length limitations...");
var checker = new FileSystemCapabilityChecker();
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
if (ciphertextLimit < shorteningThreshold) {
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit);
vaultSettings.maxCleartextFilenameLength().set(cleartextLimit);
} else {
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH);
vaultSettings.maxCleartextFilenameLength().setValue(UNLIMITED_FILENAME_LENGTH);
}
}
if (vaultSettings.maxCleartextFilenameLength.get() < UNLIMITED_FILENAME_LENGTH) {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
if (vaultSettings.maxCleartextFilenameLength().get() < UNLIMITED_FILENAME_LENGTH) {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength().get());
}
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
@@ -253,11 +253,11 @@ public class Vault {
}
public ReadOnlyStringProperty displayNameProperty() {
return vaultSettings.displayName;
return vaultSettings.displayName();
}
public String getDisplayName() {
return vaultSettings.displayName.get();
return vaultSettings.displayName().get();
}
public ObjectBinding<Mountpoint> mountPointProperty() {
@@ -274,7 +274,7 @@ public class Vault {
}
public String getDisplayablePath() {
Path p = vaultSettings.path.get();
Path p = vaultSettings.path().get();
if (p.startsWith(HOME_DIR)) {
Path relativePath = HOME_DIR.relativize(p);
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
@@ -311,7 +311,7 @@ public class Vault {
}
public Path getPath() {
return vaultSettings.path.get();
return vaultSettings.path().getValue();
}
/**
@@ -346,7 +346,7 @@ public class Vault {
}
public String getId() {
return vaultSettings.id;
return vaultSettings.getId();
}
// ******************************************************************************
@@ -27,7 +27,7 @@ public class VaultConfigCache {
void reloadConfig() throws IOException {
try {
config.set(readConfigFromStorage(this.settings.path.get()));
config.set(readConfigFromStorage(this.settings.path().get()));
} catch (IOException e) {
config.set(null);
throw e;
@@ -49,8 +49,8 @@ public class VaultListManager {
this.vaultComponentFactory = vaultComponentFactory;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
addAll(settings.directories);
vaultList.addListener(new VaultListChangeListener(settings.directories));
addAll(settings.getDirectories());
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
autoLocker.init();
}
@@ -70,11 +70,11 @@ public class VaultListManager {
private VaultSettings newVaultSettings(Path path) {
VaultSettings vaultSettings = VaultSettings.withRandomId();
vaultSettings.path.set(path);
vaultSettings.path().set(path);
if (path.getFileName() != null) {
vaultSettings.displayName.set(path.getFileName().toString());
vaultSettings.displayName().set(path.getFileName().toString());
} else {
vaultSettings.displayName.set(defaultVaultName);
vaultSettings.displayName().set(defaultVaultName);
}
return vaultSettings;
}
@@ -95,13 +95,13 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) {
var wrapper = new VaultConfigCache(vaultSettings);
try {
var vaultState = determineVaultState(vaultSettings.path.get());
var vaultState = determineVaultState(vaultSettings.path().get());
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig();
}
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e);
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
}
}
@@ -9,8 +9,8 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.function.Function;
//TODO can the enum be removed?
sealed interface IpcMessage permits HandleLaunchArgsMessage, RevealRunningAppMessage {
// TODO make sealed, remove enum
interface IpcMessage {
enum MessageType {
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
@@ -5,9 +5,10 @@ import java.util.List;
public interface IpcMessageListener {
default void handleMessage(IpcMessage message) {
switch (message) {
case RevealRunningAppMessage m -> revealRunningApp(); // TODO: rename to _ with JEP 443
case HandleLaunchArgsMessage m -> handleLaunchArgs(m.args());
if (message instanceof RevealRunningAppMessage) {
revealRunningApp();
} else if (message instanceof HandleLaunchArgsMessage m) {
handleLaunchArgs(m.args());
}
}
@@ -9,7 +9,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.SubstitutingProperties;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
@@ -30,18 +29,10 @@ import java.util.concurrent.Executors;
public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis();
static {
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
System.setProperties(lazyProcessedProps);
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
LOG = LoggerFactory.getLogger(Cryptomator.class);
}
// DaggerCryptomatorComponent gets generated by Dagger.
// Run Maven and include target/generated-sources/annotations in your IDE.
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT;
private static final Logger LOG;
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private final DebugMode debugMode;
private final SupportedLanguages supportedLanguages;
@@ -72,6 +63,7 @@ public class Cryptomator {
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
return;
}
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads.
@@ -94,7 +86,7 @@ public class Cryptomator {
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
*/
try (var communicator = IpcCommunicator.create(env.getIpcSocketPath().toList())) {
try (var communicator = IpcCommunicator.create(env.ipcSocketPath().toList())) {
if (communicator.isClient()) {
communicator.sendHandleLaunchargs(List.of(args));
communicator.sendRevealRunningApp();
@@ -6,7 +6,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -49,7 +48,7 @@ class FileOpenRequestHandler {
handleLaunchArgs(FileSystems.getDefault(), args);
}
@VisibleForTesting
// visible for testing
void handleLaunchArgs(FileSystem fs, List<String> args) {
Collection<Path> pathsToOpen = args.stream().map(str -> {
try {
@@ -6,40 +6,42 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.StreamSupport;
@Singleton
public class SupportedLanguages {
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_".
// "en" is not part of this list - it is always inserted at the top.
public static final List<String> LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", //
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", //
"ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
public static final String ENGLISH = "en";
private final List<String> sortedLanguageTags;
private final Locale preferredLocale;
@Inject
public SupportedLanguages(Settings settings) {
var preferredLanguage = settings.language.get();
var preferredLanguage = settings.languageProperty().get();
preferredLocale = preferredLanguage == null ? Locale.getDefault() : Locale.forLanguageTag(preferredLanguage);
var collator = Collator.getInstance(preferredLocale);
collator.setStrength(Collator.PRIMARY);
var sorted = new ArrayList<String>();
sorted.add(0, null);
sorted.add(1, ENGLISH);
LANGUAGE_TAGS.stream() //
List<String> sortedTags = new ArrayList<>();
sortedTags.add(0, Settings.DEFAULT_LANGUAGE);
sortedTags.add(1, ENGLISH);
getSupportedLanguageTags().stream() //
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
.forEach(sorted::add);
sortedLanguageTags = Collections.unmodifiableList(sorted);
.forEach(sortedTags::add);
sortedLanguageTags = Collections.unmodifiableList(sortedTags);
}
public void applyPreferred() {
@@ -51,4 +53,43 @@ public class SupportedLanguages {
return sortedLanguageTags;
}
/**
* Iterates over the /i18n directory and extracts from every localization file the BCP 47 code.
*
* @return list of supported BCP 47 language codes
*/
private static List<String> getSupportedLanguageTags() {
try {
var i18DirURI = SupportedLanguages.class.getResource("/i18n").toURI();
var uriScheme = i18DirURI.getScheme();
if (uriScheme.equals("jar")) {
final String[] array = i18DirURI.toString().split("!");
try (var jarFs = FileSystems.newFileSystem(URI.create(array[0]), Map.<String, String>of())) {
return streamDirectory(jarFs.getPath(array[1]));
}
} else if (uriScheme.equals("file")) {
return streamDirectory(Path.of(i18DirURI));
} else {
throw new IOException("Unsupported uri scheme: " + uriScheme);
}
} catch (URISyntaxException | IOException e) {
LOG.warn("Unable to determine additional supported languages.", e);
return List.of();
}
}
private static List<String> streamDirectory(Path i18Dir) throws IOException {
try (var dirStream = Files.newDirectoryStream(i18Dir, "strings_*.properties")) {
return StreamSupport.stream(dirStream.spliterator(), false) //
.map(SupportedLanguages::getBCP47CodeFromFileName) //
.toList();
}
}
private static String getBCP47CodeFromFileName(Path p) {
var fileName = p.getFileName().toString();
return fileName.substring("strings_".length(), fileName.indexOf(".properties")).replace('_', '-');
}
}
@@ -26,8 +26,8 @@ public class DebugMode {
}
public void initialize() {
setLogLevels(settings.debugMode.get());
settings.debugMode.addListener(this::logLevelChanged);
setLogLevels(settings.debugMode().get());
settings.debugMode().addListener(this::logLevelChanged);
}
private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
@@ -5,7 +5,6 @@ import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.classic.spi.ConfiguratorRank;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
@@ -20,7 +19,6 @@ import org.cryptomator.common.Environment;
import java.nio.file.Path;
import java.util.Map;
@ConfiguratorRank(ConfiguratorRank.CUSTOM_NORMAL_PRIORITY)
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
@@ -5,23 +5,21 @@ import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@@ -45,8 +43,9 @@ public abstract class AddVaultModule {
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage) {
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(primaryStage);
@@ -66,13 +65,6 @@ public abstract class AddVaultModule {
return new SimpleStringProperty("");
}
@Provides
@Named("shorteningThreshold")
@AddVaultWizardScoped
static IntegerProperty provideShorteningThreshold() {
return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
}
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
@@ -89,6 +81,13 @@ public abstract class AddVaultModule {
// ------------------
@Provides
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
@AddVaultWizardScoped
static Scene provideWelcomeScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_WELCOME);
}
@Provides
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
@AddVaultWizardScoped
@@ -131,15 +130,13 @@ public abstract class AddVaultModule {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS);
}
@Provides
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS)
@AddVaultWizardScoped
static Scene provideCreateNewVaultExpertSettingsScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS);
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(AddVaultWelcomeController.class)
abstract FxController bindWelcomeController(AddVaultWelcomeController controller);
@Binds
@IntoMap
@FxControllerKey(ChooseExistingVaultController.class)
@@ -184,9 +181,4 @@ public abstract class AddVaultModule {
@FxControllerKey(AddVaultSuccessController.class)
abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller);
@Binds
@IntoMap
@FxControllerKey(CreateNewVaultExpertSettingsController.class)
abstract FxController bindCreateNewVaultExpertSettingsController(CreateNewVaultExpertSettingsController controller);
}
@@ -0,0 +1,38 @@
package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.scene.Scene;
import javafx.stage.Stage;
@AddVaultWizardScoped
public class AddVaultWelcomeController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(AddVaultWelcomeController.class);
private final Stage window;
private final Lazy<Scene> chooseExistingVaultScene;
private final Lazy<Scene> createNewVaultScene;
@Inject
AddVaultWelcomeController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_EXISTING) Lazy<Scene> chooseExistingVaultScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> createNewVaultScene) {
this.window = window;
this.chooseExistingVaultScene = chooseExistingVaultScene;
this.createNewVaultScene = createNewVaultScene;
}
public void createNewVault() {
LOG.debug("AddVaultWelcomeController.createNewVault()");
window.setScene(createNewVaultScene.get());
}
public void chooseExistingVault() {
LOG.debug("AddVaultWelcomeController.chooseExistingVault()");
window.setScene(chooseExistingVaultScene.get());
}
}
@@ -12,7 +12,6 @@ import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ResourceBundle;
@AddVaultWizardScoped
@Subcomponent(modules = {AddVaultModule.class})
@@ -21,23 +20,12 @@ public interface AddVaultWizardComponent {
@AddVaultWizardWindow
Stage window();
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME)
Lazy<Scene> sceneNew();
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
Lazy<Scene> sceneExisting();
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
Lazy<Scene> scene();
default void showAddNewVaultWizard(ResourceBundle resourceBundle) {
default void showAddVaultWizard() {
Stage stage = window();
stage.setScene(sceneNew().get());
stage.setTitle(resourceBundle.getString("addvaultwizard.new.title"));
stage.sizeToScene();
stage.show();
}
default void showAddExistingVaultWizard(ResourceBundle resourceBundle) {
Stage stage = window();
stage.setScene(sceneExisting().get());
stage.setTitle(resourceBundle.getString("addvaultwizard.existing.title"));
stage.setScene(scene().get());
stage.sizeToScene();
stage.show();
}
@@ -35,6 +35,7 @@ public class ChooseExistingVaultController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(ChooseExistingVaultController.class);
private final Stage window;
private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> successScene;
private final FxApplicationWindows appWindows;
private final ObjectProperty<Path> vaultPath;
@@ -44,15 +45,9 @@ public class ChooseExistingVaultController implements FxController {
private final ObservableValue<Image> screenshot;
@Inject
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, //
@FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, //
FxApplicationWindows appWindows, //
ObjectProperty<Path> vaultPath, //
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle, //
FxApplicationStyle applicationStyle) {
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, FxApplicationStyle applicationStyle) {
this.window = window;
this.welcomeScene = welcomeScene;
this.successScene = successScene;
this.appWindows = appWindows;
this.vaultPath = vaultPath;
@@ -75,6 +70,11 @@ public class ChooseExistingVaultController implements FxController {
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
}
@FXML
public void back() {
window.setScene(welcomeScene.get());
}
@FXML
public void chooseFileAndNext() {
FileChooser fileChooser = new FileChooser();
@@ -1,118 +0,0 @@
package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.controls.NumericTextField;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.nio.file.Path;
@AddVaultWizardScoped
public class CreateNewVaultExpertSettingsController implements FxController {
public static final int MAX_SHORTENING_THRESHOLD = 220;
public static final int MIN_SHORTENING_THRESHOLD = 36;
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/en/1.7/security/architecture/#name-shortening";
private final Stage window;
private final Lazy<Application> application;
private final Lazy<Scene> chooseLocationScene;
private final Lazy<Scene> choosePasswordScene;
private final StringProperty vaultNameProperty;
private final ObjectProperty<Path> vaultPathProperty;
private final IntegerProperty shorteningThreshold;
private final BooleanBinding validShorteningThreshold;
//FXML
public Label vaultNameLabel;
public Label vaultPathLabel;
public CheckBox expertSettingsCheckBox;
public NumericTextField shorteningThresholdTextField;
@Inject
CreateNewVaultExpertSettingsController(@AddVaultWizardWindow Stage window, //
Lazy<Application> application, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, //
@Named("vaultName") StringProperty vaultName, //
ObjectProperty<Path> vaultPath, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold) {
this.window = window;
this.application = application;
this.chooseLocationScene = chooseLocationScene;
this.choosePasswordScene = choosePasswordScene;
this.vaultNameProperty = vaultName;
this.vaultPathProperty = vaultPath;
this.shorteningThreshold = shorteningThreshold;
this.validShorteningThreshold = Bindings.createBooleanBinding(this::isValidShorteningThreshold, shorteningThreshold);
}
@FXML
public void initialize() {
vaultNameLabel.textProperty().bind(vaultNameProperty);
vaultPathLabel.textProperty().bind(vaultPathProperty.asString());
shorteningThresholdTextField.setPromptText(MIN_SHORTENING_THRESHOLD + "-" + MAX_SHORTENING_THRESHOLD);
shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
shorteningThresholdTextField.textProperty().addListener((observable, oldValue, newValue) -> {
try {
int intValue = Integer.parseInt(newValue);
shorteningThreshold.set(intValue);
} catch (NumberFormatException e) {
shorteningThreshold.set(0); //the value is set to 0 to ensure that an invalid value assignment is detected during a NumberFormatException
}
});
}
@FXML
public void toggleUseExpertSettings() {
if (!expertSettingsCheckBox.isSelected()) {
shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
}
}
@FXML
public void back() {
window.setScene(chooseLocationScene.get());
}
@FXML
public void next() {
window.setScene(choosePasswordScene.get());
}
public BooleanBinding validShorteningThresholdProperty() {
return validShorteningThreshold;
}
public boolean isValidShorteningThreshold() {
var value = shorteningThreshold.get();
return value >= MIN_SHORTENING_THRESHOLD && value <= MAX_SHORTENING_THRESHOLD;
}
public void openDocs() {
application.get().getHostServices().showDocument(DOCS_NAME_SHORTENING_URL);
}
public Path getVaultPath() {
return vaultPathProperty.get();
}
public String getVaultName() {
return vaultNameProperty.get();
}
}
@@ -1,9 +1,6 @@
package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.locationpresets.LocationPreset;
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
@@ -13,19 +10,22 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
@@ -34,9 +34,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
@AddVaultWizardScoped
@@ -48,78 +45,71 @@ public class CreateNewVaultLocationController implements FxController {
private final Stage window;
private final Lazy<Scene> chooseNameScene;
private final Lazy<Scene> chooseExpertSettingsScene;
private final List<RadioButton> locationPresetBtns;
private final Lazy<Scene> choosePasswordScene;
private final ObservedLocationPresets locationPresets;
private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName;
private final ResourceBundle resourceBundle;
private final ObservableValue<VaultPathStatus> vaultPathStatus;
private final ObservableValue<Boolean> validVaultPath;
private final BooleanBinding validVaultPath;
private final BooleanProperty usePresetPath;
private final StringProperty statusText;
private final ObjectProperty<Node> statusGraphic;
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
//FXML
public ToggleGroup locationPresetsToggler;
public VBox radioButtonVBox;
public ToggleGroup predefinedLocationToggler;
public RadioButton iclouddriveRadioButton;
public RadioButton dropboxRadioButton;
public RadioButton gdriveRadioButton;
public RadioButton onedriveRadioButton;
public RadioButton megaRadioButton;
public RadioButton pcloudRadioButton;
public RadioButton customRadioButton;
public Label locationStatusLabel;
public Label vaultPathStatus;
public FontAwesome5IconView goodLocation;
public FontAwesome5IconView badLocation;
@Inject
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
ObjectProperty<Path> vaultPath, //
@Named("vaultName") StringProperty vaultName, //
ResourceBundle resourceBundle) {
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window;
this.chooseNameScene = chooseNameScene;
this.chooseExpertSettingsScene = chooseExpertSettingsScene;
this.choosePasswordScene = choosePasswordScene;
this.locationPresets = locationPresets;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
this.resourceBundle = resourceBundle;
this.vaultPathStatus = ObservableUtil.mapWithDefault(vaultPath, this::validatePath, new VaultPathStatus(false, "error.message"));
this.validVaultPath = ObservableUtil.mapWithDefault(vaultPathStatus, VaultPathStatus::valid, false);
this.vaultPathStatus.addListener(this::updateStatusLabel);
this.validVaultPath = Bindings.createBooleanBinding(this::validateVaultPathAndSetStatus, this.vaultPath);
this.usePresetPath = new SimpleBooleanProperty();
this.locationPresetBtns = LocationPresetsProvider.loadAll(LocationPresetsProvider.class) //
.flatMap(LocationPresetsProvider::getLocations) //
.sorted(Comparator.comparing(LocationPreset::name)) //
.map(preset -> { //
var btn = new RadioButton(preset.name());
btn.setUserData(preset.path());
return btn;
}).toList();
this.statusText = new SimpleStringProperty();
this.statusGraphic = new SimpleObjectProperty<>();
}
private VaultPathStatus validatePath(Path p) throws NullPointerException {
if (!Files.exists(p.getParent())) {
return new VaultPathStatus(false, "addvaultwizard.new.locationDoesNotExist");
private boolean validateVaultPathAndSetStatus() {
final Path p = vaultPath.get();
if (p == null) {
statusText.set("Error: Path is NULL.");
statusGraphic.set(badLocation);
return false;
} else if (!Files.exists(p.getParent())) {
statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
statusGraphic.set(badLocation);
return false;
} else if (!isActuallyWritable(p.getParent())) {
return new VaultPathStatus(false, "addvaultwizard.new.locationIsNotWritable");
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
statusGraphic.set(badLocation);
return false;
} else if (!Files.notExists(p)) {
return new VaultPathStatus(false, "addvaultwizard.new.fileAlreadyExists");
statusText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
statusGraphic.set(badLocation);
return false;
} else {
return new VaultPathStatus(true, "addvaultwizard.new.locationIsOk");
statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsOk"));
statusGraphic.set(goodLocation);
return true;
}
}
private void updateStatusLabel(ObservableValue<? extends VaultPathStatus> observable, VaultPathStatus oldValue, VaultPathStatus newValue) {
if (newValue.valid()) {
locationStatusLabel.setGraphic(goodLocation);
locationStatusLabel.getStyleClass().remove("label-red");
locationStatusLabel.getStyleClass().add("label-muted");
} else {
locationStatusLabel.setGraphic(badLocation);
locationStatusLabel.getStyleClass().remove("label-muted");
locationStatusLabel.getStyleClass().add("label-red");
}
this.locationStatusLabel.setText(resourceBundle.getString(newValue.localizationKey()));
}
private boolean isActuallyWritable(Path p) {
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
@@ -137,15 +127,26 @@ public class CreateNewVaultLocationController implements FxController {
@FXML
public void initialize() {
radioButtonVBox.getChildren().addAll(1, locationPresetBtns); //first item is the list header
locationPresetsToggler.getToggles().addAll(locationPresetBtns);
locationPresetsToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
usePresetPath.bind(locationPresetsToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
}
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
var storagePath = Optional.ofNullable((Path) newValue.getUserData()).orElse(customVaultPath);
vaultPath.set(storagePath.resolve(vaultName.get()));
if (iclouddriveRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getIclouddriveLocation().resolve(vaultName.get()));
} else if (dropboxRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getDropboxLocation().resolve(vaultName.get()));
} else if (gdriveRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
} else if (onedriveRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get()));
} else if (megaRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get()));
} else if (pcloudRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get()));
} else if (customRadioButton.equals(newValue)) {
vaultPath.set(customVaultPath.resolve(vaultName.get()));
}
}
@FXML
@@ -155,8 +156,10 @@ public class CreateNewVaultLocationController implements FxController {
@FXML
public void next() {
if (validVaultPath.getValue()) {
window.setScene(chooseExpertSettingsScene.get());
if (validateVaultPathAndSetStatus()) {
window.setScene(choosePasswordScene.get());
} else {
validVaultPath.invalidate();
}
}
@@ -176,12 +179,6 @@ public class CreateNewVaultLocationController implements FxController {
}
}
/* Internal classes */
private record VaultPathStatus(boolean valid, String localizationKey) {
}
/* Getter/Setter */
public Path getVaultPath() {
@@ -192,28 +189,47 @@ public class CreateNewVaultLocationController implements FxController {
return vaultPath;
}
public ObservableValue<Boolean> validVaultPathProperty() {
public BooleanBinding validVaultPathProperty() {
return validVaultPath;
}
public boolean isValidVaultPath() {
return validVaultPath.getValue();
public Boolean getValidVaultPath() {
return validVaultPath.get();
}
public ObservedLocationPresets getObservedLocationPresets() {
return locationPresets;
}
public BooleanProperty usePresetPathProperty() {
return usePresetPath;
}
public boolean isUsePresetPath() {
public boolean getUsePresetPath() {
return usePresetPath.get();
}
public BooleanBinding anyRadioButtonSelectedProperty() {
return locationPresetsToggler.selectedToggleProperty().isNotNull();
return predefinedLocationToggler.selectedToggleProperty().isNotNull();
}
public boolean isAnyRadioButtonSelected() {
return anyRadioButtonSelectedProperty().get();
}
public StringProperty statusTextProperty() {
return statusText;
}
public String getStatusText() {
return statusText.get();
}
public ObjectProperty<Node> statusGraphicProperty() {
return statusGraphic;
}
public Node getStatusGraphic() {
return statusGraphic.get();
}
}
@@ -17,6 +17,7 @@ import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.regex.Pattern;
@AddVaultWizardScoped
@@ -26,17 +27,16 @@ public class CreateNewVaultNameController implements FxController {
public TextField textField;
private final Stage window;
private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> chooseLocationScene;
private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName;
private final BooleanBinding validVaultName;
@Inject
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, //
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
ObjectProperty<Path> vaultPath, //
@Named("vaultName") StringProperty vaultName) {
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window;
this.welcomeScene = welcomeScene;
this.chooseLocationScene = chooseLocationScene;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
@@ -58,6 +58,11 @@ public class CreateNewVaultNameController implements FxController {
}
}
@FXML
public void back() {
window.setScene(welcomeScene.get());
}
@FXML
public void next() {
window.setScene(chooseLocationScene.get());

Some files were not shown because too many files have changed in this diff Show More