Compare commits

..
Author SHA1 Message Date
Armin Schrenk dcfdb5ad21 first impl draft 2023-03-20 14:38:22 +01:00
280 changed files with 2969 additions and 8086 deletions
+1 -2
View File
@@ -43,7 +43,6 @@ body:
- WinFsp (Local Drive) - WinFsp (Local Drive)
- FUSE-T - FUSE-T
- macFUSE - macFUSE
- FUSE
- WebDAV (Windows Explorer) - WebDAV (Windows Explorer)
- WebDAV (AppleScript) - WebDAV (AppleScript)
- WebDAV (gio) - WebDAV (gio)
@@ -96,4 +95,4 @@ body:
id: further-info id: further-info
attributes: attributes:
label: Anything else? 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 ## 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)) mQINBFbgeicBEADM9AcU6DTgM5KZnBaJc6x9DBLr+TCMHntTt7YM9GLTlO2Z43Jt
oYoyqdRWAY28veqpLEFgRvvVD3fdBj/KUOxF1cr2JsErwXqbjwaLq0o/0KIXz7UK
## Expectations a6pQSemZKfpOtJrfacofOTwvG6AuG9uakBYNMyxuojyOkoh3xsYS1KZ7TwPgCdET
t8/zva41Pa5kh5+GeSZJdCuygG6ynPBJEpmK5V7Qizvics5fziXecF+QaFZijafv
When reporting a vulnerability, please provide us with a detailed report that includes: YahfxokvF9pXCQTmV4m57NQma9uK0w83U9nJCPjEd+x3wK0Hxrc1ojy8ZFTA1YND
AQg/MTABgHbQQkXDQhjS/TloOObqtbMBqNSbcSXpaR4teaCWKBl1MSq00nJLj8db
- A description of the vulnerability vPJGqfg7UbXhlALggp029/kskYlR5SmbxWquLbl0Xre3fDHuHEiWcJL6MS3454Wt
- Steps to reproduce the vulnerability Mno13/4UhOlRFh5g0pLmPz7seOTJjDqc9abn/RXOLq0+3qX0gC0bDm5aCE5dQ2MV
- Possible impact of the vulnerability FMbrrlw/dZESNLZvtB3gOsramSry1R3HVZ0QJ2vMaF2cxewebqcYbuecUNj6bxpv
- Any additional information that may be helpful 5LEhEmqz6dG1meLLWDsvQLPEUWEIJnfpBiDSm342yxJq4pXnVF+aqAQsCL3FpmvZ
2j0FgFOs7iXOcFUJIiR0xUmWPk1NWYcUowqmRW8pMM9nFUzFF99iggPznwARAQAB
We ask that you do not publicly disclose the vulnerability until we have had a chance to address it. tC1DcnlwdG9tYXRvciBTdXBwb3J0IDxzdXBwb3J0QGNyeXB0b21hdG9yLm9yZz6J
AkAEEwEKACoCGwMFCQcrKAAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAleu2cQC
## Thank You GQEACgkQI7Xb75TU2B3+7A/7BKRWdo5/moCCEbBzYQ7vRMLFdwmjFFlSZ7aGC0fP
YHdeUwxPbO0cATwmNpGMma7rBn1FDg3Vto6/wottGxm+XIRwlyY84CD1VZAihZ/e
We appreciate your help in keeping Cryptomator secure. Thank you for your contributions to the security of our project. 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"
+13
View File
@@ -0,0 +1,13 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: state:awaiting-response
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.
-29
View File
@@ -1,29 +0,0 @@
# .github/release.yml
# see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
changelog:
exclude:
authors:
- cryptobot
- dependabot
- github-actions
categories:
- title: What's New 🎉
labels:
- type:feature-request
- type:enhancement
- title: Bugfixes 🐛
labels:
- type:security-issue
- type:bug
- type:minor-bug
- title: Other Changes 📎
labels:
- "*"
exclude:
labels:
- type:feature-request
- type:enhancement
- type:security-issue
- type:bug
- type:minor-bug
+24
View File
@@ -0,0 +1,24 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 365
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 90
# Issues with these labels will never be considered stale
exemptLabels:
- type:security-issue # never close automatically
- type:feature-request # never close automatically
- type:enhancement # never close automatically
- type:upstream-bug # never close automatically
- state:awaiting-response # handled by different bot
- state:blocked
- state:confirmed
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Label to use when marking an issue as stale
staleLabel: state:stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+25 -56
View File
@@ -10,8 +10,7 @@ on:
required: false required: false
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20
jobs: jobs:
get-version: get-version:
@@ -21,50 +20,26 @@ jobs:
build: build:
name: Build AppImage name: Build AppImage
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
needs: [get-version] 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: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
check-latest: true java-package: 'jdk+fx'
cache: 'maven' cache: 'maven'
- name: Ensure major jfx version in pom equals in jdk
- name: Download OpenJFX jmods shell: pwsh
id: download-jmods
run: | run: |
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip $jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check $jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
mkdir -p openjfx-jmods if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
- 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})"
exit 1 exit 1
fi }
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
@@ -78,8 +53,8 @@ jobs:
${JAVA_HOME}/bin/jlink ${JAVA_HOME}/bin/jlink
--verbose --verbose
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" --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
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -105,20 +80,18 @@ jobs:
--copyright "(C) 2016 - 2023 Skymatic GmbH" --copyright "(C) 2016 - 2023 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview" --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 "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true" --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" --java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" --java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" --java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties --add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources --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.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.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.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.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/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 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 ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit - name: Download AppImageKit
run: | 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 chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract ./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235 - name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -155,8 +124,8 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage - name: Build AppImage
run: > run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync' -u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback" --sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Create detached GPG signatures - name: Create detached GPG signatures
run: | run: |
@@ -165,7 +134,7 @@ jobs:
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: appimage-${{ matrix.appimage-suffix }} name: appimage
path: | path: |
cryptomator-*.AppImage cryptomator-*.AppImage
cryptomator-*.AppImage.zsync cryptomator-*.AppImage.zsync
+2 -8
View File
@@ -6,8 +6,7 @@ on:
types: [labeled] types: [labeled]
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20
defaults: defaults:
run: run:
@@ -21,7 +20,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-java@v3 - uses: actions/setup-java@v3
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Cache SonarCloud packages - name: Cache SonarCloud packages
@@ -54,9 +53,4 @@ jobs:
body: |- body: |-
:construction: Work in Progress :construction: Work in Progress
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
--- ---
<!-- Don't forget to include the 💾 SHA-256 checksums of release artifacts: -->
+19 -15
View File
@@ -3,6 +3,9 @@ name: Build Debian Package
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
ref:
description: 'GitHub Ref (e.g. refs/tags/1.6.16)'
required: true
semver: semver:
description: 'SemVer String (e.g. 1.7.0-beta1)' description: 'SemVer String (e.g. 1.7.0-beta1)'
required: true required: true
@@ -16,12 +19,9 @@ on:
type: boolean type: boolean
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20 OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip' OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_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'
jobs: jobs:
build: build:
@@ -29,6 +29,9 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
- id: versions - id: versions
name: Get version information name: Get version information
run: | run: |
@@ -42,13 +45,12 @@ jobs:
run: | run: |
sudo add-apt-repository ppa:coffeelibs/openjdk sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0 sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven' cache: 'maven'
- name: Run maven - name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests run: mvn -B clean package -Pdependency-check,linux -DskipTests
@@ -56,11 +58,9 @@ jobs:
id: download-jmods id: download-jmods
run: | run: |
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip 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 mkdir -p jmods/amd64
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d 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 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 mkdir -p jmods/aarch64
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d 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 - name: Ensure major jfx version in pom and in jmods is the same
@@ -97,8 +97,7 @@ jobs:
run: | run: |
cp -r dist/linux/debian/ pkgdir cp -r dist/linux/debian/ pkgdir
export RFC2822_TIMESTAMP=`date --rfc-2822` export RFC2822_TIMESTAMP=`date --rfc-2822`
export DISABLE_UPDATE_CHECK=${{ inputs.dput }} envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
find . -name "*.jar" >> pkgdir/debian/source/include-binaries find . -name "*.jar" >> pkgdir/debian/source/include-binaries
mv pkgdir cryptomator_${{ inputs.ppaver }} mv pkgdir cryptomator_${{ inputs.ppaver }}
@@ -142,10 +141,15 @@ jobs:
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
# If ref is a tag, also upload to GitHub Releases: # If ref is a tag, also upload to GitHub Releases:
- name: Determine tag name
if: startsWith(inputs.ref, 'refs/tags/')
run: |
REF=${{ inputs.ref }}
echo "TAG_NAME=${REF##*/}" >> $GITHUB_ENV
- name: Publish Debian package on GitHub Releases - name: Publish Debian package on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') if: startsWith(inputs.ref, 'refs/tags/')
env: env:
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
run: | run: |
artifacts=$(ls | grep cryptomator*.deb) artifacts=$(ls | grep cryptomator*.deb)
gh release upload ${{ github.ref_name }} $artifacts gh release upload ${{ env.TAG_NAME }} $artifacts
+3 -2
View File
@@ -22,8 +22,9 @@ on:
value: ${{ jobs.determine-version.outputs.type }} value: ${{ jobs.determine-version.outputs.type }}
env: env:
JAVA_VERSION: 19
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: 20 JAVA_CACHE: 'maven'
jobs: jobs:
determine-version: determine-version:
@@ -43,7 +44,7 @@ jobs:
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: ${{ env.JAVA_CACHE }}
- id: versions - id: versions
name: Get version information name: Get version information
run: | run: |
+19 -40
View File
@@ -8,15 +8,9 @@ on:
version: version:
description: 'Version' description: 'Version'
required: false required: false
notarize:
description: 'Notarize'
required: true
default: false
type: boolean
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20
jobs: jobs:
get-version: get-version:
@@ -37,45 +31,31 @@ jobs:
output-suffix: x64 output-suffix: x64
xcode-path: '/Applications/Xcode_13.2.1.app' xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: macFUSE 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] - os: [self-hosted, macOS, ARM64]
architecture: aarch64 architecture: aarch64
output-suffix: arm64 output-suffix: arm64
xcode-path: '/Applications/Xcode_13.2.1.app' xcode-path: '/Applications/Xcode_13.2.1.app'
fuse-lib: FUSE-T 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: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
architecture: ${{ matrix.architecture }} architecture: ${{ matrix.architecture }}
check-latest: true
cache: 'maven' cache: 'maven'
- name: Download OpenJFX jmods - name: Ensure major jfx version in pom equals in jdk
id: download-jmods if: ${{ !contains(matrix.os, 'self-hosted') }}
shell: pwsh
run: | run: |
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip $jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check $jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
mkdir -p openjfx-jmods/ if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
- 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})"
exit 1 exit 1
fi }
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
@@ -89,7 +69,7 @@ jobs:
${JAVA_HOME}/bin/jlink ${JAVA_HOME}/bin/jlink
--verbose --verbose
--output runtime --output runtime
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" --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 --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
@@ -115,17 +95,16 @@ jobs:
--java-options "-Xss5m" --java-options "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true" --java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true" --java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\"" --java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\"" --java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\"" --java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\"" --java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\"" --java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\"" --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.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator --mac-package-identifier org.cryptomator
@@ -228,7 +207,7 @@ jobs:
env: env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
- name: Notarize .dmg - name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize if: startsWith(github.ref, 'refs/tags/')
uses: cocoalibs/xcode-notarization-action@v1 uses: cocoalibs/xcode-notarization-action@v1
with: with:
app-path: 'Cryptomator-*.dmg' app-path: 'Cryptomator-*.dmg'
-22
View File
@@ -1,22 +0,0 @@
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
name: 'Close awaiting response issues'
on:
schedule:
- cron: '00 09 * * *'
jobs:
no-response:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
with:
days-before-stale: 14
days-before-close: 0
days-before-pr-close: -1
stale-issue-label: 'state:stale'
close-issue-message: "This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further."
only-labels: 'state:awaiting-response'
+2 -3
View File
@@ -4,8 +4,7 @@ on:
pull_request: pull_request:
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20
defaults: defaults:
run: run:
@@ -20,7 +19,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-java@v3 - uses: actions/setup-java@v3
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Build and Test - name: Build and Test
+4 -1
View File
@@ -6,6 +6,9 @@ on:
- 'release/**' - 'release/**'
- 'hotfix/**' - 'hotfix/**'
env:
JAVA_VERSION: 19
defaults: defaults:
run: run:
shell: bash shell: bash
@@ -15,7 +18,7 @@ jobs:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- id: validate-pom-version - id: validate-pom-version
name: Validate POM version name: Validate POM version
run: | run: |
-24
View File
@@ -1,24 +0,0 @@
# Configuration for close-stale-issues - https://github.com/marketplace/actions/close-stale-issues
name: 'Close stale issues'
on:
schedule:
- cron: '00 09 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
with:
days-before-stale: 365
days-before-close: 90
exempt-issue-labels: 'type:security-issue,type:feature-request,type:enhancement,type:upstream-bug,state:awaiting-response,state:blocked,state:confirmed'
exempt-all-milestones: true
stale-issue-label: 'state:stale'
stale-pr-label: 'state:stale'
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
+22 -49
View File
@@ -8,16 +8,11 @@ on:
version: version:
description: 'Version' description: 'Version'
required: false required: false
isDebug:
description: 'Build debug version with console output'
type: boolean
env: env:
JAVA_DIST: 'temurin' JAVA_VERSION: 19
JAVA_VERSION: 20 JAVA_DIST: 'zulu'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip' JAVA_CACHE: 'maven'
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
defaults: defaults:
run: run:
@@ -35,7 +30,6 @@ jobs:
needs: [get-version] needs: [get-version]
env: env:
LOOPBACK_ALIAS: 'cryptomator-vault' LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
@@ -43,31 +37,17 @@ jobs:
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
check-latest: true java-package: 'jdk+fx'
cache: 'maven' cache: ${{ env.JAVA_CACHE }}
- name: Download and extract JavaFX jmods from Gluon - name: Ensure major jfx version in pom equals in jdk
#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 }}.";
}
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
shell: pwsh shell: pwsh
- name: Ensure major jfx version in pom and in jmods is the same
run: | run: |
JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1) $jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@} $jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*} if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
exit 1 exit 1
fi }
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
@@ -81,16 +61,13 @@ jobs:
${JAVA_HOME}/bin/jlink ${JAVA_HOME}/bin/jlink
--verbose --verbose
--output runtime --output runtime
--module-path "jfxjmods;${JAVA_HOME}/jmods" --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 --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
--strip-debug --strip-debug
--compress=1 --compress=1
- name: Change win-console flag if debug is active
if: ${{ inputs.isDebug }}
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
- name: Run jpackage - name: Run jpackage
run: > run: >
${JAVA_HOME}/bin/jpackage ${JAVA_HOME}/bin/jpackage
@@ -111,22 +88,19 @@ jobs:
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true" --java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\"" --java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\"" --java-options "-Dcryptomator.settingsPath=\"~/AppData/Roaming/Cryptomator/settings.json\""
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\"" --java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\"" --java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\"" --java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\"" --java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
--java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/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 --resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico --icon dist/win/resources/Cryptomator.ico
${WIN_CONSOLE_FLAG}
- name: Patch Application Directory - name: Patch Application Directory
run: | run: |
cp dist/win/contrib/* appdir/Cryptomator cp dist/win/contrib/* appdir/Cryptomator
@@ -246,8 +220,7 @@ jobs:
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
check-latest: true cache: ${{ env.JAVA_CACHE }}
cache: 'maven'
- name: Generate license for exe - name: Generate license for exe
run: > run: >
mvn -B license:add-third-party mvn -B license:add-third-party
@@ -359,7 +332,7 @@ jobs:
cp msi/*.msi files cp msi/*.msi files
cp exe/*.exe files cp exe/*.exe files
- name: Upload to Kaspersky - name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@v4.3.4 uses: SamKirkland/FTP-Deploy-Action@4.3.3
with: with:
protocol: ftps protocol: ftps
server: allowlist.kaspersky-labs.com server: allowlist.kaspersky-labs.com
@@ -368,7 +341,7 @@ jobs:
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }} password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
local-dir: files/ local-dir: files/
- name: Upload to Avast - name: Upload to Avast
uses: SamKirkland/FTP-Deploy-Action@v4.3.4 uses: SamKirkland/FTP-Deploy-Action@4.3.0
with: with:
protocol: ftp protocol: ftp
server: whitelisting.avast.com server: whitelisting.avast.com
+1 -2
View File
@@ -53,10 +53,9 @@
<option name="KEEP_LINE_BREAKS" value="false" /> <option name="KEEP_LINE_BREAKS" value="false" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" /> <option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" /> <option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" /> <option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="ENUM_CONSTANTS_WRAP" value="2" /> <option name="ENUM_CONSTANTS_WRAP" value="2" />
<indentOptions> <indentOptions>
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />
+1 -1
View File
@@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" 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" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application"> <configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs> </envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs> </envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&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"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
-1
View File
@@ -31,7 +31,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
<tbody> <tbody>
<tr> <tr>
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="40"></a></td> <td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="40"></a></td>
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td> <td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
</tr> </tr>
</tbody> </tbody>
+10 -15
View File
@@ -16,6 +16,7 @@ mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile # compile
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
cp ../../../LICENSE.txt ../../../target cp ../../../LICENSE.txt ../../../target
cp ../launcher.sh ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods cp ../../../target/cryptomator-*.jar ../../../target/mods
# add runtime # add runtime
@@ -23,7 +24,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \ --verbose \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods" \ --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 \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -43,21 +44,19 @@ ${JAVA_HOME}/bin/jpackage \
--name Cryptomator \ --name Cryptomator \
--vendor "Skymatic GmbH" \ --vendor "Skymatic GmbH" \
--java-options "--enable-preview" \ --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" \ --copyright "(C) 2016 - 2023 Skymatic GmbH" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \ --app-version "${VERSION}.${REVISION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \ --java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \ --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \ --java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \ --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \ --java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \ --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \ --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \ --java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \ --add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources --resource-dir ../resources
@@ -69,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.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.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.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.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/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 cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
@@ -66,16 +66,6 @@
</content_rating> </content_rating>
<releases> <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"/>
<release date="2023-03-15" version="1.7.3"/> <release date="2023-03-15" version="1.7.3"/>
<release date="2023-03-07" version="1.7.2"/> <release date="2023-03-07" version="1.7.2"/>
<release date="2023-03-03" version="1.7.1"/> <release date="2023-03-03" version="1.7.1"/>
@@ -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> Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils Section: utils
Priority: optional Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, 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 Standards-Version: 4.5.0
Homepage: https://cryptomator.org Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git Vcs-Git: https://github.com/cryptomator/cryptomator.git
-2
View File
@@ -1,8 +1,6 @@
cryptomator usr/lib cryptomator usr/lib
common/org.cryptomator.Cryptomator.desktop usr/share/applications common/org.cryptomator.Cryptomator.desktop usr/share/applications
common/org.cryptomator.Cryptomator.svg usr/share/icons/hicolor/scalable/apps 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.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
-2
View File
@@ -1,3 +1 @@
usr/lib/cryptomator/bin/cryptomator usr/bin/cryptomator 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. # Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1 #export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64) ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
@@ -27,7 +27,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \ $(JAVA_HOME)/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JMODS_PATH}" \ --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 \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -43,23 +43,20 @@ override_dh_auto_build:
--name cryptomator \ --name cryptomator \
--vendor "Skymatic GmbH" \ --vendor "Skymatic GmbH" \
--java-options "--enable-preview" \ --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" \ --copyright "(C) 2016 - 2023 Skymatic GmbH" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \ --java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \ --java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \ --java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \ --java-options "-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \ --java-options "-Dcryptomator.p12Path=\"~/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \ --java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \ --java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \ --java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \ --java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \ --java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \ --app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \ --resource-dir resources \
--verbose --verbose
+8 -33
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'` 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" 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 # check preconditions
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi 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; } command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; }
@@ -46,22 +38,6 @@ 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 if [[ ! `security find-identity -v -p codesigning | grep -w "${CODESIGN_IDENTITY}"` ]]; then echo "Given codesign identity is invalid."; exit 1; fi
fi fi
# download and check jmods
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
mkdir -p openjfx-jmods/
unzip -j 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 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 # compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
@@ -69,8 +45,8 @@ cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime # add runtime
${JAVA_HOME}/bin/jlink \ ${JAVA_HOME}/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \ --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 \ --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 \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -95,17 +71,16 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \ --java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Djava.net.useSystemProxies=true" \
--java-options "-Dapple.awt.enableTemplateImages=true" \ --java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dsun.java2d.metal=true" \ --java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \ --java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \ --java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \ --java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \ --java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \ --java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \ --java-options "-Dcryptomator.p12Path=\"~/Library/Application Support/${APP_NAME}/key.p12\"" \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \ --java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \ --java-options "-Dcryptomator.mountPointsDir=\"~/${APP_NAME}\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \ --java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \ --mac-package-identifier ${PACKAGE_IDENTIFIER} \
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 MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
SET LOOPBACK_ALIAS="cryptomator-vault" SET LOOPBACK_ALIAS="cryptomator-vault"
powershell -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^ powershell -NoLogo -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^ -AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^ -MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^ -ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
+12 -33
View File
@@ -12,9 +12,6 @@ Param(
[bool] $clean [bool] $clean
) )
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
# check preconditions # check preconditions
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null) if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
{ {
@@ -29,7 +26,7 @@ if ((Get-Command "mvn" -ErrorAction SilentlyContinue) -eq $null)
$buildDir = Split-Path -Parent $PSCommandPath $buildDir = Split-Path -Parent $PSCommandPath
$version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout) $version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.version" -q -DforceStdout)
$semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1' $semVerNo = $version -replace '(\d\.\d\.\d).*','$1'
$revisionNo = $(git rev-list --count HEAD) $revisionNo = $(git rev-list --count HEAD)
Write-Output "`$version=$version" Write-Output "`$version=$version"
@@ -50,30 +47,11 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
Remove-Item -Path $runtimeImagePath -Force -Recurse 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 -DestinationPath ".\resources\"
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
& "$Env:JAVA_HOME\bin\jlink" ` & "$Env:JAVA_HOME\bin\jlink" `
--verbose ` --verbose `
--output runtime ` --output runtime `
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" ` --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,javafx.base,javafx.graphics,javafx.controls,javafx.fxml ` --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 `
--strip-native-commands ` --strip-native-commands `
--no-header-files ` --no-header-files `
--no-man-pages ` --no-man-pages `
@@ -104,16 +82,15 @@ if ($clean -and (Test-Path -Path $appPath)) {
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" ` --java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" ` --app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" ` --java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Djava.net.useSystemProxies=true" ` --java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
--java-options "-Dcryptomator.logDir=`"@{localappdata}/$AppName`"" ` --java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
--java-options "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`"" ` --java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`"" ` --java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`"" ` --java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`"" ` --java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`"" `
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" ` --java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" ` --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$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.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" ` --java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources ` --resource-dir resources `
@@ -174,6 +151,8 @@ $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges" "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# download Winfsp # download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value $winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
Write-Output "Downloading ${winfspMsiUrl}..." Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
+1 -1
View File
@@ -3,5 +3,5 @@
::REPLACE ME ::REPLACE ME
cd %~dp0 cd %~dp0
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^ powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS% -LoopbackAlias %LOOPBACK_ALIAS%
+1 -1
View File
@@ -2,4 +2,4 @@
:: see comments in file ./version170-migrate-settings.ps1 :: see comments in file ./version170-migrate-settings.ps1
cd %~dp0 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)" /> <CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?> <?endif?>
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
<?ifndef SkipCryptomatorLegacyCheck ?> <?ifndef SkipCryptomatorLegacyCheck ?>
<!-- Block installation if innosetup entry of Cryptomator is found --> <!-- Block installation if innosetup entry of Cryptomator is found -->
<Property Id="OLDEXEINSTALLER"> <Property Id="OLDEXEINSTALLER">
@@ -132,17 +129,11 @@
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" /> <CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
<?endif?> <?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 --> <!-- WebDAV patches -->
<SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot;" <CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
Sequence="execute" Before="PatchWebDAV" />
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1--> <!-- 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;" <CustomAction Id="V170MigrateSettings" Impersonate="no" ExeCommand="[INSTALLDIR]version170-migrate-settings.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
Sequence="execute" Before="V170MigrateSettings" />
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Running App detection and exit --> <!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/> <Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
@@ -181,12 +172,11 @@
<?endif?> <?endif?>
<?ifndef JpAllowUpgrades ?> <?ifndef JpAllowUpgrades ?>
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom> <Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
<?endif?> <?endif?>
<?ifndef JpAllowDowngrades ?> <?ifndef JpAllowDowngrades ?>
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom> <Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
<?endif?> <?endif?>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
<!-- Check and fail if Cryptomator is running --> <!-- Check and fail if Cryptomator is running -->
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom> <Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
@@ -198,10 +188,6 @@
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom> <Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
</InstallExecuteSequence> </InstallExecuteSequence>
<InstallUISequence>
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</InstallUISequence>
<WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" /> <WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" /> <WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
</Product> </Product>
+73 -62
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId> <artifactId>cryptomator</artifactId>
<version>1.10.1</version> <version>1.8.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name> <name>Cryptomator Desktop App</name>
<organization> <organization>
@@ -26,52 +26,45 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>20</project.jdk.version> <project.jdk.version>19</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now --> <!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) --> <!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds> <nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.6.7</cryptomator.cryptofs.version> <cryptomator.cryptofs.version>2.6.2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version> <cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.2.3</cryptomator.integrations.win.version> <cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.3.0</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.2.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>3.0.0</cryptomator.fuse.version> <cryptomator.fuse.version>2.1.0-SNAPSHOT</cryptomator.fuse.version>
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version> <cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
<cryptomator.webdav.version>2.0.4</cryptomator.webdav.version> <cryptomator.webdav.version>2.0.2</cryptomator.webdav.version>
<!-- 3rd party dependencies --> <!-- 3rd party dependencies -->
<commons-lang3.version>3.13.0</commons-lang3.version> <commons-lang3.version>3.12.0</commons-lang3.version>
<dagger.version>2.48</dagger.version> <dagger.version>2.45</dagger.version>
<easybind.version>2.2</easybind.version> <easybind.version>2.2</easybind.version>
<guava.version>32.1.2-jre</guava.version> <guava.version>31.1-jre</guava.version>
<jackson.version>2.15.2</jackson.version> <gson.version>2.10.1</gson.version>
<javafx.version>20.0.2</javafx.version> <javafx.version>19.0.2.1</javafx.version>
<jwt.version>4.4.0</jwt.version> <jwt.version>4.3.0</jwt.version>
<nimbus-jose.version>9.31</nimbus-jose.version> <nimbus-jose.version>9.31</nimbus-jose.version>
<logback.version>1.4.11</logback.version> <logback.version>1.4.5</logback.version>
<slf4j.version>2.0.9</slf4j.version> <slf4j.version>2.0.6</slf4j.version>
<tinyoauth2.version>0.6.0</tinyoauth2.version> <tinyoauth2.version>0.5.1</tinyoauth2.version>
<zxcvbn.version>1.8.2</zxcvbn.version> <zxcvbn.version>1.7.0</zxcvbn.version>
<!-- test dependencies --> <!-- test dependencies -->
<junit.jupiter.version>5.10.0</junit.jupiter.version> <junit.jupiter.version>5.9.2</junit.jupiter.version>
<mockito.version>5.5.0</mockito.version> <mockito.version>5.1.1</mockito.version>
<hamcrest.version>2.2</hamcrest.version> <hamcrest.version>2.2</hamcrest.version>
<!-- build-time dependencies --> <!-- build-time dependencies -->
<jetbrains.annotations.version>24.0.1</jetbrains.annotations.version> <jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>8.4.0</dependency-check.version> <dependency-check.version>8.1.0</dependency-check.version>
<jacoco.version>0.8.10</jacoco.version> <jacoco.version>0.8.8</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>
</properties> </properties>
<dependencies> <dependencies>
@@ -164,11 +157,6 @@
<artifactId>nimbus-jose-jwt</artifactId> <artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version> <version>${nimbus-jose.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- EasyBind --> <!-- EasyBind -->
<dependency> <dependency>
@@ -218,6 +206,11 @@
<artifactId>dagger</artifactId> <artifactId>dagger</artifactId>
<version>${dagger.version}</version> <version>${dagger.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- JUnit / Mockito / Hamcrest --> <!-- JUnit / Mockito / Hamcrest -->
<dependency> <dependency>
@@ -247,7 +240,7 @@
<dependency> <dependency>
<groupId>com.google.jimfs</groupId> <groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId> <artifactId>jimfs</artifactId>
<version>1.3.0</version> <version>1.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@@ -265,32 +258,32 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>${mvn-compiler.version}</version> <version>3.10.1</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId> <artifactId>maven-resources-plugin</artifactId>
<version>${mvn-resources.version}</version> <version>3.3.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<version>${mvn-dependency.version}</version> <version>3.3.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>${mvn-surefire.version}</version> <version>3.0.0-M7</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId> <artifactId>license-maven-plugin</artifactId>
<version>${license-generator.version}</version> <version>2.0.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>${mvn-jar.version}</version> <version>3.3.0</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
@@ -324,6 +317,41 @@
</compilerArgs> </compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>compile-light-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/light_theme.css</arg>
<arg>${project.build.outputDirectory}/css/light_theme.bss</arg>
</arguments>
</configuration>
</execution>
<execution>
<id>compile-dark-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/dark_theme.css</arg>
<arg>${project.build.outputDirectory}/css/dark_theme.bss</arg>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -339,23 +367,6 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <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>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
+1 -24
View File
@@ -1,17 +1,4 @@
import ch.qos.logback.classic.spi.Configurator; import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.GoogleDriveLocationPresetsProvider;
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.LeitzcloudLocationPresetsProvider;
import org.cryptomator.common.locationpresets.LocationPresetsProvider;
import org.cryptomator.common.locationpresets.MegaLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.integrations.tray.TrayMenuController; import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory; import org.cryptomator.logging.LogbackConfiguratorFactory;
import org.cryptomator.ui.traymenu.AwtTrayMenuController; import org.cryptomator.ui.traymenu.AwtTrayMenuController;
@@ -38,7 +25,7 @@ open module org.cryptomator.desktop {
requires ch.qos.logback.core; requires ch.qos.logback.core;
requires com.auth0.jwt; requires com.auth0.jwt;
requires com.google.common; requires com.google.common;
requires com.fasterxml.jackson.databind; requires com.google.gson;
requires com.nimbusds.jose.jwt; requires com.nimbusds.jose.jwt;
requires com.nulabinc.zxcvbn; requires com.nulabinc.zxcvbn;
requires com.tobiasdiez.easybind; requires com.tobiasdiez.easybind;
@@ -50,16 +37,6 @@ open module org.cryptomator.desktop {
/* TODO: filename-based modules: */ /* TODO: filename-based modules: */
requires static javax.inject; /* ugly dagger/guava crap */ requires static javax.inject; /* ugly dagger/guava crap */
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
provides TrayMenuController with AwtTrayMenuController; provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory; provides Configurator with LogbackConfiguratorFactory;
provides LocationPresetsProvider with //
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
GoogleDriveLocationPresetsProvider, //
ICloudWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
LeitzcloudLocationPresetsProvider, //
MegaLocationPresetsProvider, //
OneDriveWindowsLocationPresetsProvider, OneDriveMacLocationPresetsProvider, OneDriveLinuxLocationPresetsProvider, //
PCloudLocationPresetsProvider;
} }
@@ -139,9 +139,9 @@ public abstract class CommonsModule {
@Provides @Provides
@Singleton @Singleton
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) { static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
return settings.port.map(port -> { return settings.port().map(port -> {
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost"; String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
return InetSocketAddress.createUnresolved(host, settings.port.intValue()); return InetSocketAddress.createUnresolved(host, settings.port().intValue());
}); });
} }
@@ -1,9 +1,5 @@
package org.cryptomator.common; package org.cryptomator.common;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import java.net.URI;
public interface Constants { public interface Constants {
String MASTERKEY_FILENAME = "masterkey.cryptomator"; String MASTERKEY_FILENAME = "masterkey.cryptomator";
@@ -11,7 +7,6 @@ public interface Constants {
String VAULTCONFIG_FILENAME = "vault.cryptomator"; String VAULTCONFIG_FILENAME = "vault.cryptomator";
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator"; String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator"; String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
byte[] PEPPER = new byte[0]; byte[] PEPPER = new byte[0];
} }
@@ -2,7 +2,6 @@ package org.cryptomator.common;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -19,6 +18,8 @@ import java.util.stream.StreamSupport;
public class Environment { public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class); private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final Path RELATIVE_HOME_DIR = Paths.get("~");
private static final char PATH_LIST_SEP = ':';
private static final int DEFAULT_MIN_PW_LENGTH = 8; private static final int DEFAULT_MIN_PW_LENGTH = 8;
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath"; private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath"; private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
@@ -32,7 +33,6 @@ public class Environment {
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber"; private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir"; private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon"; private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
private Environment() {} private Environment() {}
@@ -45,16 +45,15 @@ public class Environment {
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME); logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME); logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME); logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME); logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME); logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME); logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME); logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME); logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME); logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME); logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME); logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
} }
public static Environment getInstance() { public static Environment getInstance() {
@@ -77,7 +76,11 @@ public class Environment {
return getPaths(SETTINGS_PATH_PROP_NAME); 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); return getPaths(IPC_SOCKET_PATH_PROP_NAME);
} }
@@ -85,24 +88,20 @@ public class Environment {
return getPaths(KEYCHAIN_PATHS_PROP_NAME); return getPaths(KEYCHAIN_PATHS_PROP_NAME);
} }
public Stream<Path> getP12Path() {
return getPaths(P12_PATH_PROP_NAME);
}
public Optional<Path> getLogDir() { public Optional<Path> getLogDir() {
return getPath(LOG_DIR_PROP_NAME); return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
} }
public Optional<String> getLoopbackAlias() { public Optional<String> getLoopbackAlias() {
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME)); return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
} }
public Optional<Path> getMountPointsDir() { public Optional<Path> getPluginDir() {
return getPath(MOUNTPOINT_DIR_PROP_NAME); return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
} }
public int getMinPwLength() { public Optional<Path> getMountPointsDir() {
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH); 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)); return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
} }
public Optional<Path> getPluginDir() { public int getMinPwLength() {
return getPath(PLUGIN_DIR_PROP_NAME); return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
} }
public boolean showTrayIcon() { public boolean showTrayIcon() {
return Boolean.getBoolean(TRAY_ICON_PROP_NAME); return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
} }
public boolean disableUpdateCheck() {
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
}
private Optional<Path> getPath(String propertyName) { private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName); String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get); return Optional.ofNullable(value).map(Paths::get);
} }
@VisibleForTesting // visible for testing
Stream<Path> getPaths(String propertyName) { public Path getHomeDir() {
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0)); return getPath("user.home").orElseThrow();
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of); }
// visible for testing
public Stream<Path> getPaths(String propertyName) {
Stream<String> rawSettingsPaths = getRawList(propertyName, PATH_LIST_SEP);
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Paths::get).map(this::replaceHomeDir);
}
private Path replaceHomeDir(Path path) {
if (path.startsWith(RELATIVE_HOME_DIR)) {
return getHomeDir().resolve(RELATIVE_HOME_DIR.relativize(path));
} else {
return path;
}
} }
private Stream<String> getRawList(String propertyName, char separator) { private Stream<String> getRawList(String propertyName, char separator) {
@@ -3,7 +3,6 @@ package org.cryptomator.common;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
@@ -33,15 +32,18 @@ public class ErrorCode {
this.rootCauseSpecificFrames = rootCauseSpecificFrames; this.rootCauseSpecificFrames = rootCauseSpecificFrames;
} }
public String methodCode() { // visible for testing
String methodCode() {
return format(traceCode(rootCause, LATEST_FRAME)); return format(traceCode(rootCause, LATEST_FRAME));
} }
public String rootCauseCode() { // visible for testing
String rootCauseCode() {
return format(traceCode(rootCause, rootCauseSpecificFrames)); return format(traceCode(rootCause, rootCauseSpecificFrames));
} }
public String throwableCode() { // visible for testing
String throwableCode() {
return format(traceCode(throwable, ALL_FRAMES)); 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> * @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. * @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) { static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
if (allFrames.length < bottomFrames.length) { if (allFrames.length < bottomFrames.length) {
// if frames had been stacked on top of bottomFrames, allFrames would be larger // 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) { static <T> int commonSuffixLength(T[] set, T[] subset) {
Preconditions.checkArgument(set.length >= subset.length); Preconditions.checkArgument(set.length >= subset.length);
// iterate items backwards as long as they are identical // iterate items backwards as long as they are identical
@@ -30,7 +30,7 @@ public class LicenseHolder {
this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject); this.licenseSubject = validJwtClaims.map(DecodedJWT::getSubject);
this.validLicenseProperty = validJwtClaims.isNotNull(); this.validLicenseProperty = validJwtClaims.isNotNull();
Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey.get()); Optional<DecodedJWT> claims = licenseChecker.check(settings.licenseKey().get());
validJwtClaims.set(claims.orElse(null)); validJwtClaims.set(claims.orElse(null));
} }
@@ -38,7 +38,7 @@ public class LicenseHolder {
Optional<DecodedJWT> claims = licenseChecker.check(licenseKey); Optional<DecodedJWT> claims = licenseChecker.check(licenseKey);
validJwtClaims.set(claims.orElse(null)); validJwtClaims.set(claims.orElse(null));
if (claims.isPresent()) { if (claims.isPresent()) {
settings.licenseKey.set(licenseKey); settings.licenseKey().set(licenseKey);
return true; return true;
} else { } else {
return false; return false;
@@ -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;
}
}
}
}
@@ -2,9 +2,7 @@ package org.cryptomator.common;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
public class ObservableUtil { public class ObservableUtil {
@@ -17,14 +15,4 @@ public class ObservableUtil {
} }
}, observable); }, observable);
} }
public static <T, U> ObservableValue<U> mapWithDefault(ObservableValue<T> observable, Function<? super T, ? extends U> mapper, Supplier<U> defaultValue) {
return Bindings.createObjectBinding(() -> {
if (observable.getValue() == null) {
return defaultValue.get();
} else {
return mapper.apply(observable.getValue());
}
}, observable);
}
} }
@@ -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 @Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) { static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> { return Bindings.createObjectBinding(() -> {
if (!settings.useKeychain.get()) { if (!settings.useKeychain().get()) {
return null; return null;
} }
var selectedProviderClass = settings.keychainProvider.get(); var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny(); var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findFirst().orElse(null); var fallbackProvider = providers.stream().findFirst().orElse(null);
return selectedProvider.orElse(fallbackProvider); return selectedProvider.orElse(fallbackProvider);
}, settings.keychainProvider, settings.useKeychain); }, settings.keychainProvider(), settings.useKeychain());
} }
} }
@@ -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.UTF_8).lines().filter(outputFilter);
}
/**
* Waits {@code timeoutSeconds} seconds for {@code process} to finish with exit code {@code 0}.
*
* @param process The process to wait for
* @param timeoutSeconds How long to wait (in seconds)
* @param cmdDescription A short description of the process used to generate log and exception messages
* @throws TimeoutException Thrown when the process doesn't finish in time
* @throws InterruptedException Thrown when the thread is interrupted while waiting for the process to finish
* @throws CommandFailedException Thrown when the process exit code is non-zero
*/
@Blocking
private static void waitForSuccess(Process process, int timeoutSeconds, String cmdDescription) throws TimeoutException, InterruptedException, CommandFailedException {
boolean exited = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
if (!exited) {
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
}
if (process.exitValue() != 0) {
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.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; 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 { public class IllegalMountPointException extends IllegalArgumentException {
private final Path mountpoint; public IllegalMountPointException(String msg) {
public IllegalMountPointException(Path mountpoint) {
this(mountpoint, "The provided mountpoint has a problem: " + mountpoint.toString());
}
public IllegalMountPointException(Path mountpoint, String msg) {
super(msg); super(msg);
this.mountpoint = mountpoint;
} }
public Path getMountpoint() { }
return mountpoint;
}
}
@@ -2,71 +2,50 @@ package org.cryptomator.common.mount;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.mount.Mount;
import org.cryptomator.integrations.mount.MountService; import org.cryptomator.integrations.mount.MountService;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@Module @Module
public class MountModule { public class MountModule {
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
@Provides @Provides
@Singleton @Singleton
static List<MountService> provideSupportedMountServices() { static List<MountService> provideSupportedMountServices() {
return MountService.get().toList(); return MountService.get().toList();
} }
//currently not used, because macFUSE and FUSE-T cannot be used in the same JVM
/*
@Provides @Provides
@Singleton @Singleton
@Named("FUPFMS") static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls) {
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
return new AtomicReference<>(null);
}
@Provides
@Singleton
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null); var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
return ObservableUtil.mapWithDefault(settings.mountService(), //
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
desiredServiceImpl -> { // desiredServiceImpl -> { //
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); // var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
var targetedService = serviceFromSettings.orElse(fallbackProvider); return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
}, // }, //
() -> { // new ActualMountService(fallbackProvider, true));
return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms); }
}); */
return observableMountService;
@Provides
@Singleton
static ActualMountService provideActualMountService(Settings settings, List<MountService> serviceImpls) {
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(settings.mountService().getValue())).findFirst(); //
return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); //
} }
//see https://github.com/cryptomator/cryptomator/issues/2786 @Provides
private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) { @Singleton
//set the first used problematic fuse service if applicable static ObservableValue<ActualMountService> provideMountService(ActualMountService service) {
var targetIsProblematicFuse = isProblematicFuseService(targetedService); return new SimpleObjectProperty<>(service);
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
firstUsedProblematicFuseMountService.set(targetedService);
}
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
return new ActualMountService(formerSelectedMountService.get(), false);
} else {
formerSelectedMountService.set(targetedService);
return new ActualMountService(targetedService, isDesired);
}
} }
public static boolean isProblematicFuseService(MountService service) {
return problematicFuseMountServices.contains(service.getClass().getName());
}
} }
@@ -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; package org.cryptomator.common.mount;
import java.nio.file.Path;
public class MountPointNotSupportedException extends IllegalMountPointException { public class MountPointNotSupportedException extends IllegalMountPointException {
public MountPointNotSupportedException(Path path, String msg) { public MountPointNotSupportedException(String msg) {
super(path, 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; package org.cryptomator.common.mount;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.LinkOption; import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public final class MountWithinParentUtil { public final class MountWithinParentUtil {
@@ -21,33 +22,31 @@ public final class MountWithinParentUtil {
private MountWithinParentUtil() {} private MountWithinParentUtil() {}
static void prepareParentNoMountPoint(Path mountPoint) throws IllegalMountPointException, IOException { static void prepareParentNoMountPoint(Path mountPoint) throws MountPointPreparationException {
Path hideaway = getHideaway(mountPoint); Path hideaway = getHideaway(mountPoint);
var mpState = getMountPointState(mountPoint); var mpExists = Files.exists(mountPoint, LinkOption.NOFOLLOW_LINKS);
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS); var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
if (mpState == MountPointState.BROKEN_JUNCTION) { //TODO: possible improvement by just deleting an _empty_ hideaway
LOG.info("Mountpoint \"{}\" is still a junction. Deleting it.", mountPoint); if (mpExists && hideExists) { //both resources exist (whatever type)
Files.delete(mountPoint); //Throws if mountPoint is also a non-empty folder throw new MountPointPreparationException(new FileAlreadyExistsException(hideaway.toString()));
mpState = MountPointState.NOT_EXISTING; } else if (!mpExists && !hideExists) { //neither mountpoint nor hideaway exist
} throw new MountPointPreparationException(new NoSuchFileException(mountPoint.toString()));
} else if (!mpExists) { //only hideaway exists
if (mpState == MountPointState.NOT_EXISTING && !hideExists) { //neither mountpoint nor hideaway exist checkIsDirectory(hideaway);
throw new MountPointNotExistingException(mountPoint);
} else if (mpState == MountPointState.NOT_EXISTING) { //only hideaway exists
checkIsHideawayDirectory(mountPoint, hideaway);
LOG.info("Mountpoint {} seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint); 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 { try {
if (hideExists) { //... with hideaway if (SystemUtils.IS_OS_WINDOWS) {
removeResidualHideaway(mountPoint, hideaway); 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); Files.move(mountPoint, hideaway);
if (SystemUtils.IS_OS_WINDOWS) { if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS); Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
@@ -55,66 +54,30 @@ public final class MountWithinParentUtil {
int attempts = 0; int attempts = 0;
while (!Files.notExists(mountPoint)) { while (!Files.notExists(mountPoint)) {
if (attempts >= 10) { if (attempts >= 10) {
throw new MountPointCleanupFailedException(mountPoint); throw new MountPointPreparationException("Path " + mountPoint + " could not be cleared");
} }
Thread.sleep(1000); Thread.sleep(1000);
attempts++; attempts++;
} }
} catch (IOException e) {
throw new MountPointPreparationException(e);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); 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) { static void cleanup(Path mountPoint) {
Path hideaway = getHideaway(mountPoint); Path hideaway = getHideaway(mountPoint);
try { try {
waitForMountpointRestoration(mountPoint); 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); Files.move(hideaway, mountPoint);
if (SystemUtils.IS_OS_WINDOWS) { if (SystemUtils.IS_OS_WINDOWS) {
Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false); Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false);
} }
} catch (IOException e) { } 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)) { 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) { private static void checkIsEmpty(Path toCheck) throws MountPointPreparationException, IOException {
if (!Files.isDirectory(hideawayToCheck, LinkOption.NOFOLLOW_LINKS)) {
throw new HideawayNotDirectoryException(mountPoint, hideawayToCheck);
}
}
private static void checkIsMountPointEmpty(Path toCheck) throws IllegalMountPointException, IOException {
try (var dirStream = Files.list(toCheck)) { try (var dirStream = Files.list(toCheck)) {
if (dirStream.findFirst().isPresent()) { 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) { static Path getHideaway(Path mountPoint) {
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX); return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
} }
@@ -54,19 +54,25 @@ public class Mounter {
switch (capability) { switch (capability) {
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs"); case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
case LOOPBACK_PORT -> case LOOPBACK_PORT ->
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault) builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName); case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get()); case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
case MOUNT_FLAGS -> { case MOUNT_FLAGS -> {
var mountFlags = vaultSettings.mountFlags.get(); var mountFlags = vaultSettings.mountFlags().get();
if (mountFlags == null || mountFlags.isBlank()) { if (mountFlags == null || mountFlags.isBlank()) {
builder.setMountFlags(service.getDefaultMountFlags()); builder.setMountFlags(service.getDefaultMountFlags());
} else { } else {
builder.setMountFlags(mountFlags); builder.setMountFlags(mountFlags);
} }
} }
case VOLUME_ID -> builder.setVolumeId(vaultSettings.id); case VOLUME_ID -> {
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName.get()); if(vaultSettings.usesVaultIdAsVolumeId().get()) {
builder.setVolumeId(vaultSettings.getId()+"\\test");
} else {
builder.setVolumeId(vaultSettings.mountName().get());
}
}
case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
} }
} }
@@ -75,7 +81,7 @@ public class Mounter {
private Runnable prepareMountPoint() throws IOException { private Runnable prepareMountPoint() throws IOException {
Runnable cleanup = () -> {}; Runnable cleanup = () -> {};
var userChosenMountPoint = vaultSettings.mountPoint.get(); var userChosenMountPoint = vaultSettings.getMountPoint();
var defaultMountPointBase = env.getMountPointsDir().orElseThrow(); var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER); var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT); var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
@@ -91,17 +97,13 @@ public class Mounter {
Files.createDirectories(defaultMountPointBase); Files.createDirectories(defaultMountPointBase);
builder.setMountpoint(defaultMountPointBase); builder.setMountpoint(defaultMountPointBase);
} else if (canMountToDir) { } else if (canMountToDir) {
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName.get()); var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
Files.createDirectories(mountPoint); Files.createDirectories(mountPoint);
builder.setMountpoint(mountPoint); builder.setMountpoint(mountPoint);
} }
} else { } else {
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\"); var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
if (mpIsDriveLetter) { if (!mpIsDriveLetter && canMountToParent && !canMountToDir) {
if (driveLetters.getOccupied().contains(userChosenMountPoint)) {
throw new MountPointInUseException(userChosenMountPoint);
}
} else if (canMountToParent && !canMountToDir) {
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint); MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
cleanup = () -> { cleanup = () -> {
MountWithinParentUtil.cleanup(userChosenMountPoint); MountWithinParentUtil.cleanup(userChosenMountPoint);
@@ -115,13 +117,13 @@ public class Mounter {
|| (!canMountToParent && !mpIsDriveLetter) // || (!canMountToParent && !mpIsDriveLetter) //
|| (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter); || (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter);
if (configNotSupported) { if (configNotSupported) {
throw new MountPointNotSupportedException(userChosenMountPoint, e.getMessage()); throw new MountPointNotSupportedException(e.getMessage());
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) { } else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
//mountpoint must exist //mountpoint must exist
throw new MountPointNotExistingException(userChosenMountPoint, e.getMessage()); throw new MountPointNotExistsException(e.getMessage());
} else { } else {
//TODO: add specific exception for !canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint) //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.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
@@ -29,86 +27,59 @@ import java.util.function.Consumer;
public class Settings { public class Settings {
private static final Logger LOG = LoggerFactory.getLogger(Settings.class); public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false; public static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
static final boolean DEFAULT_CHECK_FOR_UPDATES = false; public static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
static final boolean DEFAULT_START_HIDDEN = false; public static final boolean DEFAULT_START_HIDDEN = false;
static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false; public static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
static final boolean DEFAULT_USE_KEYCHAIN = true; public static final boolean DEFAULT_USE_KEYCHAIN = true;
static final int DEFAULT_PORT = 42427; public static final int DEFAULT_PORT = 42427;
static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3; public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
static final boolean DEFAULT_DEBUG_MODE = false; public static final boolean DEFAULT_DEBUG_MODE = false;
static final UiTheme DEFAULT_THEME = UiTheme.LIGHT; public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually @Deprecated // to be changed to "whatever is available" eventually
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 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(); public static final NodeOrientation DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT;
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false; public static final String DEFAULT_LICENSE_KEY = "";
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01"; 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; 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 * Package-private constructor; use {@link SettingsProvider}.
*
* @param json The parsed settings.json
*/ */
Settings(SettingsJson json) { Settings(Environment env) {
this.directories = FXCollections.observableArrayList(VaultSettings::observables); this.showTrayIcon = new SimpleBooleanProperty(env.showTrayIcon());
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);
directories.addListener(this::somethingChanged); directories.addListener(this::somethingChanged);
askedForUpdateCheck.addListener(this::somethingChanged); askedForUpdateCheck.addListener(this::somethingChanged);
@@ -129,77 +100,11 @@ public class Settings {
windowYPosition.addListener(this::somethingChanged); windowYPosition.addListener(this::somethingChanged);
windowWidth.addListener(this::somethingChanged); windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged); windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged); language.addListener(this::somethingChanged);
mountService.addListener(this::somethingChanged); mountService.addListener(this::somethingChanged);
lastUpdateCheck.addListener(this::somethingChanged);
} }
@SuppressWarnings("deprecation")
private void migrateLegacySettings(SettingsJson json) {
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
this.mountService.set(switch (json.preferredVolumeImpl) {
case "Dokany" -> "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
case "FUSE" -> {
if (SystemUtils.IS_OS_WINDOWS) {
yield "org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider";
} else if (SystemUtils.IS_OS_MAC) {
yield "org.cryptomator.frontend.fuse.mount.MacFuseMountProvider";
} else {
yield "org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider";
}
}
default -> {
if (SystemUtils.IS_OS_WINDOWS) {
yield "org.cryptomator.frontend.webdav.mount.WindowsMounter";
} else if (SystemUtils.IS_OS_MAC) {
yield "org.cryptomator.frontend.webdav.mount.MacAppleScriptMounter";
} else {
yield "org.cryptomator.frontend.webdav.mount.LinuxGioMounter";
}
}
});
}
}
SettingsJson serialized() {
var json = new SettingsJson();
json.directories = directories.stream().map(VaultSettings::serialized).toList();
json.askedForUpdateCheck = askedForUpdateCheck.get();
json.checkForUpdatesEnabled = checkForUpdates.get();
json.startHidden = startHidden.get();
json.autoCloseVaults = autoCloseVaults.get();
json.useKeychain = useKeychain.get();
json.port = port.get();
json.numTrayNotifications = numTrayNotifications.get();
json.debugMode = debugMode.get();
json.theme = theme.get();
json.keychainProvider = keychainProvider.get();
json.uiOrientation = userInterfaceOrientation.get().name();
json.licenseKey = licenseKey.get();
json.showMinimizeButton = showMinimizeButton.get();
json.showTrayIcon = showTrayIcon.get();
json.windowXPosition = windowXPosition.get();
json.windowYPosition = windowYPosition.get();
json.windowWidth = windowWidth.get();
json.windowHeight = windowHeight.get();
json.language = language.get();
json.mountService = mountService.get();
json.lastUpdateCheck = lastUpdateCheck.get();
return json;
}
private <E extends Enum<E>> E parseEnum(String value, Class<E> clazz, E defaultValue) {
try {
return Enum.valueOf(clazz, value.toUpperCase());
} catch (IllegalArgumentException e) {
LOG.warn("No value {}.{}. Defaulting to {}.", clazz.getSimpleName(), value, defaultValue);
return defaultValue;
}
}
// TODO rename to setChangeListener
void setSaveCmd(Consumer<Settings> saveCmd) { void setSaveCmd(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd; this.saveCmd = saveCmd;
} }
@@ -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; package org.cryptomator.common.settings;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -19,7 +22,12 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -36,7 +44,6 @@ import java.util.stream.Stream;
@Singleton @Singleton
public class SettingsProvider implements Supplier<Settings> { public class SettingsProvider implements Supplier<Settings> {
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class); private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
private static final long SAVE_DELAY_MS = 1000; private static final long SAVE_DELAY_MS = 1000;
@@ -44,11 +51,16 @@ public class SettingsProvider implements Supplier<Settings> {
private final Supplier<Settings> settings = Suppliers.memoize(this::load); private final Supplier<Settings> settings = Suppliers.memoize(this::load);
private final Environment env; private final Environment env;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Gson gson;
@Inject @Inject
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) { public SettingsProvider(SettingsJsonAdapter settingsJsonAdapter, Environment env, ScheduledExecutorService scheduler) {
this.env = env; this.env = env;
this.scheduler = scheduler; this.scheduler = scheduler;
this.gson = new GsonBuilder() //
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
.create();
} }
@Override @Override
@@ -57,25 +69,28 @@ public class SettingsProvider implements Supplier<Settings> {
} }
private Settings load() { 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); settings.setSaveCmd(this::scheduleSave);
return settings; return settings;
} }
private Stream<Settings> tryLoad(Path path) { private Stream<Settings> tryLoad(Path path) {
LOG.debug("Attempting to load settings from {}", path); LOG.debug("Attempting to load settings from {}", path);
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) { try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ); //
var json = JSON.reader().readValue(in, SettingsJson.class); Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
LOG.info("Settings loaded from {}", path); JsonElement json = JsonParser.parseReader(reader);
var settings = new Settings(json); if (json.isJsonObject()) {
return Stream.of(settings); Settings settings = gson.fromJson(json, Settings.class);
} catch (JacksonException e) { LOG.info("Settings loaded from {}", path);
LOG.warn("Failed to parse json file {}", path, e); return Stream.of(settings);
return Stream.empty(); } else {
LOG.warn("Invalid json file {}", path);
return Stream.empty();
}
} catch (NoSuchFileException e) { } catch (NoSuchFileException e) {
return Stream.empty(); return Stream.empty();
} catch (IOException e) { } catch (IOException | JsonParseException e) {
LOG.warn("Failed to load json file {}", path, e); LOG.warn("Exception while loading settings from " + path, e);
return Stream.empty(); return Stream.empty();
} }
} }
@@ -101,14 +116,13 @@ public class SettingsProvider implements Supplier<Settings> {
try { try {
Files.createDirectories(settingsPath.getParent()); Files.createDirectories(settingsPath.getParent());
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp"); Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); //
var jsonObj = settings.serialized(); Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse(""); gson.toJson(settings, writer);
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
} }
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING); Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
LOG.info("Settings saved to {}", settingsPath); LOG.info("Settings saved to {}", settingsPath);
} catch (IOException e) { } catch (IOException | JsonParseException e) {
LOG.error("Failed to save settings.", e); LOG.error("Failed to save settings.", e);
} }
} }
@@ -1,12 +1,9 @@
package org.cryptomator.common.settings; package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum UiTheme { public enum UiTheme {
@JsonEnumDefaultValue LIGHT("preferences.interface.theme.light"), // LIGHT("preferences.interface.theme.light"), //
DARK("preferences.interface.theme.dark"), // DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic"); AUTOMATIC("preferences.interface.theme.automatic");
@@ -6,10 +6,7 @@
package org.cryptomator.common.settings; package org.cryptomator.common.settings;
import com.google.common.base.CharMatcher; import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.VisibleForTesting;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@@ -23,7 +20,6 @@ import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
@@ -32,45 +28,34 @@ import java.util.Random;
*/ */
public class VaultSettings { public class VaultSettings {
static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false; public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true; public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
static final boolean DEFAULT_USES_READONLY_MODE = false; public 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 public static final String DEFAULT_MOUNT_FLAGS = "";
static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1; public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK; public static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false; public static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60; public static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
private static final Random RNG = new Random(); private static final Random RNG = new Random();
public final String id; private final String id;
public final ObjectProperty<Path> path; private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
public final StringProperty displayName; private final StringProperty displayName = new SimpleStringProperty();
public final BooleanProperty unlockAfterStartup; private final BooleanProperty vaultIdAsVolumeId = new SimpleBooleanProperty(false);
public final BooleanProperty revealAfterMount; private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
public final BooleanProperty usesReadOnlyMode; private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
public final StringProperty mountFlags; private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
public final IntegerProperty maxCleartextFilenameLength; private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS); //TODO: remove empty default mount flags and let this property be null if not used
public final ObjectProperty<WhenUnlocked> actionAfterUnlock; private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
public final BooleanProperty autoLockWhenIdle; private final ObjectProperty<WhenUnlocked> actionAfterUnlock = new SimpleObjectProperty<>(DEFAULT_ACTION_AFTER_UNLOCK);
public final IntegerProperty autoLockIdleSeconds; private final BooleanProperty autoLockWhenIdle = new SimpleBooleanProperty(DEFAULT_AUTOLOCK_WHEN_IDLE);
public final ObjectProperty<Path> mountPoint; private final IntegerProperty autoLockIdleSeconds = new SimpleIntegerProperty(DEFAULT_AUTOLOCK_IDLE_SECONDS);
public final StringExpression mountName; private final StringExpression mountName;
private final ObjectProperty<Path> mountPoint = new SimpleObjectProperty<>();
VaultSettings(VaultSettingsJson json) { public VaultSettings(String id) {
this.id = json.id; this.id = Objects.requireNonNull(id);
this.path = new SimpleObjectProperty<>(this, "path", json.path == null ? null : Paths.get(json.path));
this.displayName = new SimpleStringProperty(this, "displayName", json.displayName);
this.unlockAfterStartup = new SimpleBooleanProperty(this, "unlockAfterStartup", json.unlockAfterStartup);
this.revealAfterMount = new SimpleBooleanProperty(this, "revealAfterMount", json.revealAfterMount);
this.usesReadOnlyMode = new SimpleBooleanProperty(this, "usesReadOnlyMode", json.usesReadOnlyMode);
this.mountFlags = new SimpleStringProperty(this, "mountFlags", json.mountFlags);
this.maxCleartextFilenameLength = new SimpleIntegerProperty(this, "maxCleartextFilenameLength", json.maxCleartextFilenameLength);
this.actionAfterUnlock = new SimpleObjectProperty<>(this, "actionAfterUnlock", json.actionAfterUnlock);
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> { this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
final String name; final String name;
if (displayName.isEmpty().get()) { if (displayName.isEmpty().get()) {
@@ -80,18 +65,6 @@ public class VaultSettings {
} }
return normalizeDisplayName(name); return normalizeDisplayName(name);
}, displayName, path)); }, displayName, path));
migrateLegacySettings(json);
}
@SuppressWarnings("deprecation")
private void migrateLegacySettings(VaultSettingsJson json) {
// implicit migration of 1.6.x legacy setting "customMountPath" / "winDriveLetter":
if (json.useCustomMountPath && !Strings.isNullOrEmpty(json.customMountPath)) {
this.mountPoint.set(Path.of(json.customMountPath));
} else if (!Strings.isNullOrEmpty(json.winDriveLetter)) {
this.mountPoint.set(Path.of(json.winDriveLetter + ":\\"));
}
} }
Observable[] observables() { Observable[] observables() {
@@ -99,9 +72,7 @@ public class VaultSettings {
} }
public static VaultSettings withRandomId() { public static VaultSettings withRandomId() {
var defaults = new VaultSettingsJson(); return new VaultSettings(generateId());
defaults.id = generateId();
return new VaultSettings(defaults);
} }
private static String generateId() { private static String generateId() {
@@ -110,24 +81,7 @@ public class VaultSettings {
return BaseEncoding.base64Url().encode(randomBytes); return BaseEncoding.base64Url().encode(randomBytes);
} }
VaultSettingsJson serialized() { //visible for testing
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
static String normalizeDisplayName(String original) { static String normalizeDisplayName(String original) {
if (original.isBlank() || ".".equals(original) || "..".equals(original)) { if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
return "_"; return "_";
@@ -140,6 +94,72 @@ public class VaultSettings {
return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_'); return CharMatcher.anyOf("<>:\"/\\|?*").or(CharMatcher.javaIsoControl()).collapseFrom(withoutFancyWhitespaces, '_');
} }
/* Getter/Setter */
public String getId() {
return id;
}
public ObjectProperty<Path> path() {
return path;
}
public StringProperty displayName() {
return displayName;
}
public StringExpression mountName() {
return mountName;
}
public BooleanProperty unlockAfterStartup() {
return unlockAfterStartup;
}
public BooleanProperty revealAfterMount() {
return revealAfterMount;
}
public Path getMountPoint() {
return mountPoint.get();
}
public ObjectProperty<Path> mountPoint() {
return mountPoint;
}
public BooleanProperty usesVaultIdAsVolumeId() {
return vaultIdAsVolumeId;
}
public BooleanProperty usesReadOnlyMode() {
return usesReadOnlyMode;
}
public StringProperty mountFlags() {
return mountFlags;
}
public IntegerProperty maxCleartextFilenameLength() {
return maxCleartextFilenameLength;
}
public ObjectProperty<WhenUnlocked> actionAfterUnlock() {
return actionAfterUnlock;
}
public WhenUnlocked getActionAfterUnlock() {
return actionAfterUnlock.get();
}
public BooleanProperty autoLockWhenIdle() {
return autoLockWhenIdle;
}
public IntegerProperty autoLockIdleSeconds() {
return autoLockIdleSeconds;
}
/* Hashcode/Equals */ /* Hashcode/Equals */
@Override @Override
@@ -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; package org.cryptomator.common.settings;
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum WhenUnlocked { public enum WhenUnlocked {
IGNORE("vaultOptions.general.actionAfterUnlock.ignore"), IGNORE("vaultOptions.general.actionAfterUnlock.ignore"),
REVEAL("vaultOptions.general.actionAfterUnlock.reveal"), REVEAL("vaultOptions.general.actionAfterUnlock.reveal"),
@JsonEnumDefaultValue ASK("vaultOptions.general.actionAfterUnlock.ask"); ASK("vaultOptions.general.actionAfterUnlock.ask");
private String displayName; private String displayName;
@@ -50,8 +50,8 @@ public class AutoLocker {
private boolean exceedsIdleTime(Vault vault) { private boolean exceedsIdleTime(Vault vault) {
assert vault.isUnlocked(); assert vault.isUnlocked();
if (vault.getVaultSettings().autoLockWhenIdle.get()) { if (vault.getVaultSettings().autoLockWhenIdle().get()) {
int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds.get(); int maxIdleSeconds = vault.getVaultSettings().autoLockIdleSeconds().get();
var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds); var deadline = vault.getStats().getLastActivity().plusSeconds(maxIdleSeconds);
return deadline.isBefore(Instant.now()); return deadline.isBefore(Instant.now());
} else { } else {
@@ -70,7 +70,7 @@ public class Vault {
private final Mounter mounter; private final Mounter mounter;
private final BooleanProperty showingStats; private final BooleanProperty showingStats;
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null); private AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@Inject @Inject
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) { Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
@@ -80,7 +80,7 @@ public class Vault {
this.state = state; this.state = state;
this.lastKnownException = lastKnownException; this.lastKnownException = lastKnownException;
this.stats = stats; this.stats = stats;
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path); this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
this.locked = Bindings.createBooleanBinding(this::isLocked, state); this.locked = Bindings.createBooleanBinding(this::isLocked, state);
this.processing = Bindings.createBooleanBinding(this::isProcessing, state); this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state); this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
@@ -98,29 +98,29 @@ public class Vault {
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException { private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class); Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
if (vaultSettings.usesReadOnlyMode.get()) { if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY); flags.add(FileSystemFlags.READONLY);
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) { } else if (vaultSettings.maxCleartextFilenameLength().get() == -1) {
LOG.debug("Determining cleartext filename length limitations..."); LOG.debug("Determining cleartext filename length limitations...");
var checker = new FileSystemCapabilityChecker(); var checker = new FileSystemCapabilityChecker();
int shorteningThreshold = configCache.get().allegedShorteningThreshold(); int shorteningThreshold = configCache.get().allegedShorteningThreshold();
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath()); int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
if (ciphertextLimit < shorteningThreshold) { if (ciphertextLimit < shorteningThreshold) {
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath()); int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit); vaultSettings.maxCleartextFilenameLength().set(cleartextLimit);
} else { } else {
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH); vaultSettings.maxCleartextFilenameLength().setValue(UNLIMITED_FILENAME_LENGTH);
} }
} }
if (vaultSettings.maxCleartextFilenameLength.get() < UNLIMITED_FILENAME_LENGTH) { if (vaultSettings.maxCleartextFilenameLength().get() < UNLIMITED_FILENAME_LENGTH) {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get()); LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength().get());
} }
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withKeyLoader(keyLoader) // .withKeyLoader(keyLoader) //
.withFlags(flags) // .withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) // .withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.build(); .build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
@@ -253,11 +253,11 @@ public class Vault {
} }
public ReadOnlyStringProperty displayNameProperty() { public ReadOnlyStringProperty displayNameProperty() {
return vaultSettings.displayName; return vaultSettings.displayName();
} }
public String getDisplayName() { public String getDisplayName() {
return vaultSettings.displayName.get(); return vaultSettings.displayName().get();
} }
public ObjectBinding<Mountpoint> mountPointProperty() { public ObjectBinding<Mountpoint> mountPointProperty() {
@@ -274,7 +274,7 @@ public class Vault {
} }
public String getDisplayablePath() { public String getDisplayablePath() {
Path p = vaultSettings.path.get(); Path p = vaultSettings.path().get();
if (p.startsWith(HOME_DIR)) { if (p.startsWith(HOME_DIR)) {
Path relativePath = HOME_DIR.relativize(p); Path relativePath = HOME_DIR.relativize(p);
String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/";
@@ -311,7 +311,7 @@ public class Vault {
} }
public Path getPath() { public Path getPath() {
return vaultSettings.path.get(); return vaultSettings.path().getValue();
} }
/** /**
@@ -346,7 +346,7 @@ public class Vault {
} }
public String getId() { public String getId() {
return vaultSettings.id; return vaultSettings.getId();
} }
// ****************************************************************************** // ******************************************************************************
@@ -27,7 +27,7 @@ public class VaultConfigCache {
void reloadConfig() throws IOException { void reloadConfig() throws IOException {
try { try {
config.set(readConfigFromStorage(this.settings.path.get())); config.set(readConfigFromStorage(this.settings.path().get()));
} catch (IOException e) { } catch (IOException e) {
config.set(null); config.set(null);
throw e; throw e;
@@ -49,8 +49,8 @@ public class VaultListManager {
this.vaultComponentFactory = vaultComponentFactory; this.vaultComponentFactory = vaultComponentFactory;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName"); this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
addAll(settings.directories); addAll(settings.getDirectories());
vaultList.addListener(new VaultListChangeListener(settings.directories)); vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
autoLocker.init(); autoLocker.init();
} }
@@ -70,11 +70,11 @@ public class VaultListManager {
private VaultSettings newVaultSettings(Path path) { private VaultSettings newVaultSettings(Path path) {
VaultSettings vaultSettings = VaultSettings.withRandomId(); VaultSettings vaultSettings = VaultSettings.withRandomId();
vaultSettings.path.set(path); vaultSettings.path().set(path);
if (path.getFileName() != null) { if (path.getFileName() != null) {
vaultSettings.displayName.set(path.getFileName().toString()); vaultSettings.displayName().set(path.getFileName().toString());
} else { } else {
vaultSettings.displayName.set(defaultVaultName); vaultSettings.displayName().set(defaultVaultName);
} }
return vaultSettings; return vaultSettings;
} }
@@ -95,13 +95,13 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) { private Vault create(VaultSettings vaultSettings) {
var wrapper = new VaultConfigCache(vaultSettings); var wrapper = new VaultConfigCache(vaultSettings);
try { try {
var vaultState = determineVaultState(vaultSettings.path.get()); var vaultState = determineVaultState(vaultSettings.path().get());
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig(); wrapper.reloadConfig();
} }
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault(); return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e); LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault(); return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
} }
} }
@@ -9,8 +9,8 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.function.Function; import java.util.function.Function;
//TODO can the enum be removed? // TODO make sealed, remove enum
sealed interface IpcMessage permits HandleLaunchArgsMessage, RevealRunningAppMessage { interface IpcMessage {
enum MessageType { enum MessageType {
REVEAL_RUNNING_APP(RevealRunningAppMessage::decode), REVEAL_RUNNING_APP(RevealRunningAppMessage::decode),
@@ -5,9 +5,10 @@ import java.util.List;
public interface IpcMessageListener { public interface IpcMessageListener {
default void handleMessage(IpcMessage message) { default void handleMessage(IpcMessage message) {
switch (message) { if (message instanceof RevealRunningAppMessage) {
case RevealRunningAppMessage m -> revealRunningApp(); // TODO: rename to _ with JEP 443 revealRunningApp();
case HandleLaunchArgsMessage m -> handleLaunchArgs(m.args()); } else if (message instanceof HandleLaunchArgsMessage m) {
handleLaunchArgs(m.args());
} }
} }
@@ -9,7 +9,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dagger.Lazy; import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment; import org.cryptomator.common.Environment;
import org.cryptomator.common.SubstitutingProperties;
import org.cryptomator.common.ShutdownHook; import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator; import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode; import org.cryptomator.logging.DebugMode;
@@ -30,18 +29,10 @@ import java.util.concurrent.Executors;
public class Cryptomator { public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis(); private static final long STARTUP_TIME = System.currentTimeMillis();
static {
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
System.setProperties(lazyProcessedProps);
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
LOG = LoggerFactory.getLogger(Cryptomator.class);
}
// DaggerCryptomatorComponent gets generated by Dagger. // DaggerCryptomatorComponent gets generated by Dagger.
// Run Maven and include target/generated-sources/annotations in your IDE. // Run Maven and include target/generated-sources/annotations in your IDE.
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT; private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
private static final Logger LOG; private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private final DebugMode debugMode; private final DebugMode debugMode;
private final SupportedLanguages supportedLanguages; private final SupportedLanguages supportedLanguages;
@@ -72,6 +63,7 @@ public class Cryptomator {
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber); System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
return; return;
} }
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args); int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
LOG.info("Exit {}", exitCode); LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads. System.exit(exitCode); // end remaining non-daemon threads.
@@ -94,7 +86,7 @@ public class Cryptomator {
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args. * Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
* If no external process could be reached, the args will be handled by the loopback IPC endpoint. * If no external process could be reached, the args will be handled by the loopback IPC endpoint.
*/ */
try (var communicator = IpcCommunicator.create(env.getIpcSocketPath().toList())) { try (var communicator = IpcCommunicator.create(env.ipcSocketPath().toList())) {
if (communicator.isClient()) { if (communicator.isClient()) {
communicator.sendHandleLaunchargs(List.of(args)); communicator.sendHandleLaunchargs(List.of(args));
communicator.sendRevealRunningApp(); communicator.sendRevealRunningApp();
@@ -6,7 +6,6 @@
*******************************************************************************/ *******************************************************************************/
package org.cryptomator.launcher; package org.cryptomator.launcher;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -49,7 +48,7 @@ class FileOpenRequestHandler {
handleLaunchArgs(FileSystems.getDefault(), args); handleLaunchArgs(FileSystems.getDefault(), args);
} }
@VisibleForTesting // visible for testing
void handleLaunchArgs(FileSystem fs, List<String> args) { void handleLaunchArgs(FileSystem fs, List<String> args) {
Collection<Path> pathsToOpen = args.stream().map(str -> { Collection<Path> pathsToOpen = args.stream().map(str -> {
try { try {
@@ -1,14 +1,12 @@
package org.cryptomator.launcher; package org.cryptomator.launcher;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -16,39 +14,26 @@ import java.util.Locale;
public class SupportedLanguages { public class SupportedLanguages {
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class); private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_". // these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
// "en" is not part of this list - it is always inserted at the top. public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fil", "fa", "fr", "gl", "he", //
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", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "si", "sk", "sr", "sr-Latn", "sv", "sw", //
"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", "te", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
"ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
public static final String ENGLISH = "en";
private final List<String> sortedLanguageTags; @Nullable
private final String preferredLanguage;
private final Locale preferredLocale;
@Inject @Inject
public SupportedLanguages(Settings settings) { public SupportedLanguages(Settings settings) {
var preferredLanguage = settings.language.get(); this.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() //
.sorted((a, b) -> collator.compare(Locale.forLanguageTag(a).getDisplayName(), Locale.forLanguageTag(b).getDisplayName())) //
.forEach(sorted::add);
sortedLanguageTags = Collections.unmodifiableList(sorted);
} }
public void applyPreferred() { public void applyPreferred() {
LOG.debug("Using locale {}", preferredLocale); if (preferredLanguage == null) {
LOG.debug("Using system locale");
return;
}
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
Locale.setDefault(preferredLocale); Locale.setDefault(preferredLocale);
} }
public List<String> getLanguageTags() {
return sortedLanguageTags;
}
} }
@@ -26,8 +26,8 @@ public class DebugMode {
} }
public void initialize() { public void initialize() {
setLogLevels(settings.debugMode.get()); setLogLevels(settings.debugMode().get());
settings.debugMode.addListener(this::logLevelChanged); settings.debugMode().addListener(this::logLevelChanged);
} }
private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) { private void logLevelChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observable, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
@@ -5,23 +5,21 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController; import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@@ -45,8 +43,9 @@ public abstract class AddVaultModule {
@Provides @Provides
@AddVaultWizardWindow @AddVaultWizardWindow
@AddVaultWizardScoped @AddVaultWizardScoped
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage) { static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create(); Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false); stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL); stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(primaryStage); stage.initOwner(primaryStage);
@@ -66,13 +65,6 @@ public abstract class AddVaultModule {
return new SimpleStringProperty(""); return new SimpleStringProperty("");
} }
@Provides
@Named("shorteningThreshold")
@AddVaultWizardScoped
static IntegerProperty provideShorteningThreshold() {
return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
}
@Provides @Provides
@AddVaultWizardWindow @AddVaultWizardWindow
@AddVaultWizardScoped @AddVaultWizardScoped
@@ -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 @Provides
@FxmlScene(FxmlFile.ADDVAULT_EXISTING) @FxmlScene(FxmlFile.ADDVAULT_EXISTING)
@AddVaultWizardScoped @AddVaultWizardScoped
@@ -131,15 +130,13 @@ public abstract class AddVaultModule {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS); return fxmlLoaders.createScene(FxmlFile.ADDVAULT_SUCCESS);
} }
@Provides
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS)
@AddVaultWizardScoped
static Scene provideCreateNewVaultExpertSettingsScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS);
}
// ------------------ // ------------------
@Binds
@IntoMap
@FxControllerKey(AddVaultWelcomeController.class)
abstract FxController bindWelcomeController(AddVaultWelcomeController controller);
@Binds @Binds
@IntoMap @IntoMap
@FxControllerKey(ChooseExistingVaultController.class) @FxControllerKey(ChooseExistingVaultController.class)
@@ -184,9 +181,4 @@ public abstract class AddVaultModule {
@FxControllerKey(AddVaultSuccessController.class) @FxControllerKey(AddVaultSuccessController.class)
abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller); abstract FxController bindAddVaultSuccessController(AddVaultSuccessController controller);
@Binds
@IntoMap
@FxControllerKey(CreateNewVaultExpertSettingsController.class)
abstract FxController bindCreateNewVaultExpertSettingsController(CreateNewVaultExpertSettingsController controller);
} }
@@ -0,0 +1,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());
}
}

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