mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 18:21:26 +00:00
Compare commits
363 Commits
1.18.0-bet
...
ci/windows
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1f58ffb0e | ||
|
|
f21fae5b47 | ||
|
|
5ab12c1b1a | ||
|
|
0ac63e7aba | ||
|
|
ac6f34fe17 | ||
|
|
dba3de230b | ||
|
|
e11d42bb07 | ||
|
|
171d5d5a4c | ||
|
|
b86d8ca790 | ||
|
|
74fc77ab8d | ||
|
|
044dbcb11f | ||
|
|
1b243bb725 | ||
|
|
f21cb90e7d | ||
|
|
1e8f272f6a | ||
|
|
5c1f8e7576 | ||
|
|
bc0bb38e4c | ||
|
|
b9b02acbe5 | ||
|
|
e53598cfce | ||
|
|
a79fd63634 | ||
|
|
f753ddc9be | ||
|
|
158b454b0d | ||
|
|
9e6bd913cb | ||
|
|
83ef9d06d9 | ||
|
|
cf0052b4f5 | ||
|
|
0b4d86768a | ||
|
|
a77e90738a | ||
|
|
0bec741c2b | ||
|
|
af43aeca15 | ||
|
|
1629eae5d3 | ||
|
|
99898b74fb | ||
|
|
327d6065e9 | ||
|
|
2a5ef5d999 | ||
|
|
34e5d19a04 | ||
|
|
33851a8559 | ||
|
|
fb54d96997 | ||
|
|
cff47b1c73 | ||
|
|
5db05d8bc7 | ||
|
|
b85780eae9 | ||
|
|
bdfd22c483 | ||
|
|
e3433cb312 | ||
|
|
46d1d605ad | ||
|
|
a4eadd4817 | ||
|
|
c6717bd4e1 | ||
|
|
53f368108a | ||
|
|
5e52811c74 | ||
|
|
cad7b45808 | ||
|
|
a057bf6f70 | ||
|
|
4cf11cf293 | ||
|
|
2523609996 | ||
|
|
80276ee992 | ||
|
|
561432bc98 | ||
|
|
cd0a640a36 | ||
|
|
a7b8541912 | ||
|
|
d4382f3130 | ||
|
|
2983745be5 | ||
|
|
77983fe00a | ||
|
|
a4836f6528 | ||
|
|
e9b3b505a8 | ||
|
|
19c9eada9d | ||
|
|
2a5bce2c5c | ||
|
|
9ee81a0e35 | ||
|
|
8b05ae0a54 | ||
|
|
aa898c634f | ||
|
|
efbd107fb5 | ||
|
|
300cac5441 | ||
|
|
b651b9ac26 | ||
|
|
29e76e7f93 | ||
|
|
5cac6b8114 | ||
|
|
35c2141fd6 | ||
|
|
b00c81c20a | ||
|
|
7ee0606306 | ||
|
|
046372f95b | ||
|
|
c198adaf3f | ||
|
|
d53f0880ca | ||
|
|
088b177c0e | ||
|
|
287ab4e792 | ||
|
|
0e9cad5b09 | ||
|
|
a0c9caeb21 | ||
|
|
f620c6685a | ||
|
|
61be8c449c | ||
|
|
43343b9954 | ||
|
|
460b7e6225 | ||
|
|
e2430bfb22 | ||
|
|
ac7c9c2165 | ||
|
|
efd73e0d3e | ||
|
|
b441803462 | ||
|
|
42b06aa556 | ||
|
|
b1c21501a6 | ||
|
|
45633837e0 | ||
|
|
b23bd0b27a | ||
|
|
b3f3faf4ee | ||
|
|
33f26bf804 | ||
|
|
a06accb80f | ||
|
|
a50e372f05 | ||
|
|
06726303fb | ||
|
|
11c86f7287 | ||
|
|
8e68a62ab0 | ||
|
|
787d0b8e12 | ||
|
|
ba765f7a69 | ||
|
|
e9d7e631c7 | ||
|
|
88acbc8ed7 | ||
|
|
e84a73b9c3 | ||
|
|
2324ddad8b | ||
|
|
04da2504de | ||
|
|
8105dbc236 | ||
|
|
ceed70c314 | ||
|
|
eddf586b81 | ||
|
|
0d7ddd3189 | ||
|
|
491cd5e550 | ||
|
|
93d95ecd93 | ||
|
|
05a41a625c | ||
|
|
2aff02a186 | ||
|
|
4b7de11750 | ||
|
|
aaf921afcf | ||
|
|
7718445f7c | ||
|
|
0fa27f66c9 | ||
|
|
111c5ae2de | ||
|
|
8841bb1c6a | ||
|
|
8ca7e34d8f | ||
|
|
0fa6f0d915 | ||
|
|
4ffd8f4467 | ||
|
|
bb0c1b54c1 | ||
|
|
8eb91f6471 | ||
|
|
eb20fd7278 | ||
|
|
d836798361 | ||
|
|
760e260eba | ||
|
|
0cd6eb8b5e | ||
|
|
0b2107309b | ||
|
|
309881b339 | ||
|
|
532cdf30ac | ||
|
|
d746e9e215 | ||
|
|
408c80744a | ||
|
|
bf890d2165 | ||
|
|
eb90a7fb33 | ||
|
|
a47814f38c | ||
|
|
8b157fd935 | ||
|
|
5e582f666c | ||
|
|
bebe3064ee | ||
|
|
bbf8e1d1e5 | ||
|
|
98d2442deb | ||
|
|
cac5c86dab | ||
|
|
5a0145800d | ||
|
|
f4b56e5e51 | ||
|
|
7cfb63ed48 | ||
|
|
2521cd2bfb | ||
|
|
1e861ac187 | ||
|
|
a4c85be61c | ||
|
|
5b9e70da33 | ||
|
|
b83a3c27c6 | ||
|
|
5c2954de03 | ||
|
|
8a834fe71c | ||
|
|
4ff2905723 | ||
|
|
538e01b1e5 | ||
|
|
3135be9178 | ||
|
|
2c456f5323 | ||
|
|
9d464e3a4f | ||
|
|
83199eb1de | ||
|
|
5772ffec17 | ||
|
|
f3415277d7 | ||
|
|
8e9a001fde | ||
|
|
5e229395f1 | ||
|
|
18dd9770f2 | ||
|
|
185c5a4f5d | ||
|
|
285ddac219 | ||
|
|
9e631a78f2 | ||
|
|
7fe9049266 | ||
|
|
39ab9ad63f | ||
|
|
b4528f825e | ||
|
|
3d679c73e7 | ||
|
|
f4f093bb42 | ||
|
|
617f1bf2c9 | ||
|
|
02ad38f871 | ||
|
|
7a2944cbea | ||
|
|
4c47204f92 | ||
|
|
b6d0823c24 | ||
|
|
7f4776a995 | ||
|
|
9d2b31cd56 | ||
|
|
f052395a7f | ||
|
|
57be83b38d | ||
|
|
c2cd4f5bbf | ||
|
|
d3d57312ba | ||
|
|
593a64c9bd | ||
|
|
19dc4fb6ff | ||
|
|
81f45012f3 | ||
|
|
c59554f7bb | ||
|
|
11c66e8df7 | ||
|
|
c49bf0f146 | ||
|
|
e74bd91879 | ||
|
|
89d9249a08 | ||
|
|
013fff1223 | ||
|
|
fd9d27215e | ||
|
|
f91cc2374c | ||
|
|
48298bb161 | ||
|
|
7a1cd9026c | ||
|
|
754e53d8db | ||
|
|
c36f1bc8d0 | ||
|
|
7315b59a6d | ||
|
|
8a243a01aa | ||
|
|
9e4006cc89 | ||
|
|
26b69beb87 | ||
|
|
f95bf87a4b | ||
|
|
e854c7d189 | ||
|
|
8a434dcd96 | ||
|
|
6b7324723e | ||
|
|
0bdcb2b3be | ||
|
|
c3931d9d29 | ||
|
|
307825a339 | ||
|
|
093f0e8c94 | ||
|
|
c938c42c00 | ||
|
|
cee22e34db | ||
|
|
884c6f6bdd | ||
|
|
c5367db971 | ||
|
|
9a74194391 | ||
|
|
18300ee67f | ||
|
|
59560193ee | ||
|
|
a6b31e19b9 | ||
|
|
8a44115234 | ||
|
|
43a1f00bea | ||
|
|
3e458060bc | ||
|
|
4bb5a3f10d | ||
|
|
8bb451261e | ||
|
|
de4c7d47e5 | ||
|
|
b4fe63c966 | ||
|
|
ed45182b6c | ||
|
|
afd2d74c90 | ||
|
|
4bcf01270a | ||
|
|
5797108dfe | ||
|
|
64466df6b8 | ||
|
|
db9bd49dd9 | ||
|
|
4ed4a3e3e5 | ||
|
|
e1ff552f08 | ||
|
|
f3d4494d91 | ||
|
|
bdc1cc3f05 | ||
|
|
ad73b2f31d | ||
|
|
227b322b8a | ||
|
|
ccf2e60390 | ||
|
|
1104c1067a | ||
|
|
83d2c39080 | ||
|
|
446caff901 | ||
|
|
90537caee7 | ||
|
|
25dea8ebe6 | ||
|
|
4a7d2907c8 | ||
|
|
7f3af0c7f6 | ||
|
|
e6e04078fa | ||
|
|
e3f6b5f812 | ||
|
|
f3461f7da4 | ||
|
|
94518ede99 | ||
|
|
0fa041da85 | ||
|
|
daa4f3a568 | ||
|
|
fc6f7d184c | ||
|
|
e50c949b62 | ||
|
|
349349e057 | ||
|
|
4b36a1219f | ||
|
|
6533ba7367 | ||
|
|
7f9c9a7df6 | ||
|
|
afda8a4981 | ||
|
|
cdcd43a805 | ||
|
|
2f4c12a89e | ||
|
|
2b79c58577 | ||
|
|
1d3c62e2c7 | ||
|
|
17166c45ac | ||
|
|
f758388063 | ||
|
|
8767564977 | ||
|
|
59f47116f0 | ||
|
|
f5bd23de51 | ||
|
|
8fac541024 | ||
|
|
1ba21b531a | ||
|
|
daad48d5af | ||
|
|
8eedc62fbe | ||
|
|
d65442f042 | ||
|
|
216513405a | ||
|
|
f4c0bc29ed | ||
|
|
d1ce0362b4 | ||
|
|
4497fb160e | ||
|
|
5bffa28cdb | ||
|
|
14c5693594 | ||
|
|
53c6045cca | ||
|
|
16d441ec44 | ||
|
|
fa053ac3df | ||
|
|
e78a064af6 | ||
|
|
d5245009f4 | ||
|
|
8f4392711e | ||
|
|
644e38f49e | ||
|
|
3b38049d71 | ||
|
|
79bb4a5215 | ||
|
|
c199d1cb3b | ||
|
|
164a4de6de | ||
|
|
2ce7fee06d | ||
|
|
99e9e92f10 | ||
|
|
8e6500d93f | ||
|
|
89ce99deaf | ||
|
|
510e134605 | ||
|
|
62b434f549 | ||
|
|
1f1e336d57 | ||
|
|
22a37699ea | ||
|
|
44f92cfb26 | ||
|
|
2067c5a33b | ||
|
|
fc5ed9ecc0 | ||
|
|
5e956c50c2 | ||
|
|
5110b35690 | ||
|
|
499960b3ec | ||
|
|
f6479ddf24 | ||
|
|
5d8b016e5f | ||
|
|
2bf2f2b616 | ||
|
|
1089e90ebf | ||
|
|
9fee4afd2a | ||
|
|
b00ee4740a | ||
|
|
02186ca17a | ||
|
|
b0ed133e05 | ||
|
|
f5ecf846f2 | ||
|
|
a9444182bb | ||
|
|
191d9473c0 | ||
|
|
8089bcd0d0 | ||
|
|
f623b1ee7e | ||
|
|
c546f3363b | ||
|
|
8a1ec1d073 | ||
|
|
81307b6d75 | ||
|
|
50fa8e0608 | ||
|
|
626a692d63 | ||
|
|
c9b9eee564 | ||
|
|
edc308ccd1 | ||
|
|
ad214691f9 | ||
|
|
acf11da202 | ||
|
|
4280112eab | ||
|
|
638e1e562d | ||
|
|
321bd2ff8c | ||
|
|
323a884718 | ||
|
|
5bdfffd151 | ||
|
|
6fcec26eea | ||
|
|
4ddb6b73bd | ||
|
|
da845c6941 | ||
|
|
c30a28aae0 | ||
|
|
5305c5a631 | ||
|
|
b0d2dcbff3 | ||
|
|
5c1364792e | ||
|
|
abf8392baa | ||
|
|
034a70d473 | ||
|
|
8c9d75fe57 | ||
|
|
d00d77b747 | ||
|
|
6d47f28634 | ||
|
|
225332bd4f | ||
|
|
8c84720e24 | ||
|
|
968e8f51e4 | ||
|
|
4de6b83e2f | ||
|
|
90cbb0c5f6 | ||
|
|
e1cc61037e | ||
|
|
1ae951126a | ||
|
|
6bb8c0d4e7 | ||
|
|
087b6162ed | ||
|
|
f9e8031acb | ||
|
|
ebd767595b | ||
|
|
6231c5b8b3 | ||
|
|
a3b8297e23 | ||
|
|
6715ff9f5f | ||
|
|
8c5325511c | ||
|
|
895614353e | ||
|
|
676c507a50 | ||
|
|
5f4fd92b1d | ||
|
|
8191a7dae6 | ||
|
|
756672258d | ||
|
|
fb6f7072d4 | ||
|
|
aa95ad40c0 | ||
|
|
ba3667ab51 |
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -20,6 +20,10 @@
|
||||
|
||||
Translations are not managed directly in this repository. Instead, we use [Crowdin](https://translate.cryptomator.org/), which automatically synchronizes translations with this repository. If you want to help us with translations, please visit our translation project on Crowdin.
|
||||
|
||||
## Use of Generative AI
|
||||
|
||||
AI tools may assist your work, but every contribution must be fully understood, reviewed, and tested by you. Only submit changes you can clearly explain and justify. Unverified or low-quality AI output that wastes our time and resources will be closed without further review.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md).
|
||||
|
||||
76
.github/actions/win-sign-action/action.yml
vendored
Normal file
76
.github/actions/win-sign-action/action.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: 'Windows Code Signing'
|
||||
description: 'Sign files on Windows with the Azure Trusted Signing'
|
||||
inputs:
|
||||
base-dir:
|
||||
description: 'Absolute path to the base directory to search for files'
|
||||
required: true
|
||||
recursive:
|
||||
description: 'Whether to search recursively in subdirectories'
|
||||
required: false
|
||||
default: 'false'
|
||||
file-extensions:
|
||||
description: 'List of file extensions to sign, separated by comma'
|
||||
required: true
|
||||
default: 'exe,dll,ps1'
|
||||
description:
|
||||
description: 'Signature description'
|
||||
required: true
|
||||
default: 'Cryptomator'
|
||||
url:
|
||||
description: 'Signature URL'
|
||||
required: false
|
||||
default: 'https://cryptomator.org'
|
||||
append-signature:
|
||||
description: 'Whether to append the signature to existing signatures'
|
||||
required: false
|
||||
default: 'false'
|
||||
tenant-id:
|
||||
description: 'Azure Tenant ID'
|
||||
required: true
|
||||
client-id:
|
||||
description: 'Azure Client ID'
|
||||
required: true
|
||||
client-secret:
|
||||
description: 'Azure Client Secret'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Generate, mask, and output the input secrets
|
||||
id: set-secrets
|
||||
run: |
|
||||
echo "::add-mask::${{ inputs.tenant-id }}"
|
||||
echo "::add-mask::${{ inputs.client-id }}"
|
||||
echo "::add-mask::${{ inputs.client-secret }}"
|
||||
echo "tenant-id=${{ inputs.tenant-id }}" >> "$GITHUB_OUTPUT"
|
||||
echo "client-id=${{ inputs.client-id }}" >> "$GITHUB_OUTPUT"
|
||||
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
- name: Sign DLLs with Azure Trusted Signing
|
||||
uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0
|
||||
with:
|
||||
files-folder: ${{ inputs.base-dir }}
|
||||
files-folder-filter: ${{ inputs.file-extensions }}
|
||||
files-folder-recurse: ${{ inputs.recursive }}
|
||||
append-signature: ${{ inputs.append-signature }}
|
||||
description: ${{ inputs.description }}
|
||||
description-url: ${{ inputs.url }}
|
||||
azure-tenant-id: ${{ steps.set-secrets.outputs.tenant-id }}
|
||||
azure-client-id: ${{ steps.set-secrets.outputs.client-id }}
|
||||
azure-client-secret: ${{ steps.set-secrets.outputs.client-secret }}
|
||||
signing-account-name: cryptomatorSigning
|
||||
certificate-profile-name: production
|
||||
endpoint: https://weu.codesigning.azure.net/
|
||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||
timestamp-digest: SHA256
|
||||
exclude-environment-credential: false
|
||||
exclude-workload-identity-credential: true
|
||||
exclude-managed-identity-credential: true
|
||||
exclude-shared-token-cache-credential: true
|
||||
exclude-visual-studio-credential: true
|
||||
exclude-visual-studio-code-credential: true
|
||||
exclude-azure-cli-credential: true
|
||||
exclude-azure-powershell-credential: true
|
||||
exclude-azure-developer-cli-credential: true
|
||||
exclude-interactive-browser-credential: true
|
||||
33
.github/workflows/appimage.yml
vendored
33
.github/workflows/appimage.yml
vendored
@@ -19,7 +19,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '24.0.1+9'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -37,16 +37,16 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
appimage-suffix: x86_64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
|
||||
openjfx-sha: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
|
||||
openjfx-sha: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
||||
- os: ubuntu-24.04-arm
|
||||
appimage-suffix: aarch64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Plinux -DskipTests -Djavafx.platform=linux
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
||||
--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.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -117,14 +117,13 @@ jobs:
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
||||
@@ -176,7 +175,7 @@ jobs:
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: appimage-${{ matrix.appimage-suffix }}
|
||||
path: |
|
||||
@@ -186,7 +185,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
- name: Publish AppImage on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
@@ -199,10 +198,10 @@ jobs:
|
||||
name: Create PR for aur-bin repo
|
||||
needs: [build, get-version]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'release'
|
||||
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
||||
steps:
|
||||
- name: Download AppImages
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: downloads/
|
||||
merge-multiple: true
|
||||
@@ -213,7 +212,7 @@ jobs:
|
||||
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
|
||||
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: 'cryptomator/aur-bin'
|
||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
@@ -248,7 +247,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
|
||||
10
.github/workflows/aur.yml
vendored
10
.github/workflows/aur.yml
vendored
@@ -19,6 +19,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version]
|
||||
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
|
||||
env:
|
||||
INPUT_TAG: ${{ inputs.tag }}
|
||||
outputs:
|
||||
url: ${{ steps.url.outputs.url}}
|
||||
sha256: ${{ steps.sha256.outputs.sha256}}
|
||||
@@ -27,8 +29,8 @@ jobs:
|
||||
id: url
|
||||
run: |
|
||||
URL="";
|
||||
if [[ -n "${{ inputs.tag }}" ]]; then
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
|
||||
if [[ -n "${INPUT_TAG}" ]]; then
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${INPUT_TAG}.tar.gz"
|
||||
else
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
|
||||
fi
|
||||
@@ -46,7 +48,7 @@ jobs:
|
||||
env:
|
||||
AUR_PR_URL: tbd
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: 'cryptomator/aur'
|
||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
@@ -79,8 +81,8 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
if: github.event_name == 'release'
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
|
||||
32
.github/workflows/av-whitelist.yml
vendored
32
.github/workflows/av-whitelist.yml
vendored
@@ -7,6 +7,16 @@ on:
|
||||
description: "Url to the file to upload"
|
||||
required: true
|
||||
type: string
|
||||
avast:
|
||||
description: "Upload to Avast"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
kaspersky:
|
||||
description: "Upload to Kaspersky"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
url:
|
||||
@@ -30,16 +40,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
fileName: ${{ steps.extractName.outputs.fileName}}
|
||||
env:
|
||||
INPUT_URL: ${{ inputs.url }}
|
||||
steps:
|
||||
- name: Extract file name
|
||||
id: extractName
|
||||
run: |
|
||||
url="${{ inputs.url }}"
|
||||
url="${INPUT_URL}"
|
||||
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
||||
- name: Download file
|
||||
run: curl --remote-name ${{ inputs.url }} -L -o ${{steps.extractName.outputs.fileName}}
|
||||
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: ${{ steps.extractName.outputs.fileName }}
|
||||
path: ${{ steps.extractName.outputs.fileName }}
|
||||
@@ -48,15 +60,15 @@ jobs:
|
||||
name: Anti Virus Allowlisting Kaspersky
|
||||
runs-on: ubuntu-latest
|
||||
needs: download-file
|
||||
if: github.event_name == 'workflow_call' || inputs.kaspersky
|
||||
if: inputs.kaspersky
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: ${{ needs.download-file.outputs.fileName }}
|
||||
path: upload
|
||||
- name: Upload to Kaspersky
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
|
||||
uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # v4.6.3
|
||||
with:
|
||||
protocol: ftps
|
||||
server: allowlist.kaspersky-labs.com
|
||||
@@ -68,15 +80,15 @@ jobs:
|
||||
name: Anti Virus Allowlisting Avast
|
||||
runs-on: ubuntu-latest
|
||||
needs: download-file
|
||||
if: github.event_name == 'workflow_call' || inputs.avast
|
||||
if: inputs.avast
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: ${{ needs.download-file.outputs.fileName }}
|
||||
path: upload
|
||||
- name: Upload to Avast
|
||||
uses: wlixcc/SFTP-Deploy-Action@v1.2.6
|
||||
- name: Upload to Avast
|
||||
uses: wlixcc/SFTP-Deploy-Action@a5ccb9c6211a94cc59404f0fdb2a9936a6dfee64 # v1.2.6
|
||||
with:
|
||||
server: whitelisting.avast.com
|
||||
port: 22
|
||||
|
||||
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: 24
|
||||
JAVA_VERSION: 25
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -22,14 +22,14 @@ jobs:
|
||||
name: Compile and Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Build and Test
|
||||
run: >
|
||||
xvfb-run
|
||||
mvn -B verify -Djavafx.platform=linux
|
||||
mvn -B verify
|
||||
jacoco:report
|
||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||
-Pcoverage
|
||||
@@ -49,28 +49,28 @@ jobs:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
- name: Draft a release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
draft: true
|
||||
discussion_category_name: releases
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
generate_release_notes: true
|
||||
body: |-
|
||||
:construction: Work in Progress
|
||||
> [!NOTE]
|
||||
> 🚧 Work in Progress 🚧
|
||||
>
|
||||
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
|
||||
|
||||
<!--REPLACE with auto-generated release notes (see below)
|
||||
### What's New 🎉
|
||||
|
||||
### Bugfixes 🐛
|
||||
|
||||
### Other Changes 📎
|
||||
END REPLACE-->
|
||||
|
||||
Feel free to also read our [CHANGELOG.md](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
|
||||
|
||||
---
|
||||
|
||||
TODO FULL CHANGELOG
|
||||
|
||||
📜 List of closed issues is available [here](TODO)
|
||||
|
||||
---
|
||||
⏳ 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. ⏳
|
||||
|
||||
<!-- Don't forget to include the
|
||||
💾 SHA-256 checksums of release artifacts:
|
||||
@@ -78,4 +78,9 @@ jobs:
|
||||
```
|
||||
-->
|
||||
|
||||
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).
|
||||
> [!TIP]
|
||||
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||
|
||||
|
||||
|
||||
<!-- Auto-Generated Release Notes: -->
|
||||
|
||||
8
.github/workflows/check-jdk-updates.yml
vendored
8
.github/workflows/check-jdk-updates.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
JDK_VERSION: '24.0.1+9'
|
||||
JDK_VERSION: '25.0.1+8.0.LTS'
|
||||
JDK_VENDOR: temurin
|
||||
RUNTIME_VERSION_HELPER: >
|
||||
public class Test {
|
||||
@@ -23,10 +23,10 @@ jobs:
|
||||
JDK_MAJOR_VERSION: 'toBeFilled'
|
||||
steps:
|
||||
- name: Determine current major version
|
||||
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$env:GITHUB_ENV"
|
||||
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
|
||||
shell: pwsh
|
||||
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
}
|
||||
- name: Notify
|
||||
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
|
||||
33
.github/workflows/debian.yml
vendored
33
.github/workflows/debian.yml
vendored
@@ -23,13 +23,12 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '24.0.1+9'
|
||||
COFFEELIBS_JDK: 24
|
||||
COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
|
||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AARCH64_HASH: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -41,30 +40,34 @@ jobs:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [get-version]
|
||||
env:
|
||||
INPUT_PPAVER: ${{ inputs.ppaver }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- id: deb-version
|
||||
name: Determine deb-version
|
||||
run: |
|
||||
if [ -n "${{inputs.ppaver}}" ]; then
|
||||
echo "debVersion=${{inputs.ppaver }}" >> "$GITHUB_OUTPUT"
|
||||
if [ -n "${INPUT_PPAVER}" ]; then
|
||||
echo "debVersion=${INPUT_PPAVER}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "debVersion=${{needs.get-version.outputs.semVerStr}}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Install build tools
|
||||
run: |
|
||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||
sudo apt-get update
|
||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
|
||||
sudo apt-get install devscripts dput
|
||||
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
|
||||
env:
|
||||
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
check-latest: true
|
||||
cache: 'maven'
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Plinux -Djavafx.platform=linux -DskipTests
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
@@ -140,7 +143,7 @@ jobs:
|
||||
run: |
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: linux-deb-package
|
||||
path: |
|
||||
|
||||
9
.github/workflows/dependency-check.yml
vendored
9
.github/workflows/dependency-check.yml
vendored
@@ -7,12 +7,13 @@ on:
|
||||
|
||||
jobs:
|
||||
check-dependencies:
|
||||
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@v1
|
||||
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3
|
||||
with:
|
||||
runner-os: 'ubuntu-latest'
|
||||
java-distribution: 'temurin'
|
||||
java-version: 24
|
||||
check-command: 'mvn -B validate -Pdependency-check -Djavafx.platform=linux'
|
||||
java-version: 25
|
||||
secrets:
|
||||
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
|
||||
ossindex-token: ${{ secrets.OSSINDEX_API_TOKEN }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
|
||||
4
.github/workflows/dl-stats.yml
vendored
4
.github/workflows/dl-stats.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- name: Get download count of latest releases
|
||||
id: get-stats
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const query = `query($owner:String!, $name:String!) {
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
INTERVAL: 900
|
||||
JSON_DATA: ${{ steps.get-stats.outputs.result }}
|
||||
- name: Upload Results
|
||||
uses: fjogeleit/http-request-action@v1
|
||||
uses: fjogeleit/http-request-action@551353b829c3646756b2ec2b3694f819d7957495 # v2.0.0
|
||||
with:
|
||||
url: 'https://graphite-us-central1.grafana.net/metrics'
|
||||
method: 'POST'
|
||||
|
||||
6
.github/workflows/error-db.yml
vendored
6
.github/workflows/error-db.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Query Discussion Data
|
||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
||||
id: query-data
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
return await github.graphql(query, variables)
|
||||
- name: Get Gist
|
||||
id: get-gist
|
||||
uses: andymckay/get-gist-action@master
|
||||
uses: andymckay/get-gist-action@cf3bc8164af24126f7e5979eb6d3dc0c12309bd1 # not_tagged
|
||||
with:
|
||||
gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230
|
||||
- name: Merge Error Code Data
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
env:
|
||||
DISCUSSION: ${{ steps.query-data.outputs.result }}
|
||||
- name: Patch Gist
|
||||
uses: exuanbo/actions-deploy-gist@v1
|
||||
uses: exuanbo/actions-deploy-gist@47697fceaeea2006a90594ee24eb9cd0a1121ef8 # v1.1.4
|
||||
with:
|
||||
token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }}
|
||||
gist_id: accba9fb9555e7192271b85606f97230
|
||||
|
||||
13
.github/workflows/flathub.yml
vendored
13
.github/workflows/flathub.yml
vendored
@@ -26,13 +26,10 @@ jobs:
|
||||
- name: Determine tarball url
|
||||
id: url
|
||||
run: |
|
||||
URL="";
|
||||
if [[ -n "${{ inputs.tag }}" ]]; then
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
|
||||
else
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
|
||||
fi
|
||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz"
|
||||
echo "url=${URL}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
TAG: ${{ inputs.tag || github.event.release.tag_name}}
|
||||
- name: Download source tarball and compute checksum
|
||||
id: sha512
|
||||
run: |
|
||||
@@ -46,7 +43,7 @@ jobs:
|
||||
env:
|
||||
FLATHUB_PR_URL: tbd
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: 'flathub/org.cryptomator.Cryptomator'
|
||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
@@ -74,7 +71,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
14
.github/workflows/get-version.yml
vendored
14
.github/workflows/get-version.yml
vendored
@@ -23,7 +23,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: 24
|
||||
JAVA_VERSION: 25
|
||||
|
||||
jobs:
|
||||
determine-version:
|
||||
@@ -35,11 +35,11 @@ jobs:
|
||||
revNum: ${{ steps.versions.outputs.revNum }}
|
||||
type: ${{ steps.versions.outputs.type}}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -49,8 +49,8 @@ jobs:
|
||||
run: |
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ inputs.version }}"
|
||||
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${VERSION_STRING}"
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -70,7 +70,9 @@ jobs:
|
||||
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
||||
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
VERSION_STRING: ${{ inputs.version }}
|
||||
- name: Validate Version
|
||||
uses: skymatic/semver-validation-action@v3
|
||||
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
|
||||
with:
|
||||
version: ${{ steps.versions.outputs.semVerStr }}
|
||||
40
.github/workflows/mac-dmg-x64.yml
vendored
40
.github/workflows/mac-dmg-x64.yml
vendored
@@ -24,7 +24,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '24.0.1+9'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -44,12 +44,12 @@ jobs:
|
||||
architecture: x64
|
||||
output-suffix: x64
|
||||
fuse-lib: macFUSE
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-x64_bin-jmods.zip'
|
||||
openjfx-sha: '6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-x64_bin-jmods.zip'
|
||||
openjfx-sha: '0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
||||
--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.accessibility,jdk.management.jfr,java.compiler
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -121,7 +121,6 @@ jobs:
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
@@ -129,14 +128,15 @@ jobs:
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||
--java-options "-Dsun.java2d.metal=true"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||
--mac-package-identifier org.cryptomator
|
||||
--resource-dir dist/mac/resources
|
||||
@@ -151,9 +151,23 @@ jobs:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||
- name: Build and install DockTilePlugin
|
||||
env:
|
||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
||||
run: |
|
||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-destination "platform=macOS,arch=x86_64" \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
- name: Generate license for dmg
|
||||
run: >
|
||||
mvn -B -Djavafx.platform=mac license:add-third-party
|
||||
mvn -B license:add-third-party
|
||||
-Dlicense.thirdPartyFilename=license.rtf
|
||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||
@@ -248,7 +262,7 @@ jobs:
|
||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||
- name: Notarize .dmg
|
||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
||||
uses: cocoalibs/xcode-notarization-action@v1
|
||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||
with:
|
||||
app-path: 'Cryptomator-*.dmg'
|
||||
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||
@@ -269,7 +283,7 @@ jobs:
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: dmg-${{ matrix.output-suffix }}
|
||||
path: |
|
||||
@@ -278,7 +292,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
- name: Publish dmg on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
|
||||
40
.github/workflows/mac-dmg.yml
vendored
40
.github/workflows/mac-dmg.yml
vendored
@@ -22,7 +22,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '24.0.1+9'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -42,12 +42,12 @@ jobs:
|
||||
architecture: aarch64
|
||||
output-suffix: arm64
|
||||
fuse-lib: FUSE-T
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: '13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
||||
--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.accessibility,jdk.management.jfr,java.compiler
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -119,7 +119,6 @@ jobs:
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
@@ -127,14 +126,15 @@ jobs:
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||
--java-options "-Dsun.java2d.metal=true"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||
--mac-package-identifier org.cryptomator
|
||||
@@ -150,9 +150,23 @@ jobs:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||
- name: Build and install DockTilePlugin
|
||||
env:
|
||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
||||
run: |
|
||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-destination "platform=macOS,arch=arm64" \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
- name: Generate license for dmg
|
||||
run: >
|
||||
mvn -B -Djavafx.platform=mac license:add-third-party
|
||||
mvn -B license:add-third-party
|
||||
-Dlicense.thirdPartyFilename=license.rtf
|
||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||
@@ -247,7 +261,7 @@ jobs:
|
||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||
- name: Notarize .dmg
|
||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
||||
uses: cocoalibs/xcode-notarization-action@v1
|
||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||
with:
|
||||
app-path: 'Cryptomator-*.dmg'
|
||||
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||
@@ -268,7 +282,7 @@ jobs:
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: dmg-${{ matrix.output-suffix }}
|
||||
path: |
|
||||
@@ -277,7 +291,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
- name: Publish dmg on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
|
||||
2
.github/workflows/no-response.yml
vendored
2
.github/workflows/no-response.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 0
|
||||
|
||||
4
.github/workflows/post-publish.yml
vendored
4
.github/workflows/post-publish.yml
vendored
@@ -19,14 +19,14 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Publish asc on GitHub Releases
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
cryptomator-*.tar.gz.asc
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
|
||||
8
.github/workflows/pullrequest.yml
vendored
8
.github/workflows/pullrequest.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: 24
|
||||
JAVA_VERSION: 25
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
name: Compile and Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage -Djavafx.platform=linux
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
||||
10
.github/workflows/release-check.yml
vendored
10
.github/workflows/release-check.yml
vendored
@@ -12,16 +12,16 @@ defaults:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: 23
|
||||
JAVA_VERSION: 25
|
||||
|
||||
jobs:
|
||||
check-preconditions:
|
||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Cache NVD DB
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||
key: dependency-check-${{ github.run_id }}
|
||||
@@ -60,6 +60,6 @@ jobs:
|
||||
- name: Run org.owasp:dependency-check plugin
|
||||
id: dependency-check
|
||||
continue-on-error: true
|
||||
run: mvn -B verify -Pdependency-check -DskipTests -Djavafx.platform=linux
|
||||
run: mvn -B verify -Pdependency-check -DskipTests
|
||||
env:
|
||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
with:
|
||||
days-before-stale: 365
|
||||
days-before-close: 90
|
||||
|
||||
141
.github/workflows/win-exe.yml
vendored
141
.github/workflows/win-exe.yml
vendored
@@ -8,10 +8,6 @@ on:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
isDebug:
|
||||
description: 'Build debug version with console output'
|
||||
type: boolean
|
||||
default: false
|
||||
sign:
|
||||
description: 'Sign binaries'
|
||||
required: false
|
||||
@@ -26,8 +22,8 @@ on:
|
||||
|
||||
|
||||
env:
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
|
||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
|
||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
||||
@@ -51,18 +47,13 @@ jobs:
|
||||
include:
|
||||
- arch: x64
|
||||
os: windows-latest
|
||||
java-dist: 'zulu'
|
||||
java-version: '24.0.1+9'
|
||||
java-dist: 'temurin' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
|
||||
java-version: '25.0.2+10.0.LTS'
|
||||
java-package: 'jdk'
|
||||
- arch: arm64
|
||||
os: windows-11-arm
|
||||
java-dist: 'liberica'
|
||||
java-version: '24.0.1+11'
|
||||
java-package: 'jdk+fx' #This is needed, as liberica contains JFX 24 Jmods for Windows ARM64
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ matrix.java-dist }}
|
||||
java-version: ${{ matrix.java-version }}
|
||||
@@ -102,7 +93,7 @@ jobs:
|
||||
- name: Set version
|
||||
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
|
||||
run: mvn -B clean package -Pwin -DskipTests
|
||||
- name: Patch target dir
|
||||
run: |
|
||||
cp LICENSE.txt target
|
||||
@@ -122,7 +113,7 @@ jobs:
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
||||
--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.crypto.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
@@ -144,14 +135,13 @@ jobs:
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
|
||||
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
|
||||
@@ -195,12 +185,31 @@ jobs:
|
||||
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -Path appdir/jpackage-jmod -ItemType Directory
|
||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
||||
$extractDir = "appdir/jpackage-jimage"
|
||||
New-Item -Path $extractDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
& "$env:JAVA_HOME\bin\jimage.exe" extract --dir $extractDir "$env:JAVA_HOME\lib\modules"
|
||||
|
||||
$wixhelper = Get-ChildItem -Path $extractDir -Recurse -File -Filter "wixhelper.dll" | Select-Object -First 1
|
||||
if (-not $wixhelper) {
|
||||
throw "wixhelper.dll not found in $env:JAVA_HOME\lib\modules"
|
||||
}
|
||||
|
||||
Copy-Item -Path $wixhelper.FullName -Destination "appdir/wixhelper.dll" -Force
|
||||
Remove-Item -Path $extractDir -Recurse -Force
|
||||
- name: Sign DLLs with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\appdir
|
||||
recursive: true
|
||||
append-signature: true
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign DLLs with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'appdir'
|
||||
file-extensions: 'dll,exe,ps1'
|
||||
@@ -225,7 +234,7 @@ jobs:
|
||||
}
|
||||
- name: Generate license for MSI
|
||||
run: >
|
||||
mvn -B license:add-third-party "-Djavafx.platform=win"
|
||||
mvn -B license:add-third-party
|
||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||
"-Dlicense.outputDirectory=dist/win/resources"
|
||||
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
||||
@@ -257,16 +266,16 @@ jobs:
|
||||
env:
|
||||
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
|
||||
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
|
||||
- name: Sign msi with Actalis CodeSigner
|
||||
- name: Sign MSI with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: 'installer'
|
||||
file-extensions: 'msi'
|
||||
sign-description: 'Cryptomator Installer'
|
||||
sign-url: 'https://cryptomator.org'
|
||||
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
|
||||
password: ${{ secrets.WIN_CODESIGN_PW }}
|
||||
base-dir: ${{ github.workspace }}\installer
|
||||
file-extensions: msi
|
||||
description: 'Cryptomator Installer'
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Add possible alpha/beta tags and architecture to installer name
|
||||
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
@@ -277,7 +286,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: msi-${{ matrix.arch }}
|
||||
path: |
|
||||
@@ -298,28 +307,22 @@ jobs:
|
||||
java-dist: 'zulu'
|
||||
java-version: '24.0.1+9'
|
||||
java-package: 'jdk'
|
||||
- arch: arm64
|
||||
os: windows-11-arm
|
||||
executable-suffix: arm64
|
||||
java-dist: 'liberica'
|
||||
java-version: '24.0.1+11'
|
||||
java-package: 'jdk+fx' #This is needed, as liberica contains JFX 24 Jmods for Windows ARM64
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Install wix and extensions
|
||||
run: |
|
||||
dotnet tool install --global wix --version 6.0.0
|
||||
wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.0 --global
|
||||
wix.exe extension add WixToolset.Util.wixext/6.0.0 --global
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: msi-${{ matrix.arch }}
|
||||
path: dist/win/bundle/resources
|
||||
- name: Strip version info from msi file name
|
||||
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ matrix.java-dist }}
|
||||
java-version: ${{ matrix.java-version }}
|
||||
@@ -328,7 +331,7 @@ jobs:
|
||||
cache: 'maven'
|
||||
- name: Generate license for exe
|
||||
run: >
|
||||
mvn -B license:add-third-party "-Djavafx.platform=win"
|
||||
mvn -B license:add-third-party
|
||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
||||
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
||||
@@ -369,9 +372,20 @@ jobs:
|
||||
- name: Detach burn engine in preparation to sign
|
||||
run: >
|
||||
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
|
||||
- name: Sign WiX burn engine with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\tmp
|
||||
file-extensions: exe
|
||||
append-signature: true
|
||||
description: 'Cryptomator Bundle Installer'
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign burn engine with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'tmp'
|
||||
file-extensions: 'exe'
|
||||
@@ -382,9 +396,20 @@ jobs:
|
||||
- name: Reattach signed burn engine to installer
|
||||
run: >
|
||||
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
||||
- name: Sign EXE installer with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\installer
|
||||
file-extensions: exe
|
||||
append-signature: true
|
||||
description: 'Cryptomator Bundle Installer'
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign installer with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'installer'
|
||||
file-extensions: 'exe'
|
||||
@@ -402,7 +427,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: exe-${{ matrix.executable-suffix }}
|
||||
path: |
|
||||
@@ -417,26 +442,22 @@ jobs:
|
||||
needs: [ build-msi, build-exe ]
|
||||
outputs:
|
||||
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
|
||||
download-url-msi-arm64: ${{ fromJSON(steps.publish.outputs.assets)[1].browser_download_url }}
|
||||
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
|
||||
download-url-exe-arm64: ${{ fromJSON(steps.publish.outputs.assets)[3].browser_download_url }}
|
||||
steps:
|
||||
- name: Download installers
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Publish installers on GitHub Releases
|
||||
id: publish
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
# do not change ordering of filelist, required for correct job output
|
||||
files: |
|
||||
*x64.msi
|
||||
*arm64.msi
|
||||
*x64.exe
|
||||
*arm64.exe
|
||||
*.asc
|
||||
|
||||
allowlist-msi-x64:
|
||||
@@ -446,13 +467,6 @@ jobs:
|
||||
url: ${{ needs.publish.outputs.download-url-msi-x64 }}
|
||||
secrets: inherit
|
||||
|
||||
allowlist-msi-arm64:
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
needs: [ publish ]
|
||||
with:
|
||||
url: ${{ needs.publish.outputs.download-url-msi-arm64 }}
|
||||
secrets: inherit
|
||||
|
||||
allowlist-exe-x64:
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
needs: [ publish, allowlist-msi-x64 ]
|
||||
@@ -460,13 +474,6 @@ jobs:
|
||||
url: ${{ needs.publish.outputs.download-url-exe-x64 }}
|
||||
secrets: inherit
|
||||
|
||||
allowlist-exe-arm64:
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
needs: [ publish, allowlist-msi-arm64 ]
|
||||
with:
|
||||
url: ${{ needs.publish.outputs.download-url-exe-arm64 }}
|
||||
secrets: inherit
|
||||
|
||||
notify-winget:
|
||||
name: Notify for winget-release
|
||||
if: needs.get-version.outputs.versionType == 'stable'
|
||||
@@ -474,7 +481,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
|
||||
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Submit package
|
||||
uses: vedantmgoyal2009/winget-releaser@main
|
||||
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
|
||||
with:
|
||||
identifier: Cryptomator.Cryptomator
|
||||
version: ${{ inputs.tag }}
|
||||
|
||||
48
.idea/compiler.xml
generated
48
.idea/compiler.xml
generated
@@ -12,18 +12,15 @@
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<option name="dagger.fastInit" value="enabled" />
|
||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.55/dagger-compiler-2.55.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.55/dagger-2.55.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.0.21-1.0.28/symbol-processing-api-2.0.21-1.0.28.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||
@@ -31,14 +28,41 @@
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.41.0/checker-qual-3.41.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||
</processorPath>
|
||||
<module name="cryptomator" />
|
||||
</profile>
|
||||
@@ -46,7 +70,7 @@
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" />
|
||||
<module name="cryptomator" options="-Adagger.fastInit=enabled" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/inspectionProfiles/Project_Default.xml
generated
8
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,10 +1,8 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="Deprecation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="DEPRECATED_ATTRIBUTES" />
|
||||
<inspection_tool class="MarkedForRemoval" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
|
||||
<inspection_tool class="RedundantScheduledForRemovalAnnotation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
|
||||
</profile>
|
||||
</component>
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,7 +8,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" project-jdk-name="24" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
43
CHANGELOG.md
Normal file
43
CHANGELOG.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
The changelog starts with version 1.19.0.
|
||||
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
|
||||
|
||||
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
|
||||
|
||||
### Added
|
||||
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
|
||||
* Implemented `.dmg` update mechanism
|
||||
* Implemented Flatpak update mechanism
|
||||
* App notifications ([#4069](https://github.com/cryptomator/cryptomator/pull/4069))
|
||||
* Mark files in-use for Hub vaults ([#4078](https://github.com/cryptomator/cryptomator/pull/4078))
|
||||
* Accessibility: Adjust app to be used with a screen reader ([#547](https://github.com/cryptomator/cryptomator/issues/547))
|
||||
* Show Archived Vault Dialog on unlock when Hub returns 410 ([#4081](https://github.com/cryptomator/cryptomator/pull/4081))
|
||||
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
|
||||
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
|
||||
|
||||
### Changed
|
||||
* Built using JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/issues/4031))
|
||||
* Modernized Template for GitHub Releases
|
||||
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
|
||||
* Updated dependencies
|
||||
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
|
||||
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
|
||||
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
|
||||
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
|
||||
* `com.google.dagger:*` from 2.57.2 to 2.59.1
|
||||
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
|
||||
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
|
||||
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
|
||||
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
|
||||
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
|
||||
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
|
||||
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
|
||||
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
|
||||
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1
|
||||
|
||||
|
||||
8
dist/common/cryptomator.config
vendored
Normal file
8
dist/common/cryptomator.config
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# This is the Cryptomator administrative configuration file.
|
||||
# It is a simple key-value pair file.
|
||||
# Lines starting with '#' are comments and will be ignored.
|
||||
# For more info, read the docs at https://docs.cryptomator.org/desktop/advanced-settings/
|
||||
#
|
||||
# Example:
|
||||
# Sets the plugin directory and enables plugin loading
|
||||
# cryptomator.pluginDir=@{userhome}/Cryptomator/Plugins
|
||||
13
dist/linux/appimage/build.sh
vendored
13
dist/linux/appimage/build.sh
vendored
@@ -19,16 +19,16 @@ if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not
|
||||
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
||||
|
||||
# compile
|
||||
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=linux
|
||||
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
|
||||
cp ../../../LICENSE.txt ../../../target
|
||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||
|
||||
JAVAFX_VERSION=24.0.1
|
||||
JAVAFX_VERSION=25
|
||||
JAVAFX_ARCH="x64"
|
||||
JAVAFX_JMODS_SHA256='425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
|
||||
JAVAFX_JMODS_SHA256='96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
||||
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
||||
JAVAFX_ARCH="aarch64"
|
||||
JAVAFX_JMODS_SHA256='7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
|
||||
JAVAFX_JMODS_SHA256='951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1'
|
||||
fi
|
||||
|
||||
# download javaFX jmods
|
||||
@@ -62,7 +62,7 @@ ${JAVA_HOME}/bin/jlink \
|
||||
--verbose \
|
||||
--output runtime \
|
||||
--module-path "${JMOD_PATHS}" \
|
||||
--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.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
@@ -88,8 +88,8 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--app-version "${VERSION}.${REVISION_NO}" \
|
||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||
--java-options "-Djava.net.useSystemProxies=true" \
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||
@@ -115,6 +115,7 @@ cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/us
|
||||
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
||||
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||
cp ../common/application-vnd.cryptomator.encrypted.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
|
||||
|
||||
9
dist/linux/common/application-vnd.cryptomator.encrypted.xml
vendored
Normal file
9
dist/linux/common/application-vnd.cryptomator.encrypted.xml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/vnd.cryptomator.encrypted">
|
||||
<comment>Cryptomator Encrypted Data</comment>
|
||||
<glob pattern="*.c9r"/>
|
||||
<glob pattern="*.c9s"/>
|
||||
<glob pattern="*.c9u"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -83,6 +83,9 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2025-11-12" version="1.18.0">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
|
||||
</release>
|
||||
<release date="2025-07-08" version="1.17.1">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.17.1</url>
|
||||
</release>
|
||||
|
||||
4
dist/linux/debian/control
vendored
4
dist/linux/debian/control
vendored
@@ -2,7 +2,7 @@ Source: cryptomator
|
||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-24 (>= 24.0.1+9-0ppa3), libgtk-3-0, libxxf86vm1, libgl1
|
||||
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
|
||||
Standards-Version: 4.5.0
|
||||
Homepage: https://cryptomator.org
|
||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||
@@ -12,7 +12,7 @@ Package: cryptomator
|
||||
Architecture: any
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3, libgtk-3-0 (>= 3.20.0)
|
||||
Recommends: gvfs-backends, gvfs-fuse, gnome-keyring
|
||||
XB-AppName: Cryptomator
|
||||
XB-Category: Utility;Security;FileTools;
|
||||
|
||||
3
dist/linux/debian/cryptomator.install
vendored
3
dist/linux/debian/cryptomator.install
vendored
@@ -6,4 +6,5 @@ common/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/sca
|
||||
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
|
||||
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
|
||||
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
|
||||
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
|
||||
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
|
||||
common/application-vnd.cryptomator.encrypted.xml usr/share/mime/packages
|
||||
1
dist/linux/debian/postinst
vendored
1
dist/linux/debian/postinst
vendored
@@ -25,6 +25,7 @@ case "$1" in
|
||||
fi
|
||||
xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
|
||||
1
dist/linux/debian/prerm
vendored
1
dist/linux/debian/prerm
vendored
@@ -23,6 +23,7 @@ case "$1" in
|
||||
|
||||
xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||
;;
|
||||
|
||||
failed-upgrade)
|
||||
|
||||
8
dist/linux/debian/rules
vendored
8
dist/linux/debian/rules
vendored
@@ -4,11 +4,12 @@
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
JAVA_HOME = /usr/lib/jvm/java-24-coffeelibs
|
||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64
|
||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||
else ifeq ($(DEB_BUILD_ARCH),arm64)
|
||||
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64
|
||||
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
|
||||
endif
|
||||
|
||||
@@ -28,7 +29,7 @@ override_dh_auto_build:
|
||||
$(JAVA_HOME)/bin/jlink \
|
||||
--output runtime \
|
||||
--module-path "${JMODS_PATH}" \
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
@@ -45,14 +46,13 @@ override_dh_auto_build:
|
||||
--vendor "Skymatic GmbH" \
|
||||
--java-options "--enable-preview" \
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow" \
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||
--java-options "-Djava.net.useSystemProxies=true" \
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||
|
||||
1
dist/mac/.gitignore
vendored
1
dist/mac/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
embedded.provisionprofile
|
||||
xcuserdata/
|
||||
|
||||
19
dist/mac/DockTilePlugin/CryptomatorDockTilePlugin.swift
vendored
Normal file
19
dist/mac/DockTilePlugin/CryptomatorDockTilePlugin.swift
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// CryptomatorDockTilePlugin.swift
|
||||
// Integrations
|
||||
//
|
||||
// Created by Tobias Hagemann on 22.09.25.
|
||||
// Copyright © 2025 Cryptomator. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
class CryptomatorDockTilePlugin: NSObject, NSDockTilePlugIn {
|
||||
func setDockTile(_ dockTile: NSDockTile?) {
|
||||
guard let dockTile = dockTile, let image = Bundle(for: Self.self).image(forResource: "Cryptomator") else {
|
||||
return
|
||||
}
|
||||
dockTile.contentView = NSImageView(image: image)
|
||||
dockTile.display()
|
||||
}
|
||||
}
|
||||
314
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.pbxproj
vendored
Normal file
314
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.pbxproj
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */; };
|
||||
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */ = {isa = PBXBuildFile; fileRef = 74E08DEC2E858532007E665C /* Cryptomator.icns */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cryptomator.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptomatorDockTilePlugin.swift; sourceTree = "<group>"; };
|
||||
74E08DEC2E858532007E665C /* Cryptomator.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Cryptomator.icns; path = ../resources/Cryptomator.icns; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
74E08DD62E858467007E665C /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
74E08DD02E858467007E665C = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */,
|
||||
74E08DEC2E858532007E665C /* Cryptomator.icns */,
|
||||
74E08DDA2E858467007E665C /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
74E08DDA2E858467007E665C /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
74E08DD82E858467007E665C /* DockTilePlugin */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */;
|
||||
buildPhases = (
|
||||
74E08DD52E858467007E665C /* Sources */,
|
||||
74E08DD62E858467007E665C /* Frameworks */,
|
||||
74E08DD72E858467007E665C /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = DockTilePlugin;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = DockTilePlugin;
|
||||
productReference = 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
74E08DD12E858467007E665C /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastUpgradeCheck = 2600;
|
||||
ORGANIZATIONNAME = Cryptomator;
|
||||
TargetAttributes = {
|
||||
74E08DD82E858467007E665C = {
|
||||
CreatedOnToolsVersion = 26.0.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 74E08DD02E858467007E665C;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 74E08DDA2E858467007E665C /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
74E08DD82E858467007E665C /* DockTilePlugin */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
74E08DD72E858467007E665C /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
74E08DD52E858467007E665C /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
74E08DDB2E858467007E665C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = YZQJQUHA3L;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
74E08DDC2E858467007E665C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = YZQJQUHA3L;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
74E08DDE2E858467007E665C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
|
||||
PRODUCT_NAME = Cryptomator;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
WRAPPER_EXTENSION = docktileplugin;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
74E08DDF2E858467007E665C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
|
||||
PRODUCT_NAME = Cryptomator;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
WRAPPER_EXTENSION = docktileplugin;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
74E08DDB2E858467007E665C /* Debug */,
|
||||
74E08DDC2E858467007E665C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
74E08DDE2E858467007E665C /* Debug */,
|
||||
74E08DDF2E858467007E665C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 74E08DD12E858467007E665C /* Project object */;
|
||||
}
|
||||
7
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
7
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
67
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/xcshareddata/xcschemes/DockTilePlugin.xcscheme
vendored
Normal file
67
dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/xcshareddata/xcschemes/DockTilePlugin.xcscheme
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "74E08DD82E858467007E665C"
|
||||
BuildableName = "Cryptomator.docktileplugin"
|
||||
BlueprintName = "DockTilePlugin"
|
||||
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "74E08DD82E858467007E665C"
|
||||
BuildableName = "Cryptomator.docktileplugin"
|
||||
BlueprintName = "DockTilePlugin"
|
||||
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
28
dist/mac/dmg/build.sh
vendored
28
dist/mac/dmg/build.sh
vendored
@@ -32,15 +32,15 @@ REVISION_NO=`git rev-list --count HEAD`
|
||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||
FUSE_LIB="FUSE-T"
|
||||
|
||||
JAVAFX_VERSION=24.0.1
|
||||
JAVAFX_VERSION=25
|
||||
JAVAFX_ARCH="undefined"
|
||||
JAVAFX_JMODS_SHA256="undefined"
|
||||
if [ "$(machine)" = "arm64e" ]; then
|
||||
JAVAFX_ARCH="aarch64"
|
||||
JAVAFX_JMODS_SHA256="b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4"
|
||||
JAVAFX_JMODS_SHA256="13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba"
|
||||
else
|
||||
JAVAFX_ARCH="x64"
|
||||
JAVAFX_JMODS_SHA256="6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105"
|
||||
JAVAFX_JMODS_SHA256="0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52"
|
||||
fi
|
||||
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||
|
||||
@@ -71,7 +71,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
||||
fi
|
||||
|
||||
# compile
|
||||
mvn -B -Djavafx.platform=mac -f../../../pom.xml clean package -DskipTests -Pmac
|
||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
||||
cp ../../../LICENSE.txt ../../../target
|
||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||
|
||||
@@ -85,7 +85,7 @@ fi
|
||||
${JAVA_HOME}/bin/jlink \
|
||||
--output runtime \
|
||||
--module-path "${JMOD_PATHS}" \
|
||||
--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.security.auth,jdk.accessibility,jdk.management.jfr,java.compiler \
|
||||
--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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,java.compiler \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
@@ -114,15 +114,16 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
||||
--java-options "-Dsun.java2d.metal=true" \
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" \
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
|
||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
|
||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
|
||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||
--resource-dir ../resources
|
||||
@@ -133,8 +134,21 @@ sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
||||
|
||||
# build and install dock tile plugin
|
||||
echo "Building and installing Cryptomator.docktileplugin..."
|
||||
DERIVED_DATA_PATH=../DockTilePlugin/build
|
||||
xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p ${APP_NAME}.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
|
||||
# generate license
|
||||
mvn -B -Djavafx.platform=mac -f../../../pom.xml license:add-third-party \
|
||||
mvn -B -f../../../pom.xml license:add-third-party \
|
||||
-Dlicense.thirdPartyFilename=license.rtf \
|
||||
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
||||
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
||||
|
||||
4
dist/mac/resources/Info.plist
vendored
4
dist/mac/resources/Info.plist
vendored
@@ -105,6 +105,7 @@
|
||||
<array>
|
||||
<string>c9r</string>
|
||||
<string>c9s</string>
|
||||
<string>c9u</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
@@ -116,5 +117,8 @@
|
||||
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<!-- register dock tile plugin -->
|
||||
<key>NSDockTilePlugIn</key>
|
||||
<string>Cryptomator.docktileplugin</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
16
dist/win/build.ps1
vendored
16
dist/win/build.ps1
vendored
@@ -65,7 +65,7 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
|
||||
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
||||
|
||||
# compile
|
||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin "-Djavafx.platform=win"
|
||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
|
||||
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
||||
|
||||
# add runtime
|
||||
@@ -86,16 +86,16 @@ switch ($archName) {
|
||||
'ARM64' {
|
||||
$javafxBaseJmod = Join-Path $Env:JAVA_HOME "jmods\javafx.base.jmod"
|
||||
if (!(Test-Path $javafxBaseJmod)) {
|
||||
Write-Error "JavaFX module not found in JDK. Please ensure full JDK (including jmods) is installed."
|
||||
Write-Error "JavaFX module not found in JDK. Please ensure a JDK with JavaFX (including jmods) is installed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$jmodPaths = "$Env:JAVA_HOME/jmods"
|
||||
}
|
||||
'x64' {
|
||||
$javaFxVersion='24.0.1'
|
||||
$javaFxVersion='25'
|
||||
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
||||
$javaFxJmodsSHA256 = 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
|
||||
$javaFxJmodsSHA256 = 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
|
||||
$javaFxJmods = '.\resources\jfxJmods.zip'
|
||||
|
||||
if( !(Test-Path -Path $javaFxJmods) ) {
|
||||
@@ -133,7 +133,7 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
|
||||
--verbose `
|
||||
--output runtime `
|
||||
--module-path $jmodPaths `
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.mscapi,java.compiler,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.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
||||
--strip-native-commands `
|
||||
--no-header-files `
|
||||
--no-man-pages `
|
||||
@@ -155,7 +155,7 @@ $javaOptions = @(
|
||||
"--java-options", "-Djava.net.useSystemProxies=true"
|
||||
"--java-options", "-Dcryptomator.logDir=`"@{localappdata}/$AppName`""
|
||||
"--java-options", "-XX:ErrorFile=`"C:/cryptomator/cryptomator_crash.log`""
|
||||
"--java-options", "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`""
|
||||
"--java-options", "-Dcryptomator.adminConfigPath=`"C:/ProgramData/$AppName/config.properties`""
|
||||
"--java-options", "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
|
||||
"--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
|
||||
"--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
|
||||
@@ -194,7 +194,7 @@ if ($LASTEXITCODE -ne 0) {
|
||||
}
|
||||
|
||||
#Create RTF license for msi
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
||||
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
||||
@@ -237,7 +237,7 @@ if ($LASTEXITCODE -ne 0) {
|
||||
}
|
||||
|
||||
#Create RTF license for bundle
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
|
||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
||||
|
||||
12
dist/win/contrib/disableUserConfig.bat
vendored
Normal file
12
dist/win/contrib/disableUserConfig.bat
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
:: Batch wrapper for PowerShell script to disable user configuration in Cryptomator
|
||||
:: This is executed as a Custom Action during MSI installation
|
||||
:: This file must be located in the INSTALLDIR
|
||||
|
||||
:: Change to INSTALLDIR
|
||||
cd %~dp0
|
||||
:: Execute the PowerShell script
|
||||
powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File ".\disableUserConfig.ps1"
|
||||
|
||||
:: Return the exit code from PowerShell
|
||||
exit /b %ERRORLEVEL%
|
||||
24
dist/win/contrib/disableUserConfig.ps1
vendored
Normal file
24
dist/win/contrib/disableUserConfig.ps1
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# PowerShell script to disable user configuration
|
||||
# This script is executed as a Custom Action during MSI installation
|
||||
# It deletes the file .package, effectively disabling user specific jpackage configuration.
|
||||
# NOTE: This file must be located in the same directory as set in the MSI property INSTALLDIR
|
||||
|
||||
try {
|
||||
|
||||
# Determine file path
|
||||
$packageFile = Join-Path $PSScriptRoot 'app\.package'
|
||||
|
||||
#check if file exists
|
||||
if (Test-Path -Path $packageFile) {
|
||||
Write-Host "Deleting file: $packageFile"
|
||||
Remove-Item -Path $packageFile -Force -ErrorAction Stop
|
||||
} else {
|
||||
Write-Host "File not found: $packageFile. Skipping deletion."
|
||||
}
|
||||
|
||||
exit 0
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error deleting package file: $_"
|
||||
exit 1
|
||||
}
|
||||
33
dist/win/resources/main.wxs
vendored
33
dist/win/resources/main.wxs
vendored
@@ -87,17 +87,37 @@
|
||||
|
||||
<!-- Non-Opening ProgID -->
|
||||
<ns0:DirectoryRef Id="INSTALLDIR">
|
||||
<ns0:Component Bitness="always64" Id="nonStartingProgID" >
|
||||
<ns0:Component Bitness="always64" Id="nonStartingProgID">
|
||||
<ns0:File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileEncryptedData)" Name="$(var.IconFileEncryptedData)"/>
|
||||
<ns0:ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
|
||||
<ns0:Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
|
||||
<ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/>
|
||||
</ns0:Extension>
|
||||
<ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||
</ns0:ProgId>
|
||||
</ns0:Component>
|
||||
</ns0:DirectoryRef>
|
||||
|
||||
<ns0:StandardDirectory Id="CommonAppDataFolder">
|
||||
<ns0:Directory Id="CryptomatorDesktopProgramData" Name="Cryptomator">
|
||||
<ns0:Component Id="AdminConfigDir" Guid="c078b7da-ba6e-4069-a5ab-5c0f0f9856a0">
|
||||
<ns0:CreateFolder>
|
||||
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||
</ns0:CreateFolder>
|
||||
</ns0:Component>
|
||||
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
|
||||
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
|
||||
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||
</ns0:File>
|
||||
</ns0:Component>
|
||||
</ns0:Directory>
|
||||
</ns0:StandardDirectory>
|
||||
|
||||
<!-- Standard required root -->
|
||||
|
||||
<ns0:Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
|
||||
@@ -105,7 +125,9 @@
|
||||
<ns0:ComponentGroupRef Id="Files"/>
|
||||
<ns0:ComponentGroupRef Id="FileAssociations"/>
|
||||
<!-- Ref to additional ProgIDs -->
|
||||
<ns0:ComponentRef Id="nonStartingProgID" />
|
||||
<ns0:ComponentRef Id="nonStartingProgID"/>
|
||||
<ns0:ComponentRef Id="AdminConfigDir"/>
|
||||
<ns0:ComponentRef Id="AdminConfigFile"/>
|
||||
</ns0:Feature>
|
||||
|
||||
<ns0:CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
|
||||
@@ -131,6 +153,10 @@
|
||||
<!-- Property for controlling update check behavior (can be set via command line) -->
|
||||
<ns0:Property Id="DISABLEUPDATECHECK" Secure="yes" />
|
||||
|
||||
<!-- Disable user config -->
|
||||
<ns0:SetProperty Id="DisableUserConfig" Value=""[INSTALLDIR]disableUserConfig.bat"" Sequence="execute" Before="DisableUserConfig" />
|
||||
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||
|
||||
<!-- WebDAV patches -->
|
||||
<ns0:SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)"" Sequence="execute" Before="PatchWebDAV" />
|
||||
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||
@@ -187,9 +213,10 @@
|
||||
<ns0:Custom Action="FailOnRunningApp" After="Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Condition="FOUNDRUNNINGAPP" />
|
||||
|
||||
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
|
||||
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||
<!-- Skip action on uninstall -->
|
||||
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
|
||||
<ns0:Custom Action="PatchWebDAV" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||
<!-- Configure update check setting if property is provided -->
|
||||
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||
</ns0:InstallExecuteSequence>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
|
||||
MIT License|The MIT License (MIT)|The MIT License|MIT license
|
||||
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
|
||||
Apache License v2.0|Apache License, Version 2.0|The Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
|
||||
MIT License|MIT|The MIT License (MIT)|The MIT License|MIT license
|
||||
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1|GNU Lesser General Public License
|
||||
GPLv2|GNU General Public License Version 2
|
||||
GPLv2+CE|CDDL + GPLv2 with classpath exception
|
||||
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
|
||||
|
||||
53
pom.xml
53
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptomator</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<version>1.19.0-SNAPSHOT</version>
|
||||
<name>Cryptomator Desktop App</name>
|
||||
|
||||
<organization>
|
||||
@@ -26,30 +26,32 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.jdk.version>24</project.jdk.version>
|
||||
<project.jdk.version>25</project.jdk.version>
|
||||
|
||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.7.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.4.1</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.6.1</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version>
|
||||
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version>
|
||||
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
|
||||
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
|
||||
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||
<dagger.version>2.57.1</dagger.version>
|
||||
<caffeine.version>3.2.3</caffeine.version>
|
||||
<commons-lang3.version>3.20.0</commons-lang3.version>
|
||||
<dagger.version>2.59.1</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<jackson.version>2.20.0</jackson.version>
|
||||
<javafx.version>24.0.1</javafx.version>
|
||||
<jackson.version>2.21.0</jackson.version>
|
||||
<javafx.version>25</javafx.version>
|
||||
<jwt.version>4.5.0</jwt.version>
|
||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
||||
<logback.version>1.5.18</logback.version>
|
||||
<nimbus-jose.version>10.5</nimbus-jose.version>
|
||||
<logback.version>1.5.31</logback.version>
|
||||
<slf4j.version>2.0.17</slf4j.version>
|
||||
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||
@@ -62,7 +64,7 @@
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
|
||||
<dependency-check.version>12.1.5</dependency-check.version>
|
||||
<jacoco.version>0.8.13</jacoco.version>
|
||||
<jacoco.version>0.8.14</jacoco.version>
|
||||
<license-generator.version>2.7.0</license-generator.version>
|
||||
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
||||
<mvn-compiler.version>3.14.1</mvn-compiler.version>
|
||||
@@ -75,12 +77,26 @@
|
||||
<surefire.jacoco.args></surefire.jacoco.args>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<name>Central Portal Snapshots</name>
|
||||
<id>central-portal-snapshots</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Cryptomator Libs -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<version>${cryptomator.cryptolib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
@@ -214,7 +230,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
<!-- JUnit / Mockito / Hamcrest -->
|
||||
<dependency>
|
||||
@@ -316,7 +332,6 @@
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Adagger.fastInit=enabled</arg>
|
||||
<arg>-Adagger.formatGeneratedSource=enabled</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import ch.qos.logback.classic.spi.Configurator;
|
||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||
@@ -14,11 +13,14 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
|
||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||
import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
open module org.cryptomator.desktop {
|
||||
@@ -50,17 +52,18 @@ open module org.cryptomator.desktop {
|
||||
requires io.github.coffeelibs.tinyoauth2client;
|
||||
requires org.slf4j;
|
||||
requires org.apache.commons.lang3;
|
||||
requires com.github.benmanes.caffeine;
|
||||
|
||||
/* dagger bs */
|
||||
requires jakarta.inject;
|
||||
requires static javax.inject;
|
||||
requires java.compiler;
|
||||
requires com.github.benmanes.caffeine;
|
||||
|
||||
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||
uses SSLContextProvider;
|
||||
uses org.cryptomator.event.NotificationHandler;
|
||||
|
||||
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||
|
||||
@@ -74,13 +74,6 @@ public abstract class CommonsModule {
|
||||
return new MasterkeyFileAccess(Constants.PEPPER, csprng);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("SemVer")
|
||||
static Comparator<String> providesSemVerComparator() {
|
||||
return new SemVerComparator();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<RevealPathService> provideRevealPathService() {
|
||||
|
||||
@@ -13,5 +13,7 @@ public interface Constants {
|
||||
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
||||
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
|
||||
byte[] PEPPER = new byte[0];
|
||||
// Separator used to concatenate Hub username and device name in the filesystem owner identifier.
|
||||
String HUB_USER_DEVICE_SEPARATOR = "&";
|
||||
|
||||
}
|
||||
|
||||
@@ -124,6 +124,15 @@ public class Environment {
|
||||
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app version concatenated with the build number (if defined).
|
||||
*
|
||||
* @return version string formatted like {@code 1.2.3-4567} or {@code 1.2.3} if no build number is defined.
|
||||
*/
|
||||
public String getAppVersionWithBuildNumber() {
|
||||
return getAppVersion() + getBuildNumber().map("-"::concat).orElse("");
|
||||
}
|
||||
|
||||
public Optional<Path> getPluginDir() {
|
||||
return getPath(PLUGIN_DIR_PROP_NAME);
|
||||
}
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||
import org.cryptomator.event.VaultEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.MapChangeListener;
|
||||
import javafx.collections.ObservableMap;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Map containing {@link VaultEvent}s.
|
||||
* The map is keyed by the ciphertext path of the affected resource _and_ the {@link FilesystemEvent}s class in order to group same events
|
||||
* <p>
|
||||
* Use {@link EventMap#put(VaultEvent)} to add an element and {@link EventMap#remove(VaultEvent)} to remove it.
|
||||
* <p>
|
||||
* The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed.
|
||||
*/
|
||||
@Singleton
|
||||
public class EventMap implements ObservableMap<EventMap.EventKey, VaultEvent> {
|
||||
|
||||
private static final int MAX_SIZE = 300;
|
||||
|
||||
public record EventKey(Path ciphertextPath, Class<? extends FilesystemEvent> c) {}
|
||||
|
||||
private final ObservableMap<EventMap.EventKey, VaultEvent> delegate;
|
||||
|
||||
@Inject
|
||||
public EventMap() {
|
||||
delegate = FXCollections.observableHashMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
|
||||
delegate.addListener(mapChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
|
||||
delegate.removeListener(mapChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultEvent get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable VaultEvent put(EventKey key, VaultEvent value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultEvent remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends EventKey, ? extends VaultEvent> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<EventKey> keySet() {
|
||||
return delegate.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<VaultEvent> values() {
|
||||
return delegate.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Entry<EventKey, VaultEvent>> entrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(InvalidationListener invalidationListener) {
|
||||
delegate.addListener(invalidationListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(InvalidationListener invalidationListener) {
|
||||
delegate.removeListener(invalidationListener);
|
||||
}
|
||||
|
||||
public synchronized void put(VaultEvent e) {
|
||||
//compute key
|
||||
var key = computeKey(e.actualEvent());
|
||||
//if-else
|
||||
var nullOrEntry = delegate.get(key);
|
||||
if (nullOrEntry == null) {
|
||||
if (size() == MAX_SIZE) {
|
||||
delegate.entrySet().stream() //
|
||||
.min(Comparator.comparing(entry -> entry.getValue().actualEvent().getTimestamp())) //
|
||||
.ifPresent(oldestEntry -> delegate.remove(oldestEntry.getKey()));
|
||||
}
|
||||
delegate.put(key, e);
|
||||
} else {
|
||||
delegate.put(key, nullOrEntry.incrementCount(e.actualEvent()));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized VaultEvent remove(VaultEvent similar) {
|
||||
//compute key
|
||||
var key = computeKey(similar.actualEvent());
|
||||
return this.remove(key);
|
||||
}
|
||||
|
||||
private EventKey computeKey(FilesystemEvent e) {
|
||||
var p = switch (e) {
|
||||
case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath;
|
||||
case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext;
|
||||
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
|
||||
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
|
||||
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
|
||||
};
|
||||
return new EventKey(p, e.getClass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Interface marking a class to be used in {@link org.cryptomator.cryptofs.CryptoFileSystemProperties.Builder#withOwnerGetter(Supplier)}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FilesystemOwnerSupplier {
|
||||
|
||||
/**
|
||||
* Get the filesystem owner.
|
||||
*
|
||||
* @return the filesystem owner
|
||||
*/
|
||||
String getOwner();
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2017 Sebastian Stenzel and others.
|
||||
* All rights reserved.
|
||||
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
|
||||
*/
|
||||
public class SemVerComparator implements Comparator<String> {
|
||||
|
||||
private static final char VERSION_SEP = '.'; // http://semver.org/spec/v2.0.0.html#spec-item-2
|
||||
private static final String PRE_RELEASE_SEP = "-"; // http://semver.org/spec/v2.0.0.html#spec-item-9
|
||||
private static final String BUILD_SEP = "+"; // http://semver.org/spec/v2.0.0.html#spec-item-10
|
||||
|
||||
@Override
|
||||
public int compare(String version1, String version2) {
|
||||
// "Build metadata SHOULD be ignored when determining version precedence.
|
||||
// Thus two versions that differ only in the build metadata, have the same precedence."
|
||||
String v1WithoutBuildMetadata = StringUtils.substringBefore(version1, BUILD_SEP);
|
||||
String v2WithoutBuildMetadata = StringUtils.substringBefore(version2, BUILD_SEP);
|
||||
|
||||
if (v1WithoutBuildMetadata.equals(v2WithoutBuildMetadata)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String v1MajorMinorPatch = StringUtils.substringBefore(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
|
||||
String v2MajorMinorPatch = StringUtils.substringBefore(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
|
||||
String v1PreReleaseVersion = StringUtils.substringAfter(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
|
||||
String v2PreReleaseVersion = StringUtils.substringAfter(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
|
||||
return compare(v1MajorMinorPatch, v1PreReleaseVersion, v2MajorMinorPatch, v2PreReleaseVersion);
|
||||
}
|
||||
|
||||
private int compare(String v1MajorMinorPatch, String v1PreReleaseVersion, String v2MajorMinorPatch, String v2PreReleaseVersion) {
|
||||
int comparisonResult = compareNumericallyThenLexicographically(v1MajorMinorPatch, v2MajorMinorPatch);
|
||||
if (comparisonResult == 0) {
|
||||
if (v1PreReleaseVersion.isEmpty()) {
|
||||
return 1; // 1.0.0 > 1.0.0-BETA
|
||||
} else if (v2PreReleaseVersion.isEmpty()) {
|
||||
return -1; // 1.0.0-BETA < 1.0.0
|
||||
} else {
|
||||
return compareNumericallyThenLexicographically(v1PreReleaseVersion, v2PreReleaseVersion);
|
||||
}
|
||||
} else {
|
||||
return comparisonResult;
|
||||
}
|
||||
}
|
||||
|
||||
private int compareNumericallyThenLexicographically(String version1, String version2) {
|
||||
final String[] vComps1 = StringUtils.split(version1, VERSION_SEP);
|
||||
final String[] vComps2 = StringUtils.split(version2, VERSION_SEP);
|
||||
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
|
||||
|
||||
for (int i = 0; i < commonCompCount; i++) {
|
||||
int subversionComparisonResult = 0;
|
||||
try {
|
||||
final int v1 = Integer.parseInt(vComps1[i]);
|
||||
final int v2 = Integer.parseInt(vComps2[i]);
|
||||
subversionComparisonResult = v1 - v2;
|
||||
} catch (NumberFormatException ex) {
|
||||
// ok, lets compare this fragment lexicographically
|
||||
subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
|
||||
}
|
||||
if (subversionComparisonResult != 0) {
|
||||
return subversionComparisonResult;
|
||||
}
|
||||
}
|
||||
|
||||
// all in common so far? longest version string is considered the higher version:
|
||||
return vComps1.length - vComps2.length;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -167,6 +167,7 @@ public class Mounter {
|
||||
usedMountServices.add(mountService);
|
||||
|
||||
var builder = mountService.forFileSystem(cryptoFsRoot);
|
||||
LOG.debug("Using mount service {} for mounting vault {}", mountService.getClass().getName(), vaultSettings.displayName);
|
||||
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
||||
var cleanup = internal.prepare();
|
||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
|
||||
|
||||
public final class BackupRestorer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BackupRestorer.class);
|
||||
|
||||
private BackupRestorer() {}
|
||||
|
||||
public static void restoreIfBackupPresent(Path vaultPath, String filePrefix) {
|
||||
Path targetFile = vaultPath.resolve(filePrefix);
|
||||
|
||||
try (Stream<Path> files = Files.list(vaultPath)) {
|
||||
files.filter(file -> isFileMatchingPattern(file.getFileName().toString(), filePrefix))
|
||||
.max((f1, f2) -> {
|
||||
try {
|
||||
FileTime time1 = Files.getLastModifiedTime(f1);
|
||||
FileTime time2 = Files.getLastModifiedTime(f2);
|
||||
return time1.compareTo(time2);
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.ifPresent(backupFile -> copyBackupFile(backupFile, targetFile));
|
||||
} catch (IOException e) {
|
||||
LOG.info("Unable to restore backup files in '{}'", vaultPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFileMatchingPattern(String fileName, String filePrefix) {
|
||||
return fileName.startsWith(filePrefix) && fileName.endsWith(MASTERKEY_BACKUP_SUFFIX);
|
||||
}
|
||||
|
||||
private static void copyBackupFile(Path backupFile, Path configPath) {
|
||||
try {
|
||||
Files.copy(backupFile, configPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
LOG.debug("Backup restored - file: '{}' path: '{}'", backupFile, configPath);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Unable to copy backup file from '{}' to '{}'", backupFile, configPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
|
||||
import static org.cryptomator.common.Constants.DEFAULT_KEY_ID;
|
||||
|
||||
public final class CryptoFsInitializer {
|
||||
|
||||
private CryptoFsInitializer() {}
|
||||
|
||||
public static void init(Path recoveryPath,
|
||||
Masterkey masterkey,
|
||||
int shorteningThreshold,
|
||||
CryptorProvider.Scheme scheme) throws IOException, CryptoException {
|
||||
|
||||
MasterkeyLoader loader = ignored -> masterkey.copy();
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties //
|
||||
.cryptoFileSystemProperties() //
|
||||
.withCipherCombo(scheme) //
|
||||
.withKeyLoader(loader) //
|
||||
.withShorteningThreshold(shorteningThreshold) //
|
||||
.build();
|
||||
CryptoFileSystemProvider.initialize(recoveryPath, fsProps, DEFAULT_KEY_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.Cryptor;
|
||||
import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME;
|
||||
|
||||
public final class MasterkeyService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MasterkeyService.class);
|
||||
|
||||
private MasterkeyService() {}
|
||||
|
||||
public static void recoverFromRecoveryKey(String recoveryKey, RecoveryKeyFactory recoveryKeyFactory, Path recoveryPath, CharSequence newPassword) throws IOException {
|
||||
recoveryKeyFactory.newMasterkeyFileWithPassphrase(recoveryPath, recoveryKey, newPassword);
|
||||
}
|
||||
|
||||
public static Masterkey load(MasterkeyFileAccess masterkeyFileAccess, Path masterkeyFilePath, CharSequence password) throws IOException {
|
||||
return masterkeyFileAccess.load(masterkeyFilePath, password);
|
||||
}
|
||||
|
||||
public static CryptorProvider.Scheme validateRecoveryKeyAndDetectCombo(RecoveryKeyFactory recoveryKeyFactory, //
|
||||
Vault vault, String recoveryKey, //
|
||||
MasterkeyFileAccess masterkeyFileAccess) throws IOException, CryptoException, NoSuchElementException {
|
||||
String tmpPass = UUID.randomUUID().toString();
|
||||
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
|
||||
Path tempRecoveryPath = recoveryDirectory.getRecoveryPath();
|
||||
recoverFromRecoveryKey(recoveryKey, recoveryKeyFactory, tempRecoveryPath, tmpPass);
|
||||
Path masterkeyFilePath = tempRecoveryPath.resolve(MASTERKEY_FILENAME);
|
||||
|
||||
try (Masterkey mk = load(masterkeyFileAccess, masterkeyFilePath, tmpPass)) {
|
||||
return detect(mk, vault.getPath()).orElseThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<CryptorProvider.Scheme> detect(Masterkey masterkey, Path vaultPath) {
|
||||
try (Stream<Path> paths = Files.walk(vaultPath.resolve(DATA_DIR_NAME))) {
|
||||
Optional<Path> c9rFile = paths //
|
||||
.filter(p -> p.toString().endsWith(".c9r")) //
|
||||
.filter(p -> !p.endsWith("dir.c9r")) //
|
||||
.filter(Files::isRegularFile) //
|
||||
.findFirst();
|
||||
if (c9rFile.isEmpty()) {
|
||||
LOG.info("Unable to detect Crypto scheme: No *.c9r file found in {}", vaultPath);
|
||||
return Optional.empty();
|
||||
}
|
||||
return determineScheme(c9rFile.get(), masterkey);
|
||||
} catch (IOException e) {
|
||||
LOG.info("Unable to detect Crypto scheme: Failed to inspect vault", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<CryptorProvider.Scheme> determineScheme(Path c9rFile, Masterkey masterkey) {
|
||||
return Arrays.stream(CryptorProvider.Scheme.values()).filter(scheme -> {
|
||||
try (Cryptor cryptor = CryptorProvider.forScheme(scheme).provide(masterkey.copy(), SecureRandom.getInstanceStrong())) {
|
||||
int headerSize = cryptor.fileHeaderCryptor().headerSize();
|
||||
|
||||
ByteBuffer headerBuf = ByteBuffer.allocate(headerSize);
|
||||
|
||||
try (FileChannel channel = FileChannel.open(c9rFile, StandardOpenOption.READ)) {
|
||||
channel.read(headerBuf, 0);
|
||||
}
|
||||
|
||||
headerBuf.flip();
|
||||
cryptor.fileHeaderCryptor().decryptHeader(headerBuf.duplicate());
|
||||
LOG.debug("Detected Crypto scheme: {}", scheme);
|
||||
return true;
|
||||
} catch (IllegalArgumentException | CryptoException e) {
|
||||
LOG.debug("Could not decrypt with scheme: {}", scheme);
|
||||
return false;
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
LOG.warn("Unable to detect Crypto scheme: Failed to decrypt .c9r file", e);
|
||||
return false;
|
||||
}
|
||||
}).findFirst();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
public enum RecoveryActionType {
|
||||
RESTORE_ALL,
|
||||
RESTORE_MASTERKEY,
|
||||
RESTORE_VAULT_CONFIG,
|
||||
RESET_PASSWORD,
|
||||
SHOW_KEY,
|
||||
CONVERT_VAULT
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class RecoveryDirectory implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RecoveryDirectory.class);
|
||||
|
||||
private final Path recoveryPath;
|
||||
private final Path vaultPath;
|
||||
|
||||
private RecoveryDirectory(Path vaultPath, Path recoveryPath) {
|
||||
this.vaultPath = vaultPath;
|
||||
this.recoveryPath = recoveryPath;
|
||||
}
|
||||
|
||||
public static RecoveryDirectory create(Path vaultPath) throws IOException {
|
||||
Path tempDir = Files.createTempDirectory("cryptomator");
|
||||
return new RecoveryDirectory(vaultPath, tempDir);
|
||||
}
|
||||
|
||||
public void moveRecoveredFile(String file) throws IOException {
|
||||
Files.move(recoveryPath.resolve(file), vaultPath.resolve(file), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private void deleteRecoveryDirectory() {
|
||||
try (var paths = Files.walk(recoveryPath)) {
|
||||
paths.sorted(Comparator.reverseOrder()).forEach(p -> {
|
||||
try {
|
||||
Files.delete(p);
|
||||
} catch (IOException e) {
|
||||
LOG.info("Unable to delete {}. Please delete it manually.", p);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to clean up recovery directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
deleteRecoveryDirectory();
|
||||
}
|
||||
|
||||
public Path getRecoveryPath() {
|
||||
return recoveryPath;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.cryptomator.common.recovery;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultComponent;
|
||||
import org.cryptomator.common.vaults.VaultConfigCache;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
|
||||
|
||||
public final class VaultPreparator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultPreparator.class);
|
||||
|
||||
private VaultPreparator() {}
|
||||
|
||||
public static Vault prepareVault(Path selectedDirectory, //
|
||||
VaultComponent.Factory vaultComponentFactory, //
|
||||
List<MountService> mountServices, //
|
||||
ResourceBundle resourceBundle) {
|
||||
VaultSettings vaultSettings = VaultSettings.withRandomId();
|
||||
vaultSettings.path.set(selectedDirectory);
|
||||
if (selectedDirectory.getFileName() != null) {
|
||||
vaultSettings.displayName.set(selectedDirectory.getFileName().toString());
|
||||
} else {
|
||||
vaultSettings.displayName.set(resourceBundle.getString("defaults.vault.vaultName"));
|
||||
}
|
||||
|
||||
var wrapper = new VaultConfigCache(vaultSettings);
|
||||
Vault vault = vaultComponentFactory.create(vaultSettings, wrapper, LOCKED, null).vault();
|
||||
try {
|
||||
VaultListManager.determineVaultState(vault.getPath());
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for {}", vaultSettings.path.get(), e);
|
||||
}
|
||||
|
||||
//due to https://github.com/cryptomator/cryptomator/issues/2880#issuecomment-1680313498
|
||||
var nameOfWinfspLocalMounter = "org.cryptomator.frontend.fuse.mount.WinFspMountProvider";
|
||||
if (SystemUtils.IS_OS_WINDOWS && vaultSettings.path.get().toString().contains("Dropbox") && mountServices.stream().anyMatch(s -> s.getClass().getName().equals(nameOfWinfspLocalMounter))) {
|
||||
vaultSettings.mountService.setValue(nameOfWinfspLocalMounter);
|
||||
}
|
||||
|
||||
return vault;
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,8 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.NodeOrientation;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Settings {
|
||||
|
||||
@@ -53,6 +51,7 @@ public class Settings {
|
||||
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
|
||||
public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
|
||||
|
||||
private final SettingsProvider provider;
|
||||
public final ObservableList<VaultSettings> directories;
|
||||
public final BooleanProperty startHidden;
|
||||
public final BooleanProperty autoCloseVaults;
|
||||
@@ -78,13 +77,12 @@ public class Settings {
|
||||
public final ObjectProperty<Instant> lastUpdateCheckReminder;
|
||||
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
|
||||
public final StringProperty lastUpdateAttemptedByVersion;
|
||||
|
||||
private Consumer<Settings> saveCmd;
|
||||
|
||||
public static Settings create(Environment env) {
|
||||
public static Settings create(SettingsProvider provider, Environment env) {
|
||||
var defaults = new SettingsJson();
|
||||
defaults.showTrayIcon = env.showTrayIcon();
|
||||
return new Settings(defaults);
|
||||
return new Settings(provider, defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +90,8 @@ public class Settings {
|
||||
*
|
||||
* @param json The parsed settings.json
|
||||
*/
|
||||
Settings(SettingsJson json) {
|
||||
Settings(SettingsProvider provider, SettingsJson json) {
|
||||
this.provider = provider;
|
||||
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
|
||||
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
|
||||
@@ -118,6 +117,7 @@ public class Settings {
|
||||
this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck);
|
||||
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
||||
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
|
||||
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
|
||||
|
||||
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||
|
||||
@@ -148,15 +148,11 @@ public class Settings {
|
||||
lastUpdateCheckReminder.addListener(this::somethingChanged);
|
||||
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
||||
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
|
||||
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void migrateLegacySettings(SettingsJson json) {
|
||||
// migrate renamed keychainAccess
|
||||
if(this.keychainProvider.getValueSafe().equals("org.cryptomator.linux.keychain.SecretServiceKeychainAccess")) {
|
||||
this.keychainProvider.setValue("org.cryptomator.linux.keychain.GnomeKeyringKeychainAccess");
|
||||
}
|
||||
|
||||
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
|
||||
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
|
||||
this.mountService.set(switch (json.preferredVolumeImpl) {
|
||||
@@ -210,6 +206,7 @@ public class Settings {
|
||||
json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get();
|
||||
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
||||
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
|
||||
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -222,20 +219,12 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO rename to setChangeListener
|
||||
void setSaveCmd(Consumer<Settings> saveCmd) {
|
||||
this.saveCmd = saveCmd;
|
||||
}
|
||||
|
||||
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
|
||||
this.save();
|
||||
provider.scheduleSave(this);
|
||||
}
|
||||
|
||||
void save() {
|
||||
if (saveCmd != null) {
|
||||
saveCmd.accept(this);
|
||||
}
|
||||
public void saveNow() {
|
||||
provider.saveNow(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,4 +96,7 @@ class SettingsJson {
|
||||
|
||||
@JsonProperty("previouslyUsedVaultDirectory")
|
||||
Path previouslyUsedVaultDirectory;
|
||||
|
||||
@JsonProperty("lastUpdateAttemptedByVersion")
|
||||
String lastUpdateAttemptedByVersion;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -61,8 +63,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
Settings settings = env.getSettingsPath() //
|
||||
.flatMap(this::tryLoad) //
|
||||
.findFirst() //
|
||||
.orElseGet(() -> Settings.create(env));
|
||||
settings.setSaveCmd(this::scheduleSave);
|
||||
.orElseGet(() -> Settings.create(this, env));
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
|
||||
var json = JSON.reader().readValue(in, SettingsJson.class);
|
||||
LOG.info("Settings loaded from {}", path);
|
||||
var settings = new Settings(json);
|
||||
var settings = new Settings(this, json);
|
||||
return Stream.of(settings);
|
||||
} catch (JacksonException e) {
|
||||
LOG.warn("Failed to parse json file {}", path, e);
|
||||
@@ -84,19 +85,33 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleSave(Settings settings) {
|
||||
if (settings == null) {
|
||||
return;
|
||||
void saveNow(Settings settings) {
|
||||
try {
|
||||
scheduleSave(settings, 0L).get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
LOG.error("Saving settings was interrupted.", e);
|
||||
} catch (ExecutionException e) {
|
||||
LOG.error("Unexpected exception while saving.", e);
|
||||
}
|
||||
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
|
||||
settingsPath.ifPresent(path -> {
|
||||
Runnable saveCommand = () -> this.save(settings, path);
|
||||
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
||||
if (previouslyScheduledTask != null) {
|
||||
previouslyScheduledTask.cancel(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scheduleSave(Settings settings) {
|
||||
scheduleSave(settings, SAVE_DELAY_MS);
|
||||
}
|
||||
|
||||
private Future<?> scheduleSave(Settings settings, long delayMillis) {
|
||||
if (settings == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
final Path settingsPath = env.getSettingsPath().findFirst().orElseThrow(); // always save to preferred (first) path
|
||||
Runnable saveCommand = () -> this.save(settings, settingsPath);
|
||||
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, delayMillis, TimeUnit.MILLISECONDS);
|
||||
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
||||
if (previouslyScheduledTask != null) {
|
||||
previouslyScheduledTask.cancel(false);
|
||||
}
|
||||
return scheduledTask;
|
||||
}
|
||||
|
||||
private void save(Settings settings, Path settingsPath) {
|
||||
@@ -107,7 +122,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
||||
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
|
||||
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
|
||||
var jsonObj = settings.serialized();
|
||||
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
|
||||
jsonObj.writtenByVersion = env.getAppVersionWithBuildNumber();
|
||||
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
|
||||
}
|
||||
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
@@ -10,14 +10,6 @@ public enum UiTheme {
|
||||
DARK("preferences.interface.theme.dark"), //
|
||||
AUTOMATIC("preferences.interface.theme.automatic");
|
||||
|
||||
public static UiTheme[] applicableValues() {
|
||||
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
|
||||
return values();
|
||||
} else {
|
||||
return new UiTheme[]{LIGHT, DARK};
|
||||
}
|
||||
}
|
||||
|
||||
private final String displayName;
|
||||
|
||||
UiTheme(String displayName) {
|
||||
|
||||
@@ -10,7 +10,7 @@ package org.cryptomator.common.vaults;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.common.FilesystemOwnerSupplier;
|
||||
import org.cryptomator.common.mount.Mounter;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
@@ -23,7 +23,8 @@ import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.cryptomator.event.VaultEvent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.event.NotificationManager;
|
||||
import org.cryptomator.integrations.mount.MountFailedException;
|
||||
import org.cryptomator.integrations.mount.Mountpoint;
|
||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
||||
@@ -35,7 +36,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -75,10 +75,12 @@ public class Vault {
|
||||
private final BooleanBinding missing;
|
||||
private final BooleanBinding needsMigration;
|
||||
private final BooleanBinding unknownError;
|
||||
private final BooleanBinding missingVaultConfig;
|
||||
private final ObjectBinding<Mountpoint> mountPoint;
|
||||
private final Mounter mounter;
|
||||
private final Settings settings;
|
||||
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||
private final NotificationManager notificationManager;
|
||||
private final BooleanProperty showingStats;
|
||||
|
||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||
@@ -91,7 +93,8 @@ public class Vault {
|
||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||
VaultStats stats, //
|
||||
Mounter mounter, Settings settings, //
|
||||
FileSystemEventAggregator fileSystemEventAggregator) {
|
||||
FileSystemEventAggregator fileSystemEventAggregator, //
|
||||
NotificationManager notificationManager) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.configCache = configCache;
|
||||
this.cryptoFileSystem = cryptoFileSystem;
|
||||
@@ -103,12 +106,14 @@ public class Vault {
|
||||
this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
|
||||
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
|
||||
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
|
||||
this.missingVaultConfig = Bindings.createBooleanBinding(this::isMissingVaultConfig, state);
|
||||
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
||||
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
||||
this.mounter = mounter;
|
||||
this.settings = settings;
|
||||
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||
this.notificationManager = notificationManager;
|
||||
this.showingStats = new SimpleBooleanProperty(false);
|
||||
this.quickAccessEntry = new AtomicReference<>(null);
|
||||
}
|
||||
@@ -145,14 +150,17 @@ public class Vault {
|
||||
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
|
||||
}
|
||||
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||
var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||
.withKeyLoader(keyLoader) //
|
||||
.withFlags(flags) //
|
||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||
.withFilesystemEventConsumer(this::consumeVaultEvent) //
|
||||
.build();
|
||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||
.withFilesystemEventConsumer(this::consumeVaultEvent);
|
||||
if (keyLoader instanceof FilesystemOwnerSupplier oo) {
|
||||
fsPropsBuilder.withOwnerGetter(oo::getOwner);
|
||||
}
|
||||
|
||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build());
|
||||
}
|
||||
|
||||
private void destroyCryptoFileSystem() {
|
||||
@@ -262,6 +270,7 @@ public class Vault {
|
||||
|
||||
private void consumeVaultEvent(FilesystemEvent e) {
|
||||
fileSystemEventAggregator.put(this, e);
|
||||
notificationManager.offer(this, e);
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -336,6 +345,14 @@ public class Vault {
|
||||
return state.get() == VaultState.Value.ERROR;
|
||||
}
|
||||
|
||||
public BooleanBinding missingVaultConfigProperty() {
|
||||
return missingVaultConfig;
|
||||
}
|
||||
|
||||
public boolean isMissingVaultConfig() {
|
||||
return state.get() == VaultState.Value.VAULT_CONFIG_MISSING || state.get() == VaultState.Value.ALL_MISSING;
|
||||
}
|
||||
|
||||
public ReadOnlyStringProperty displayNameProperty() {
|
||||
return vaultSettings.displayName;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class VaultConfigCache {
|
||||
private final VaultSettings settings;
|
||||
private final AtomicReference<VaultConfig.UnverifiedVaultConfig> config;
|
||||
|
||||
VaultConfigCache(VaultSettings settings) {
|
||||
public VaultConfigCache(VaultSettings settings) {
|
||||
this.settings = settings;
|
||||
this.config = new AtomicReference<>(null);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.cryptomator.common.vaults;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.recovery.BackupRestorer;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
@@ -34,9 +35,7 @@ import java.util.ResourceBundle;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@Singleton
|
||||
public class VaultListManager {
|
||||
@@ -67,6 +66,12 @@ public class VaultListManager {
|
||||
autoLocker.init();
|
||||
}
|
||||
|
||||
public boolean isAlreadyAdded(Path vaultPath) {
|
||||
assert vaultPath.isAbsolute();
|
||||
assert vaultPath.normalize().equals(vaultPath);
|
||||
return vaultList.stream().anyMatch(v -> vaultPath.equals(v.getPath()));
|
||||
}
|
||||
|
||||
public Vault add(Path pathToVault) throws IOException {
|
||||
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
|
||||
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
|
||||
@@ -114,59 +119,122 @@ public class VaultListManager {
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public void addVault(Vault vault) {
|
||||
Path path = vault.getPath().normalize().toAbsolutePath();
|
||||
if (!isAlreadyAdded(path)) {
|
||||
vaultList.add(vault);
|
||||
}
|
||||
}
|
||||
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
var wrapper = new VaultConfigCache(vaultSettings);
|
||||
try {
|
||||
var vaultState = determineVaultState(vaultSettings.path.get());
|
||||
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||
wrapper.reloadConfig();
|
||||
if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) {
|
||||
var keyIdScheme = wrapper.get().getKeyId().getScheme();
|
||||
vaultSettings.lastKnownKeyLoader.set(keyIdScheme);
|
||||
}
|
||||
} else if (vaultState == NEEDS_MIGRATION) {
|
||||
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
||||
}
|
||||
initializeLastKnownKeyLoaderIfPossible(vaultSettings, vaultState, wrapper);
|
||||
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e);
|
||||
LOG.warn("Failed to determine vault state for {}", vaultSettings.path.get(), e);
|
||||
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeLastKnownKeyLoaderIfPossible(VaultSettings vaultSettings, VaultState.Value vaultState, VaultConfigCache wrapper) throws IOException {
|
||||
if (vaultSettings.lastKnownKeyLoader.get() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (vaultState) {
|
||||
case LOCKED -> {
|
||||
wrapper.reloadConfig();
|
||||
vaultSettings.lastKnownKeyLoader.set(wrapper.get().getKeyId().getScheme());
|
||||
}
|
||||
case NEEDS_MIGRATION -> {
|
||||
//for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
||||
}
|
||||
case VAULT_CONFIG_MISSING -> {
|
||||
//Nothing to do here, since there is no config to read
|
||||
}
|
||||
case MISSING, ALL_MISSING, ERROR, PROCESSING -> {
|
||||
// no config available or not safe to load
|
||||
}
|
||||
default -> {
|
||||
if (Files.exists(vaultSettings.path.get().resolve(VAULTCONFIG_FILENAME))) {
|
||||
try {
|
||||
wrapper.reloadConfig();
|
||||
vaultSettings.lastKnownKeyLoader.set(wrapper.get().getKeyId().getScheme());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to load config for {}", vaultSettings.path.get(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static VaultState.Value redetermineVaultState(Vault vault) {
|
||||
VaultState state = vault.stateProperty();
|
||||
VaultState.Value previousState = state.getValue();
|
||||
return switch (previousState) {
|
||||
case LOCKED, NEEDS_MIGRATION, MISSING -> {
|
||||
try {
|
||||
var determinedState = determineVaultState(vault.getPath());
|
||||
if (determinedState == LOCKED) {
|
||||
vault.getVaultConfigCache().reloadConfig();
|
||||
}
|
||||
state.set(determinedState);
|
||||
yield determinedState;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
|
||||
state.set(ERROR);
|
||||
vault.setLastKnownException(e);
|
||||
yield ERROR;
|
||||
}
|
||||
VaultState.Value previous = state.getValue();
|
||||
|
||||
if (previous.equals(UNLOCKED) || previous.equals(PROCESSING)) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
try {
|
||||
VaultState.Value determined = determineVaultState(vault.getPath());
|
||||
|
||||
if (determined == LOCKED) {
|
||||
vault.getVaultConfigCache().reloadConfig();
|
||||
}
|
||||
case ERROR, UNLOCKED, PROCESSING -> previousState;
|
||||
};
|
||||
|
||||
state.set(determined);
|
||||
return determined;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to (re)determine vault state for {}", vault.getPath(), e);
|
||||
vault.setLastKnownException(e);
|
||||
state.set(ERROR);
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
|
||||
public static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
|
||||
if (!Files.exists(pathToVault)) {
|
||||
return VaultState.Value.MISSING;
|
||||
return MISSING;
|
||||
}
|
||||
|
||||
VaultState.Value structureResult = checkDirStructure(pathToVault);
|
||||
|
||||
if (structureResult == LOCKED || structureResult == NEEDS_MIGRATION) {
|
||||
return structureResult;
|
||||
}
|
||||
|
||||
Path pathToVaultConfig = pathToVault.resolve(VAULTCONFIG_FILENAME);
|
||||
Path pathToMasterkey = pathToVault.resolve(MASTERKEY_FILENAME);
|
||||
|
||||
if (!Files.exists(pathToVaultConfig)) {
|
||||
BackupRestorer.restoreIfBackupPresent(pathToVault, VAULTCONFIG_FILENAME);
|
||||
}
|
||||
if (!Files.exists(pathToMasterkey)) {
|
||||
BackupRestorer.restoreIfBackupPresent(pathToVault, MASTERKEY_FILENAME);
|
||||
}
|
||||
|
||||
boolean hasConfig = Files.exists(pathToVaultConfig);
|
||||
|
||||
if (!hasConfig && !Files.exists(pathToMasterkey)) {
|
||||
return ALL_MISSING;
|
||||
}
|
||||
if (!hasConfig) {
|
||||
return VAULT_CONFIG_MISSING;
|
||||
}
|
||||
|
||||
return checkDirStructure(pathToVault);
|
||||
}
|
||||
|
||||
private static VaultState.Value checkDirStructure(Path pathToVault) throws IOException {
|
||||
return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
|
||||
case VAULT -> VaultState.Value.LOCKED;
|
||||
case UNRELATED -> VaultState.Value.MISSING;
|
||||
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? //
|
||||
VaultState.Value.NEEDS_MIGRATION //
|
||||
: VaultState.Value.MISSING;
|
||||
case VAULT -> LOCKED;
|
||||
case UNRELATED -> MISSING;
|
||||
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? NEEDS_MIGRATION : MISSING;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,16 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
||||
*/
|
||||
MISSING,
|
||||
|
||||
/**
|
||||
* No vault config found at the provided path
|
||||
*/
|
||||
VAULT_CONFIG_MISSING,
|
||||
|
||||
/**
|
||||
* No vault config and masterkey found at the provided path
|
||||
*/
|
||||
ALL_MISSING,
|
||||
|
||||
/**
|
||||
* Vault requires migration to a newer vault format
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -101,6 +102,7 @@ public class FileSystemEventAggregator {
|
||||
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
|
||||
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
|
||||
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
|
||||
case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext;
|
||||
};
|
||||
return new FSEventBucket(v, p, event.getClass());
|
||||
}
|
||||
|
||||
85
src/main/java/org/cryptomator/event/NotificationManager.java
Normal file
85
src/main/java/org/cryptomator/event/NotificationManager.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package org.cryptomator.event;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Manager for notifications.
|
||||
* <p>
|
||||
* To add (filesystem) events, use method {@link #offer(Vault, FilesystemEvent)}. If the input event is eligible, it is added to an internal queue.
|
||||
* An event is eligible, if
|
||||
* <ul>
|
||||
* <li>the event should trigger a notification and</li>
|
||||
* <li>it is not added within the last {@value DEBOUNCE_THRESHOLD_SECONDS} seconds</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see org.cryptomator.ui.fxapp.FxNotificationManager
|
||||
*/
|
||||
@Singleton
|
||||
public class NotificationManager {
|
||||
|
||||
private static final int DEBOUNCE_THRESHOLD_SECONDS = 5;
|
||||
|
||||
private final Cache<FSEventBucket, FilesystemEvent> debounceCache;
|
||||
private final ConcurrentLinkedQueue<VaultEvent> pendingEvents;
|
||||
|
||||
@Inject
|
||||
public NotificationManager() {
|
||||
debounceCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(DEBOUNCE_THRESHOLD_SECONDS)).build();
|
||||
pendingEvents = new ConcurrentLinkedQueue<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers the given filesystem event to the notification manager.
|
||||
*
|
||||
* @param v The vault where the filesystem event happened
|
||||
* @param e the actual filesystem event
|
||||
* @return {@code true} if the filesystem event is accepted, otherwise {@code false}.
|
||||
*/
|
||||
public boolean offer(Vault v, FilesystemEvent e) {
|
||||
return switch (e) {
|
||||
case FileIsInUseEvent fiiue -> addEvent(v, fiiue.ciphertextPath(), fiiue);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
boolean addEvent(Vault v, Path keyPath, FilesystemEvent e) {
|
||||
var key = new FSEventBucket(v, keyPath, e.getClass());
|
||||
var isAdded = new AtomicBoolean(false);
|
||||
debounceCache.asMap().computeIfAbsent(key, _ -> {
|
||||
synchronized (this) {
|
||||
pendingEvents.add(new VaultEvent(v, e));
|
||||
isAdded.set(true);
|
||||
}
|
||||
return e;
|
||||
});
|
||||
return isAdded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all events to the target list and clears afterward the pending-event-queue
|
||||
*
|
||||
* @param target list where the filesystem events are copied to
|
||||
* @return {@code true}, if elements were copied
|
||||
*/
|
||||
public boolean appendToAndClear(List<VaultEvent> target) {
|
||||
//it is not clear, if addAll iterates thread-safe over the pendingEvents
|
||||
//hence we synchronize moving (copy then clear) and adding-single-element operations
|
||||
synchronized (this) {
|
||||
var result = target.addAll(pendingEvents);
|
||||
pendingEvents.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,6 @@ package org.cryptomator.event;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||
|
||||
import java.time.Instant;
|
||||
public record VaultEvent(Vault v, FilesystemEvent actualEvent) {
|
||||
|
||||
public record VaultEvent(Vault v, FilesystemEvent actualEvent, int count) implements Comparable<VaultEvent> {
|
||||
|
||||
public VaultEvent(Vault v, FilesystemEvent actualEvent) {
|
||||
this(v, actualEvent, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(VaultEvent other) {
|
||||
var timeResult = actualEvent.getTimestamp().compareTo(other.actualEvent().getTimestamp());
|
||||
if(timeResult != 0) {
|
||||
return timeResult;
|
||||
} else {
|
||||
return this.equals(other) ? 0 : this.actualEvent.getClass().getName().compareTo(other.actualEvent.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public VaultEvent incrementCount(FilesystemEvent update) {
|
||||
return new VaultEvent(v, update, count+1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Factory to generate admin properties.
|
||||
*
|
||||
* <p>
|
||||
* Admin properties are {@link Properties} using system properties as defaults, but allow overwriting a specific set of properties with an external config file.
|
||||
* Those properties are created by calling {@link #create()}. The method first reads system property {@value #ADMIN_PROP_FILE_KEY}. If it contains a path to a valid properties file, all overridable properties from the file are loaded into the returned admin properties.
|
||||
* <p>
|
||||
* The overridable properties are:
|
||||
* <ul>
|
||||
* <li>cryptomator.logDir</li>
|
||||
* <li>cryptomator.pluginDir</li>
|
||||
* <li>cryptomator.p12Path</li>
|
||||
* <li>cryptomator.mountPointsDir</li>
|
||||
* <li>cryptomator.disableUpdateCheck</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see Properties
|
||||
* @see System#getProperties()
|
||||
*/
|
||||
class AdminPropertiesFactory {
|
||||
|
||||
private static final Logger LOG = EventualLogger.INSTANCE;
|
||||
private static final long MAX_CONFIG_SIZE_BYTES = 8192;
|
||||
private static final String ADMIN_PROP_FILE_KEY = "cryptomator.adminConfigPath";
|
||||
private static final Set<String> ALLOWED_OVERRIDES = Set.of( //
|
||||
"cryptomator.logDir", //
|
||||
"cryptomator.pluginDir", //
|
||||
"cryptomator.p12Path", //
|
||||
"cryptomator.mountPointsDir", //
|
||||
"cryptomator.disableUpdateCheck");
|
||||
|
||||
|
||||
/**
|
||||
* Creates new {@link Properties} containing overridable properties from the admin config.
|
||||
* <p>
|
||||
* The returned properties object uses as default the {@link System} properties.
|
||||
* For a list of overridable properties, see {@link AdminPropertiesFactory}
|
||||
*
|
||||
* @return {@link Properties} containing overridable properties from the admin config and defaulting to system properties.
|
||||
*/
|
||||
static Properties create() {
|
||||
var systemProps = System.getProperties();
|
||||
var adminProps = new Properties(systemProps);
|
||||
|
||||
final String adminCfgPath = System.getProperty(ADMIN_PROP_FILE_KEY);
|
||||
if (adminCfgPath == null) {
|
||||
LOG.debug("Admin config property is not defined. Skipping.");
|
||||
return adminProps;
|
||||
}
|
||||
var propsFromFile = loadPropertiesFromFile(Path.of(adminCfgPath));
|
||||
|
||||
for (var key : propsFromFile.stringPropertyNames()) {
|
||||
if (ALLOWED_OVERRIDES.contains(key)) {
|
||||
var value = propsFromFile.getProperty(key);
|
||||
LOG.info("Overwriting {} with value {} from admin config.", key, value);
|
||||
adminProps.setProperty(key, value);
|
||||
} else {
|
||||
LOG.debug("Property {} in admin config is not supported for override.", key);
|
||||
}
|
||||
}
|
||||
return adminProps;
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
static Properties loadPropertiesFromFile(Path adminPropertiesPath) {
|
||||
var adminProps = new Properties();
|
||||
try (FileChannel ch = FileChannel.open(adminPropertiesPath, StandardOpenOption.READ); //
|
||||
Reader reader = Channels.newReader(ch, StandardCharsets.UTF_8)) {
|
||||
if (ch.size() > MAX_CONFIG_SIZE_BYTES) {
|
||||
throw new IOException("Config file %s exceeds maximum size of %d".formatted(adminPropertiesPath, MAX_CONFIG_SIZE_BYTES));
|
||||
}
|
||||
adminProps.load(reader);
|
||||
} catch (NoSuchFileException _) {
|
||||
//NO-OP
|
||||
LOG.debug("No admin properties found at {}.", adminPropertiesPath);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
LOG.warn("Failed to read administrative properties from {}. Returning empty properties.", adminPropertiesPath, e);
|
||||
}
|
||||
return adminProps;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.common.SubstitutingProperties;
|
||||
import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.ipc.IpcCommunicator;
|
||||
import org.cryptomator.logging.DebugMode;
|
||||
import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -35,7 +35,8 @@ public class Cryptomator {
|
||||
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||
|
||||
static {
|
||||
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
|
||||
var adminProps = AdminPropertiesFactory.create();
|
||||
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
|
||||
System.setProperties(lazyProcessedProps);
|
||||
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
@@ -64,6 +65,7 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
EventualLogger.INSTANCE.drainTo(LOG);
|
||||
var printVersion = Optional.ofNullable(args) //
|
||||
.stream() //Streams either one element (the args-array) or zero elements
|
||||
.flatMap(Arrays::stream) //
|
||||
|
||||
@@ -4,7 +4,6 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
@@ -30,11 +29,6 @@ class CryptomatorModule {
|
||||
return new ArrayBlockingQueue<>(10);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||
return UiAppearanceProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
||||
106
src/main/java/org/cryptomator/launcher/EventualLogger.java
Normal file
106
src/main/java/org/cryptomator/launcher/EventualLogger.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.event.DefaultLoggingEvent;
|
||||
import org.slf4j.event.Level;
|
||||
import org.slf4j.event.LoggingEvent;
|
||||
import org.slf4j.helpers.AbstractLogger;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
|
||||
class EventualLogger extends AbstractLogger {
|
||||
|
||||
static final EventualLogger INSTANCE = new EventualLogger();
|
||||
|
||||
private final Queue<LoggingEvent> bufferedEvents = new ArrayDeque<>();
|
||||
|
||||
private EventualLogger() {
|
||||
}
|
||||
|
||||
synchronized void drainTo(Logger gutter) {
|
||||
for (var event : bufferedEvents) {
|
||||
var builder = gutter.atLevel(event.getLevel()) //
|
||||
.setCause(event.getThrowable()) //
|
||||
.setMessage(event.getMessage());
|
||||
Objects.requireNonNullElse(event.getArguments(), List.of()).forEach(builder::addArgument);
|
||||
Objects.requireNonNullElse(event.getMarkers(), List.<Marker>of()).forEach(builder::addMarker);
|
||||
builder.log();
|
||||
}
|
||||
bufferedEvents.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
|
||||
var event = new DefaultLoggingEvent(level, this);
|
||||
if (marker != null) {
|
||||
event.addMarker(marker);
|
||||
}
|
||||
event.setMessage(messagePattern);
|
||||
for (var arg : Objects.requireNonNullElse(arguments, new Object[]{})) {
|
||||
event.addArgument(arg);
|
||||
}
|
||||
event.setThrowable(throwable);
|
||||
bufferedEvents.add(event);
|
||||
}
|
||||
|
||||
//Unclear, unused and undocumented method of slf4j, see also https://github.com/qos-ch/slf4j/discussions/348
|
||||
@Override
|
||||
protected String getFullyQualifiedCallerName() {
|
||||
return getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled(Marker marker) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
this.vault = vault;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.screenshot = applicationStyle.appliedThemeProperty().map(this::selectScreenshot);
|
||||
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
|
||||
}
|
||||
|
||||
private Image selectScreenshot(Theme theme) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.ResourceBundle;
|
||||
public class PasswordStrengthUtil {
|
||||
|
||||
private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length
|
||||
private static final String RESSOURCE_PREFIX = "passwordStrength.messageLabel.";
|
||||
private static final List<String> SANITIZED_INPUTS = List.of("cryptomator");
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
@@ -48,13 +47,15 @@ public class PasswordStrengthUtil {
|
||||
}
|
||||
|
||||
public String getStrengthDescription(Number score) {
|
||||
if (score.intValue() == -1) {
|
||||
return String.format(resourceBundle.getString(RESSOURCE_PREFIX + "tooShort"), minPwLength);
|
||||
} else if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
|
||||
return resourceBundle.getString(RESSOURCE_PREFIX + score.intValue());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
return switch (score.intValue()) {
|
||||
case -1 -> String.format(resourceBundle.getString("passwordStrength.messageLabel.tooShort"), minPwLength);
|
||||
case 0 -> resourceBundle.getString("passwordStrength.messageLabel.0");
|
||||
case 1 -> resourceBundle.getString("passwordStrength.messageLabel.1");
|
||||
case 2 -> resourceBundle.getString("passwordStrength.messageLabel.2");
|
||||
case 3 -> resourceBundle.getString("passwordStrength.messageLabel.3");
|
||||
case 4 -> resourceBundle.getString("passwordStrength.messageLabel.4");
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,13 +38,15 @@ public enum FxmlFile {
|
||||
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
||||
MIGRATION_START("/fxml/migration_start.fxml"), //
|
||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||
NOTIFICATION("/fxml/notification.fxml"), //
|
||||
PREFERENCES("/fxml/preferences.fxml"), //
|
||||
QUIT("/fxml/quit.fxml"), //
|
||||
QUIT_FORCED("/fxml/quit_forced.fxml"), //
|
||||
RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
|
||||
RECOVERYKEY_EXPERT_SETTINGS("/fxml/recoverykey_expert_settings.fxml"), //
|
||||
RECOVERYKEY_ONBOARDING("/fxml/recoverykey_onboarding.fxml"), //
|
||||
RECOVERYKEY_RECOVER("/fxml/recoverykey_recover.fxml"), //
|
||||
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
|
||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||
SHARE_VAULT("/fxml/share_vault.fxml"), //
|
||||
SIMPLE_DIALOG("/fxml/simple_dialog.fxml"), //
|
||||
@@ -58,6 +60,7 @@ public enum FxmlFile {
|
||||
VAULT_STATISTICS("/fxml/stats.fxml"), //
|
||||
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
|
||||
|
||||
|
||||
private final String ressourcePathString;
|
||||
|
||||
FxmlFile(String ressourcePathString) {
|
||||
|
||||
56
src/main/java/org/cryptomator/ui/common/SystemBarUtil.java
Normal file
56
src/main/java/org/cryptomator/ui/common/SystemBarUtil.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import javafx.stage.Screen;
|
||||
|
||||
/**
|
||||
* Utility class providing methods regarding the OS bar.
|
||||
*/
|
||||
public class SystemBarUtil {
|
||||
|
||||
public enum Placement {
|
||||
/**
|
||||
* OS Bar placed at the left screen edge
|
||||
*/
|
||||
LEFT,
|
||||
/**
|
||||
* OS Bar placed at the top screen edge
|
||||
*/
|
||||
TOP,
|
||||
/**
|
||||
* OS Bar placed at the right screen edge
|
||||
*/
|
||||
RIGHT,
|
||||
/**
|
||||
* OS Bar placed at the bottom screen edge
|
||||
*/
|
||||
BOTTOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the placement of the OS bar on the given screen.
|
||||
* <p>
|
||||
* <b>Assuming the OS bar fills one screen edge completely</b>,
|
||||
* this method determines that screen edge by comparing the actual screen bounds with the visual ones.
|
||||
* <p>
|
||||
* If the screen does not have a system bar, the bottom placement is returned.
|
||||
* If the screen does have multiple system bars, the first in following priority is returned:
|
||||
* LEFT, TOP, RIGHT, BOTTOM.
|
||||
*
|
||||
* @param screen a {@link Screen} where an OS bar exists
|
||||
* @return {@link Placement} indicating the screen edge.
|
||||
*/
|
||||
public static Placement getPlacementOfSystembar(Screen screen) {
|
||||
var bounds = screen.getBounds();
|
||||
var vBounds = screen.getVisualBounds();
|
||||
//assumption: the system bar fills a whole screen side
|
||||
if (bounds.getMinX() != vBounds.getMinX()) {
|
||||
return Placement.LEFT;
|
||||
} else if (bounds.getMinY() != vBounds.getMinY()) {
|
||||
return Placement.TOP;
|
||||
} else if (bounds.getMaxX() != vBounds.getMaxX()) {
|
||||
return Placement.RIGHT;
|
||||
} else {
|
||||
return Placement.BOTTOM;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ public enum FontAwesome5Icon {
|
||||
CARET_DOWN("\uF0D7"), //
|
||||
CARET_RIGHT("\uF0Da"), //
|
||||
CHECK("\uF00C"), //
|
||||
CHEVRON_LEFT("\uF053"), //
|
||||
CHEVRON_RIGHT("\uF054"), //
|
||||
CLOCK("\uF017"), //
|
||||
CLIPBOARD("\uF328"), //
|
||||
COG("\uF013"), //
|
||||
@@ -60,6 +62,7 @@ public enum FontAwesome5Icon {
|
||||
TRASH("\uF1F8"), //
|
||||
UNLINK("\uf127"), //
|
||||
USER_COG("\uf4fe"), //
|
||||
USER_LOCK("\uf502"), //
|
||||
WRENCH("\uF0AD"), //
|
||||
WINDOW_MINIMIZE("\uF2D1"), //
|
||||
;
|
||||
|
||||
@@ -4,24 +4,27 @@ import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.AccessibleRole;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class NotificationBar extends HBox {
|
||||
public class InfoBar extends HBox {
|
||||
|
||||
@FXML
|
||||
private Label notificationLabel;
|
||||
private Label infoMessage;
|
||||
|
||||
private final BooleanProperty dismissable = new SimpleBooleanProperty();
|
||||
private final BooleanProperty notify = new SimpleBooleanProperty();
|
||||
|
||||
|
||||
public NotificationBar() {
|
||||
public InfoBar() {
|
||||
setAlignment(Pos.CENTER);
|
||||
setStyle("-fx-alignment: center;");
|
||||
getStyleClass().addAll("info-bar");
|
||||
|
||||
Region spacer = new Region();
|
||||
spacer.setMinWidth(40);
|
||||
@@ -36,14 +39,21 @@ public class NotificationBar extends HBox {
|
||||
vbox.setAlignment(Pos.CENTER);
|
||||
HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS);
|
||||
|
||||
notificationLabel = new Label();
|
||||
notificationLabel.getStyleClass().add("notification-label");
|
||||
notificationLabel.setStyle("-fx-alignment: center;");
|
||||
vbox.getChildren().add(notificationLabel);
|
||||
infoMessage = new Label();
|
||||
infoMessage.setFocusTraversable(true);
|
||||
infoMessage.setAccessibleRole(AccessibleRole.BUTTON);
|
||||
vbox.getChildren().add(infoMessage);
|
||||
|
||||
Button closeButton = new Button("X");
|
||||
var closeGraphic = new FontAwesome5IconView();
|
||||
closeGraphic.setGlyph(FontAwesome5Icon.TIMES);
|
||||
closeGraphic.setGlyphSize(12);
|
||||
closeGraphic.getStyleClass().add("glyph");
|
||||
|
||||
Button closeButton = new Button();
|
||||
closeButton.setGraphic(closeGraphic);
|
||||
closeButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||
closeButton.setAccessibleText(ResourceBundle.getBundle("i18n.strings").getString("main.notification.closeButton.tooltip"));
|
||||
closeButton.setMinWidth(40);
|
||||
closeButton.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-weight: bold;");
|
||||
closeButton.visibleProperty().bind(dismissable);
|
||||
|
||||
closeButton.setOnAction(_ -> {
|
||||
@@ -61,11 +71,11 @@ public class NotificationBar extends HBox {
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return notificationLabel.getText();
|
||||
return infoMessage.getText();
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
notificationLabel.setText(text);
|
||||
infoMessage.setText(text);
|
||||
}
|
||||
|
||||
public void setStyleClass(String styleClass) {
|
||||
@@ -4,8 +4,10 @@ import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.common.recovery.RecoveryActionType;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.changepassword.NewPasswordController;
|
||||
import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
@@ -20,6 +22,7 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.Scene;
|
||||
@@ -119,8 +122,8 @@ abstract class ConvertVaultModule {
|
||||
@Provides
|
||||
@IntoMap
|
||||
@FxControllerKey(RecoveryKeyValidateController.class)
|
||||
static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
|
||||
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
|
||||
static FxController provideRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, masterkeyFileAccess, new SimpleObjectProperty<>(RecoveryActionType.CONVERT_VAULT), new SimpleObjectProperty<>(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
//obvservable getter
|
||||
//observable getter
|
||||
|
||||
public ObservableValue<String> dropZoneTextProperty() {
|
||||
return dropZoneText;
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.dialogs;
|
||||
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
@@ -19,17 +20,21 @@ public class Dialogs {
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final StageFactory stageFactory;
|
||||
private final DefaultSceneFactory sceneFactory;
|
||||
|
||||
private static final String BUTTON_KEY_CLOSE = "generic.button.close";
|
||||
|
||||
@Inject
|
||||
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) {
|
||||
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.stageFactory = stageFactory;
|
||||
this.sceneFactory = sceneFactory;
|
||||
}
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
|
||||
|
||||
private SimpleDialog.Builder createDialogBuilder() {
|
||||
return new SimpleDialog.Builder(resourceBundle, stageFactory);
|
||||
return new SimpleDialog.Builder(resourceBundle, stageFactory, sceneFactory);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
|
||||
@@ -47,6 +52,52 @@ public class Dialogs {
|
||||
});
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareContactHubVaultOwner(Stage window) {
|
||||
return createDialogBuilder().setOwner(window) //
|
||||
.setTitleKey("contactHubVaultOwner.title") //
|
||||
.setMessageKey("contactHubVaultOwner.message") //
|
||||
.setDescriptionKey("contactHubVaultOwner.description") //
|
||||
.setIcon(FontAwesome5Icon.EXCLAMATION)//
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareHubVaultArchived(Stage window, Vault vault) {
|
||||
return createDialogBuilder().setOwner(window) //
|
||||
.setTitleKey("unlock.title", vault.getDisplayName()) //
|
||||
.setMessageKey("hub.archived.message") //
|
||||
.setDescriptionKey("hub.archived.description") //
|
||||
.setIcon(FontAwesome5Icon.BAN)//
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareRecoveryVaultAdded(Stage window, String displayName) {
|
||||
return createDialogBuilder().setOwner(window) //
|
||||
.setTitleKey("recover.existing.title") //
|
||||
.setMessageKey("recover.existing.message") //
|
||||
.setDescriptionKey("recover.existing.description", displayName) //
|
||||
.setIcon(FontAwesome5Icon.CHECK)//
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||
}
|
||||
public SimpleDialog.Builder prepareRecoveryVaultAlreadyExists(Stage window, String displayName) {
|
||||
return createDialogBuilder().setOwner(window) //
|
||||
.setTitleKey("recover.alreadyExists.title") //
|
||||
.setMessageKey("recover.alreadyExists.message") //
|
||||
.setDescriptionKey("recover.alreadyExists.description", displayName) //
|
||||
.setIcon(FontAwesome5Icon.EXCLAMATION)//
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareRecoverPasswordSuccess(Stage window) {
|
||||
return createDialogBuilder()
|
||||
.setOwner(window) //
|
||||
.setTitleKey("recoveryKey.recover.title") //
|
||||
.setMessageKey("recoveryKey.recover.resetSuccess.message") //
|
||||
.setDescriptionKey("recoveryKey.recover.resetSuccess.description") //
|
||||
.setIcon(FontAwesome5Icon.CHECK)
|
||||
.setOkAction(Stage::close)
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) {
|
||||
return createDialogBuilder() //
|
||||
.setOwner(window) //
|
||||
@@ -69,7 +120,7 @@ public class Dialogs {
|
||||
.setMessageKey("dokanySupportEnd.message") //
|
||||
.setDescriptionKey("dokanySupportEnd.description") //
|
||||
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||
.setOkButtonKey("generic.button.close") //
|
||||
.setOkButtonKey(BUTTON_KEY_CLOSE) //
|
||||
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
|
||||
.setOkAction(Stage::close) //
|
||||
.setCancelAction(cancelAction);
|
||||
@@ -83,8 +134,20 @@ public class Dialogs {
|
||||
.setDescriptionKey("retryIfReadonly.description") //
|
||||
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||
.setOkButtonKey("retryIfReadonly.retry") //
|
||||
.setCancelButtonKey("generic.button.close") //
|
||||
.setCancelButtonKey(BUTTON_KEY_CLOSE) //
|
||||
.setOkAction(okAction) //
|
||||
.setCancelAction(Stage::close);
|
||||
}
|
||||
|
||||
public SimpleDialog.Builder prepareNoDDirectorySelectedDialog(Stage window) {
|
||||
return createDialogBuilder() //
|
||||
.setOwner(window) //
|
||||
.setTitleKey("recover.invalidSelection.title") //
|
||||
.setMessageKey("recover.invalidSelection.message") //
|
||||
.setDescriptionKey("recover.invalidSelection.description") //
|
||||
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||
.setOkButtonKey("generic.button.change") //
|
||||
.setOkAction(Stage::close);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.dialogs;
|
||||
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
@@ -30,15 +31,15 @@ public class SimpleDialog {
|
||||
|
||||
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( //
|
||||
new SimpleDialogController(resolveText(builder.messageKey, null), //
|
||||
resolveText(builder.descriptionKey, null), //
|
||||
resolveText(builder.descriptionKey, builder.descriptionArgs), //
|
||||
builder.icon, //
|
||||
resolveText(builder.okButtonKey, null), //
|
||||
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
|
||||
() -> builder.okAction.accept(dialogStage), //
|
||||
() -> builder.cancelAction.accept(dialogStage)), //
|
||||
Scene::new, builder.resourceBundle);
|
||||
builder.sceneFactory, builder.resourceBundle);
|
||||
|
||||
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
|
||||
dialogStage.setScene(loaderFactory.createScene(FxmlFile.SIMPLE_DIALOG));
|
||||
}
|
||||
|
||||
public void showAndWait() {
|
||||
@@ -62,19 +63,22 @@ public class SimpleDialog {
|
||||
private Stage owner;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final StageFactory stageFactory;
|
||||
private final DefaultSceneFactory sceneFactory;
|
||||
private String titleKey;
|
||||
private String[] titleArgs;
|
||||
private String messageKey;
|
||||
private String descriptionKey;
|
||||
private String[] descriptionArgs;
|
||||
private String okButtonKey;
|
||||
private String cancelButtonKey;
|
||||
private FontAwesome5Icon icon;
|
||||
private Consumer<Stage> okAction = Stage::close;
|
||||
private Consumer<Stage> cancelAction = Stage::close;
|
||||
|
||||
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) {
|
||||
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.stageFactory = stageFactory;
|
||||
this.sceneFactory = sceneFactory;
|
||||
}
|
||||
|
||||
public Builder setOwner(Stage owner) {
|
||||
@@ -93,8 +97,9 @@ public class SimpleDialog {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDescriptionKey(String descriptionKey) {
|
||||
public Builder setDescriptionKey(String descriptionKey, String... args) {
|
||||
this.descriptionKey = descriptionKey;
|
||||
this.descriptionArgs = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.cryptomator.ui.eventview;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
@@ -115,7 +118,7 @@ public class EventListCellController implements FxController {
|
||||
eventActionsMenu.hide();
|
||||
eventActionsMenu.getItems().clear();
|
||||
eventTooltip.setText(item.getKey().vault().getDisplayName());
|
||||
addAction("generic.action.dismiss", () -> {
|
||||
addLocalizedAction("generic.action.dismiss", () -> {
|
||||
fileSystemEventAggregator.remove(item.getKey());
|
||||
});
|
||||
switch (item.getValue().mostRecentEvent()) {
|
||||
@@ -124,20 +127,41 @@ public class EventListCellController implements FxController {
|
||||
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
|
||||
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
|
||||
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
|
||||
case FileIsInUseEvent fse -> this.adjustToFileInUseEvent(fse);
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustToFileInUseEvent(FileIsInUseEvent fiiue) {
|
||||
eventIcon.setValue(FontAwesome5Icon.USER_LOCK);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
|
||||
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
||||
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
|
||||
}
|
||||
|
||||
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||
var user = userAndDevice[0];
|
||||
var device = userAndDevice.length == 1 ? userAndDevice[0] : userAndDevice[1];
|
||||
addLocalizedAction("eventView.entry.inUse.copyUserAndDevice", () -> copyToClipboard(user + ", " + device));
|
||||
addLocalizedAction("eventView.entry.inUse.ignoreLock", fiiue.ignoreMethod());
|
||||
}
|
||||
|
||||
|
||||
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
|
||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
} else {
|
||||
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
||||
}
|
||||
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||
}
|
||||
|
||||
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
|
||||
@@ -145,9 +169,9 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
} else {
|
||||
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,11 +180,11 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
} else {
|
||||
addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||
addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,9 +193,9 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
} else {
|
||||
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,14 +204,19 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
} else {
|
||||
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addAction(String localizationKey, Runnable action) {
|
||||
var entry = new MenuItem(resourceBundle.getString(localizationKey));
|
||||
private void addLocalizedAction(String localizationKey, Runnable action) {
|
||||
var entryText = resourceBundle.getString(localizationKey);
|
||||
addAction(entryText, action);
|
||||
}
|
||||
|
||||
private void addAction(String entryText, Runnable action) {
|
||||
var entry = new MenuItem(entryText);
|
||||
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
|
||||
entry.setOnAction(_ -> action.run());
|
||||
eventActionsMenu.getItems().addLast(entry);
|
||||
@@ -234,18 +263,17 @@ public class EventListCellController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private Path convertVaultPathToSystemPath(Path p) {
|
||||
if (!(p instanceof CryptoPath)) {
|
||||
throw new IllegalArgumentException("Path " + p + " is not a vault path");
|
||||
}
|
||||
private Path convertVaultPathToSystemPath(String vaultInternalPath) {
|
||||
var v = eventEntry.getValue().getKey().vault();
|
||||
if (!v.isUnlocked()) {
|
||||
return Path.of(System.getProperty("user.home"));
|
||||
}
|
||||
|
||||
var mountUri = v.getMountPoint().uri();
|
||||
var internalPath = p.toString().substring(1);
|
||||
return Path.of(mountUri.getPath().concat(internalPath).substring(1));
|
||||
var mountPoint = v.getMountPoint().uri().getPath();
|
||||
if(SystemUtils.IS_OS_WINDOWS) {
|
||||
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
|
||||
}
|
||||
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
||||
}
|
||||
|
||||
private void reveal(RevealPathService s, Path p) {
|
||||
|
||||
@@ -30,9 +30,20 @@ public class FxApplication {
|
||||
private final FxApplicationTerminator applicationTerminator;
|
||||
private final AutoUnlocker autoUnlocker;
|
||||
private final FxFSEventList fxFSEventList;
|
||||
private final FxNotificationManager notificationManager;
|
||||
|
||||
@Inject
|
||||
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) {
|
||||
FxApplication(@Named("startupTime") long startupTime, //
|
||||
Environment environment, //
|
||||
Settings settings, //
|
||||
AppLaunchEventHandler launchEventHandler, //
|
||||
Lazy<TrayMenuComponent> trayMenu, //
|
||||
FxApplicationWindows appWindows, //
|
||||
FxApplicationStyle applicationStyle, //
|
||||
FxApplicationTerminator applicationTerminator, //
|
||||
AutoUnlocker autoUnlocker, //
|
||||
FxFSEventList fxFSEventList, //
|
||||
FxNotificationManager notificationManager) {
|
||||
this.startupTime = startupTime;
|
||||
this.environment = environment;
|
||||
this.settings = settings;
|
||||
@@ -43,6 +54,7 @@ public class FxApplication {
|
||||
this.applicationTerminator = applicationTerminator;
|
||||
this.autoUnlocker = autoUnlocker;
|
||||
this.fxFSEventList = fxFSEventList;
|
||||
this.notificationManager = notificationManager;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
@@ -88,6 +100,7 @@ public class FxApplication {
|
||||
|
||||
launchEventHandler.startHandlingLaunchEvents();
|
||||
fxFSEventList.schedulePollForUpdates();
|
||||
notificationManager.schedulePollForUpdates();
|
||||
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,28 +7,29 @@ package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.decryptname.DecryptNameComponent;
|
||||
import org.cryptomator.ui.error.ErrorComponent;
|
||||
import org.cryptomator.ui.eventview.EventViewComponent;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.notification.NotificationComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||
import org.cryptomator.ui.sharevault.ShareVaultComponent;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.scene.image.Image;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
|
||||
@Module(subcomponents = {TrayMenuComponent.class, //
|
||||
DecryptNameComponent.class, //
|
||||
MainWindowComponent.class, //
|
||||
PreferencesComponent.class, //
|
||||
@@ -40,7 +41,9 @@ import java.io.InputStream;
|
||||
HealthCheckComponent.class, //
|
||||
UpdateReminderComponent.class, //
|
||||
ShareVaultComponent.class, //
|
||||
EventViewComponent.class})
|
||||
EventViewComponent.class, //
|
||||
RecoveryKeyComponent.class, //
|
||||
NotificationComponent.class})
|
||||
abstract class FxApplicationModule {
|
||||
|
||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||
@@ -49,6 +52,12 @@ abstract class FxApplicationModule {
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||
return UiAppearanceProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
||||
@@ -79,4 +88,10 @@ abstract class FxApplicationModule {
|
||||
return factory.create();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
|
||||
return factory.create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,82 +36,91 @@ public class FxApplicationStyle {
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
var uiTheme = settings.theme.get();
|
||||
if (uiTheme == UiTheme.AUTOMATIC) {
|
||||
registerOsThemeListener();
|
||||
}
|
||||
applyTheme(uiTheme);
|
||||
settings.theme.addListener(this::appThemeChanged);
|
||||
loadSelectedStyleSheet(settings.theme.get());
|
||||
}
|
||||
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, UiTheme oldValue, UiTheme newValue) {
|
||||
if (oldValue == newValue) {
|
||||
// no-op
|
||||
} else if (newValue == UiTheme.AUTOMATIC) {
|
||||
registerOsThemeListener();
|
||||
} else if (oldValue == UiTheme.AUTOMATIC) {
|
||||
removeOsThemeListener();
|
||||
}
|
||||
|
||||
applyTheme(newValue);
|
||||
}
|
||||
|
||||
private void removeOsThemeListener() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
try {
|
||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to disable automatic theme switching.");
|
||||
}
|
||||
}
|
||||
loadSelectedStyleSheet(newValue);
|
||||
}
|
||||
|
||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
case AUTOMATIC -> {
|
||||
appearanceProvider.ifPresent(provider -> {
|
||||
try {
|
||||
provider.addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to enable automatic theme switching.");
|
||||
}
|
||||
});
|
||||
applySystemTheme();
|
||||
LOG.warn("Failed to disable automatic theme switching.", e);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Unable to remove listener os theme changes: No supported UiAppearanceProvider present");
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme theme) {
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applySystemTheme() {
|
||||
private void registerOsThemeListener() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
||||
try {
|
||||
appearanceProvider.get().addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.warn("Failed to enable automatic theme switching.", e);
|
||||
}
|
||||
} else {
|
||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
||||
applyLightTheme();
|
||||
LOG.warn("Unable to register for os theme changes: No supported UiAppearanceProvider present");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLightTheme() {
|
||||
var stylesheet = Optional //
|
||||
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
|
||||
.orElse(getClass().getResource("/css/light_theme.css"));
|
||||
private void applyTheme(UiTheme uiTheme) {
|
||||
if (!licenseHolder.isValidLicense()) {
|
||||
loadAndApplyLightTheme();
|
||||
} else {
|
||||
switch (uiTheme) {
|
||||
case AUTOMATIC -> {
|
||||
var osTheme = appearanceProvider.map(UiAppearanceProvider::getSystemTheme).orElse(Theme.LIGHT);
|
||||
systemInterfaceThemeChanged(osTheme);
|
||||
}
|
||||
case LIGHT -> loadAndApplyLightTheme();
|
||||
case DARK -> loadAndApplyDarkTheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme osTheme) {
|
||||
switch (osTheme) {
|
||||
case LIGHT -> loadAndApplyLightTheme();
|
||||
case DARK -> loadAndApplyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndApplyLightTheme() {
|
||||
loadAndApplyTheme(Theme.LIGHT, "/css/light_theme.css");
|
||||
}
|
||||
|
||||
private void loadAndApplyDarkTheme() {
|
||||
loadAndApplyTheme(Theme.DARK, "/css/dark_theme.css");
|
||||
}
|
||||
|
||||
private void loadAndApplyTheme(Theme appTheme, String cssFile) {
|
||||
var stylesheet = getClass().getResource(cssFile);
|
||||
if (stylesheet == null) {
|
||||
LOG.warn("Failed to load light_theme stylesheet");
|
||||
} else {
|
||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
|
||||
appliedTheme.set(Theme.LIGHT);
|
||||
throw new IllegalStateException("Cannot find resource %s".formatted(cssFile));
|
||||
}
|
||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(appTheme));
|
||||
appliedTheme.set(appTheme);
|
||||
}
|
||||
|
||||
private void applyDarkTheme() {
|
||||
var stylesheet = Optional //
|
||||
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
|
||||
.orElse(getClass().getResource("/css/dark_theme.css"));
|
||||
if (stylesheet == null) {
|
||||
LOG.warn("Failed to load dark_theme stylesheet");
|
||||
} else {
|
||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
|
||||
appliedTheme.set(Theme.DARK);
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectProperty<Theme> appliedThemeProperty() {
|
||||
public ObjectProperty<Theme> appliedAppThemeProperty() {
|
||||
return appliedTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationTerminator {
|
||||
|
||||
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
|
||||
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR, VAULT_CONFIG_MISSING, ALL_MISSING);
|
||||
private static final Set<VaultState.Value> STATES_PREVENT_TERMINATION = EnumSet.of(PROCESSING);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.cryptomator.ui.error.ErrorComponent;
|
||||
import org.cryptomator.ui.eventview.EventViewComponent;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.notification.NotificationComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
@@ -54,6 +55,7 @@ public class FxApplicationWindows {
|
||||
private final LockComponent.Factory lockWorkflowFactory;
|
||||
private final ErrorComponent.Factory errorWindowFactory;
|
||||
private final Lazy<EventViewComponent> eventViewWindow;
|
||||
private final Lazy<NotificationComponent> notificationWindow;
|
||||
private final ExecutorService executor;
|
||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||
@@ -73,6 +75,7 @@ public class FxApplicationWindows {
|
||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||
ShareVaultComponent.Factory shareVaultWindow, //
|
||||
Lazy<EventViewComponent> eventViewWindow, //
|
||||
Lazy<NotificationComponent> notificationWindow,
|
||||
ExecutorService executor, //
|
||||
Dialogs dialogs) {
|
||||
this.primaryStage = primaryStage;
|
||||
@@ -85,6 +88,7 @@ public class FxApplicationWindows {
|
||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||
this.errorWindowFactory = errorWindowFactory;
|
||||
this.eventViewWindow = eventViewWindow;
|
||||
this.notificationWindow = notificationWindow;
|
||||
this.executor = executor;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
this.shareVaultWindow = shareVaultWindow;
|
||||
@@ -193,6 +197,10 @@ public class FxApplicationWindows {
|
||||
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showNotification() {
|
||||
return CompletableFuture.supplyAsync(() -> notificationWindow.get().showNotification(), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the generic error scene in the given window.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.cryptomator.ui.fxapp;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
@@ -11,6 +13,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -23,6 +26,8 @@ import java.util.concurrent.TimeUnit;
|
||||
@FxApplicationScoped
|
||||
public class FxFSEventList {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxFSEventList.class);
|
||||
|
||||
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
|
||||
private final FileSystemEventAggregator eventAggregator;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
@@ -37,7 +42,13 @@ public class FxFSEventList {
|
||||
}
|
||||
|
||||
public void schedulePollForUpdates() {
|
||||
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
|
||||
try {
|
||||
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
|
||||
} catch ( RejectedExecutionException e) {
|
||||
if(!scheduler.isShutdown()) {
|
||||
LOG.warn("Failed to poll for filesystem events", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.event.NotificationManager;
|
||||
import org.cryptomator.event.VaultEvent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Notification manager inside the UI domain.
|
||||
* <p>
|
||||
* Polls the {@link NotificationManager} for pending events every {@value POLL_INTERVAL_SECONDS } seconds and
|
||||
* triggers the notification window display when events are available.
|
||||
* Returns an observable list of events requiring a user notification with {@link #getEventsRequiringNotification()}.
|
||||
*
|
||||
* @see NotificationManager
|
||||
*/
|
||||
@FxApplicationScoped
|
||||
public class FxNotificationManager {
|
||||
|
||||
private static final int POLL_INTERVAL_SECONDS = 1;
|
||||
|
||||
private final NotificationManager notificationManager;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final FxApplicationWindows applicationWindows;
|
||||
private final ObservableList<VaultEvent> eventsRequiringNotification;
|
||||
|
||||
@Inject
|
||||
public FxNotificationManager(NotificationManager notificationManager, ScheduledExecutorService scheduler, FxApplicationWindows applicationWindows) {
|
||||
this.notificationManager = notificationManager;
|
||||
this.scheduler = scheduler;
|
||||
this.applicationWindows = applicationWindows;
|
||||
this.eventsRequiringNotification = FXCollections.observableArrayList();
|
||||
}
|
||||
|
||||
public void schedulePollForUpdates() {
|
||||
scheduler.scheduleAtFixedRate(this::checkForPendingNotifications, 0, POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void checkForPendingNotifications() {
|
||||
Platform.runLater(() -> {
|
||||
if (notificationManager.appendToAndClear(eventsRequiringNotification)) {
|
||||
applicationWindows.showNotification();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public ObservableList<VaultEvent> getEventsRequiringNotification() {
|
||||
return eventsRequiringNotification;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.integrations.common.DisplayName;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
import org.cryptomator.integrations.common.Priority;
|
||||
import org.cryptomator.integrations.uiappearance.Theme;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.ColorScheme;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@DisplayName("JavaFX Color Scheme switcher")
|
||||
@OperatingSystem(OperatingSystem.Value.LINUX)
|
||||
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
||||
@Priority(1050)
|
||||
public class JfxUiAppearanceProvider implements UiAppearanceProvider {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JfxUiAppearanceProvider.class);
|
||||
|
||||
private final ConcurrentHashMap<UiAppearanceListener, ChangeListener<ColorScheme>> uiAppearanceListeners = new ConcurrentHashMap<>();
|
||||
private final Platform.Preferences preferences = Platform.getPreferences(); //Note: this service impl MUST be loaded in the fx application thread
|
||||
|
||||
@Override
|
||||
public Theme getSystemTheme() {
|
||||
return switch (preferences.getColorScheme()) {
|
||||
case DARK -> Theme.DARK;
|
||||
case LIGHT -> Theme.LIGHT;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustToTheme(Theme theme) {
|
||||
//no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
|
||||
var fxChangeListener = (ChangeListener<ColorScheme>) (_, _, newScheme) -> {
|
||||
var newTheme = switch (newScheme) {
|
||||
case DARK -> Theme.DARK;
|
||||
case LIGHT -> Theme.LIGHT;
|
||||
};
|
||||
uiAppearanceListener.systemAppearanceChanged(newTheme);
|
||||
};
|
||||
LOG.debug("Register listener for OS theme changes");
|
||||
uiAppearanceListeners.computeIfAbsent(uiAppearanceListener, k -> {
|
||||
Platform.runLater(() -> preferences.colorSchemeProperty().addListener(fxChangeListener));
|
||||
return fxChangeListener;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
|
||||
var fxChangeListener = uiAppearanceListeners.remove(uiAppearanceListener);
|
||||
if (fxChangeListener != null) {
|
||||
LOG.debug("Removing listener for OS theme changes");
|
||||
Platform.runLater(() -> preferences.colorSchemeProperty().removeListener(fxChangeListener));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.SemVerComparator;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.concurrent.ScheduledService;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
import javafx.util.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class UpdateChecker {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
||||
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
|
||||
|
||||
private final Environment env;
|
||||
private final Settings settings;
|
||||
private final StringProperty latestVersion = new SimpleStringProperty();
|
||||
private final ScheduledService<String> updateCheckerService;
|
||||
private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
|
||||
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||
private final Comparator<String> versionComparator = new SemVerComparator();
|
||||
private final BooleanBinding updateAvailable;
|
||||
private final BooleanBinding checkFailed;
|
||||
|
||||
@Inject
|
||||
UpdateChecker(Settings settings, //
|
||||
Environment env, //
|
||||
ScheduledService<String> updateCheckerService) {
|
||||
this.env = env;
|
||||
this.settings = settings;
|
||||
this.updateCheckerService = updateCheckerService;
|
||||
this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
|
||||
this.updateAvailable = Bindings.createBooleanBinding(this::isUpdateAvailable, latestVersion);
|
||||
this.checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, state);
|
||||
}
|
||||
|
||||
public void automaticallyCheckForUpdatesIfEnabled() {
|
||||
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
|
||||
startCheckingForUpdates(AUTO_CHECK_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkForUpdatesNow() {
|
||||
startCheckingForUpdates(Duration.ZERO);
|
||||
}
|
||||
|
||||
private void startCheckingForUpdates(Duration initialDelay) {
|
||||
updateCheckerService.cancel();
|
||||
updateCheckerService.reset();
|
||||
updateCheckerService.setDelay(initialDelay);
|
||||
updateCheckerService.setOnRunning(this::checkStarted);
|
||||
updateCheckerService.setOnSucceeded(this::checkSucceeded);
|
||||
updateCheckerService.setOnFailed(this::checkFailed);
|
||||
updateCheckerService.start();
|
||||
}
|
||||
|
||||
private void checkStarted(WorkerStateEvent event) {
|
||||
LOG.debug("Checking for updates...");
|
||||
state.set(UpdateCheckState.IS_CHECKING);
|
||||
}
|
||||
|
||||
private void checkSucceeded(WorkerStateEvent event) {
|
||||
var latestVersionString = updateCheckerService.getValue();
|
||||
LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersionString);
|
||||
lastSuccessfulUpdateCheck.set(Instant.now());
|
||||
latestVersion.set(latestVersionString);
|
||||
state.set(UpdateCheckState.CHECK_SUCCESSFUL);
|
||||
}
|
||||
|
||||
private void checkFailed(WorkerStateEvent event) {
|
||||
state.set(UpdateCheckState.CHECK_FAILED);
|
||||
}
|
||||
|
||||
public enum UpdateCheckState {
|
||||
NOT_CHECKED,
|
||||
IS_CHECKING,
|
||||
CHECK_SUCCESSFUL,
|
||||
CHECK_FAILED;
|
||||
}
|
||||
|
||||
/* Observable Properties */
|
||||
public BooleanBinding checkingForUpdatesProperty() {
|
||||
return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING);
|
||||
}
|
||||
|
||||
public ReadOnlyStringProperty latestVersionProperty() {
|
||||
return latestVersion;
|
||||
}
|
||||
|
||||
public BooleanBinding updateAvailableProperty() {
|
||||
return updateAvailable;
|
||||
}
|
||||
|
||||
public BooleanBinding checkFailedProperty() {
|
||||
return checkFailed;
|
||||
}
|
||||
|
||||
public boolean isUpdateAvailable() {
|
||||
String currentVersion = getCurrentVersion();
|
||||
String latestVersionString = latestVersion.get();
|
||||
|
||||
if (currentVersion == null || latestVersionString == null) {
|
||||
return false;
|
||||
} else {
|
||||
return versionComparator.compare(currentVersion, latestVersionString) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectProperty<Instant> lastSuccessfulUpdateCheckProperty() {
|
||||
return lastSuccessfulUpdateCheck;
|
||||
}
|
||||
|
||||
public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public String getCurrentVersion() {
|
||||
return env.getAppVersion();
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.concurrent.ScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.util.Duration;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@Module
|
||||
public abstract class UpdateCheckerModule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerModule.class);
|
||||
|
||||
private static final URI LATEST_VERSION_URI = URI.create("https://api.cryptomator.org/desktop/latest-version.json");
|
||||
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
|
||||
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static Optional<HttpClient> provideHttpClient() {
|
||||
try {
|
||||
return Optional.of(HttpClient.newBuilder() //
|
||||
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
|
||||
.build());
|
||||
} catch (UncheckedIOException e) {
|
||||
LOG.error("HttpClient for update check cannot be created.", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static HttpRequest provideCheckForUpdatesRequest(Environment env) {
|
||||
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", //
|
||||
env.getAppVersion(), //
|
||||
SystemUtils.OS_NAME, //
|
||||
SystemUtils.OS_VERSION, //
|
||||
SystemUtils.OS_ARCH); //
|
||||
return HttpRequest.newBuilder() //
|
||||
.uri(LATEST_VERSION_URI) //
|
||||
.header("User-Agent", userAgent) //
|
||||
.timeout(java.time.Duration.ofSeconds(10))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("checkForUpdatesInterval")
|
||||
@FxApplicationScoped
|
||||
static ObjectBinding<Duration> provideCheckForUpdateInterval(Settings settings) {
|
||||
return Bindings.when(settings.checkForUpdates).then(UPDATE_CHECK_INTERVAL).otherwise(DISABLED_UPDATE_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static ScheduledService<String> provideCheckForUpdatesService(ExecutorService executor, Optional<HttpClient> httpClient, HttpRequest checkForUpdatesRequest, @Named("checkForUpdatesInterval") ObjectBinding<Duration> period) {
|
||||
ScheduledService<String> service = new ScheduledService<>() {
|
||||
@Override
|
||||
protected Task<String> createTask() {
|
||||
if (httpClient.isPresent()) {
|
||||
return new UpdateCheckerTask(httpClient.get(), checkForUpdatesRequest);
|
||||
} else {
|
||||
return new Task<>() {
|
||||
@Override
|
||||
protected String call() {
|
||||
throw new NullPointerException("No HttpClient present.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
service.setOnFailed(event -> LOG.error("Failed to execute update service", service.getException()));
|
||||
service.setExecutor(executor);
|
||||
service.periodProperty().bind(period);
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.concurrent.Task;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
public class UpdateCheckerTask extends Task<String> {
|
||||
|
||||
private static final ObjectMapper JSON = new ObjectMapper();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerTask.class);
|
||||
|
||||
private static final long MAX_RESPONSE_SIZE = 10L * 1024; // 10kb should be sufficient. protect against flooding
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final HttpRequest checkForUpdatesRequest;
|
||||
|
||||
UpdateCheckerTask(HttpClient httpClient, HttpRequest checkForUpdatesRequest) {
|
||||
this.httpClient = httpClient;
|
||||
this.checkForUpdatesRequest = checkForUpdatesRequest;
|
||||
|
||||
setOnFailed(event -> LOG.error("Failed to check for updates", getException()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String call() throws IOException, InterruptedException {
|
||||
HttpResponse<InputStream> response = httpClient.send(checkForUpdatesRequest, HttpResponse.BodyHandlers.ofInputStream());
|
||||
if (response.statusCode() == 200) {
|
||||
return processBody(response);
|
||||
} else {
|
||||
throw new IOException("Unexpected HTTP response code " + response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
private String processBody(HttpResponse<InputStream> response) throws IOException {
|
||||
try (InputStream in = response.body(); //
|
||||
InputStream limitedIn = ByteStreams.limit(in, MAX_RESPONSE_SIZE)) {
|
||||
var json = JSON.reader().readTree(limitedIn);
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
return json.get("mac").asText();
|
||||
} else if (SystemUtils.IS_OS_WINDOWS) {
|
||||
return json.get("win").asText();
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
return json.get("linux").asText();
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported operating system");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user