Compare commits

..
Author SHA1 Message Date
Armin Schrenk 0da557068b prevent usage of cached zip file 2025-06-30 17:04:10 +02:00
Armin Schrenk 50fb1c3042 use same jpackage for msi and appimage 2025-06-30 12:07:58 +02:00
Armin Schrenk f2525791f8 fix path 2025-06-30 11:54:56 +02:00
Armin Schrenk 01d9fe32b5 use proper download url 2025-06-30 11:41:54 +02:00
Armin Schrenk 68e0da37f5 use patched jdk version to build msi on arm 2025-06-30 11:21:26 +02:00
240 changed files with 1820 additions and 6890 deletions
-4
View File
@@ -20,10 +20,6 @@
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. 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 ## 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). 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).
@@ -1,76 +0,0 @@
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/trusted-signing-action@fc390cf8ed0f14e248a542af1d838388a47c7a7c # v0.5.10
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 }}
trusted-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
+16 -16
View File
@@ -19,7 +19,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.1+8.0.LTS' JAVA_VERSION: '24.0.1+9'
jobs: jobs:
get-version: get-version:
@@ -37,16 +37,16 @@ jobs:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
appimage-suffix: x86_64 appimage-suffix: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
openjfx-sha: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e' openjfx-sha: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
appimage-suffix: aarch64 appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
openjfx-sha: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4' openjfx-sha: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
@@ -75,7 +75,7 @@ jobs:
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
run: mvn -B clean package -Plinux -DskipTests run: mvn -B clean package -Plinux -DskipTests -Djavafx.platform=linux
- name: Patch target dir - name: Patch target dir
run: | run: |
cp LICENSE.txt target cp LICENSE.txt target
@@ -95,7 +95,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}" --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.cryptoki,jdk.crypto.ec,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.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -117,6 +117,7 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" --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 "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
@@ -132,7 +133,6 @@ jobs:
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" --java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--resource-dir dist/linux/resources --resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir - name: Patch Cryptomator.AppDir
run: | run: |
@@ -175,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
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: appimage-${{ matrix.appimage-suffix }} name: appimage-${{ matrix.appimage-suffix }}
path: | path: |
@@ -185,7 +185,7 @@ jobs:
if-no-files-found: error if-no-files-found: error
- name: Publish AppImage on GitHub Releases - name: Publish AppImage on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
@@ -198,10 +198,10 @@ jobs:
name: Create PR for aur-bin repo name: Create PR for aur-bin repo
needs: [build, get-version] needs: [build, get-version]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable' if: github.event_name == 'release'
steps: steps:
- name: Download AppImages - name: Download AppImages
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@v4
with: with:
path: downloads/ path: downloads/
merge-multiple: true merge-multiple: true
@@ -212,7 +212,7 @@ jobs:
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT" echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1) AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT" echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
with: with:
repository: 'cryptomator/aur-bin' repository: 'cryptomator/aur-bin'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -247,7 +247,7 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification - name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 uses: rtCamp/action-slack-notify@v2
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot' SLACK_USERNAME: 'Cryptobot'
+4 -6
View File
@@ -19,8 +19,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [get-version] needs: [get-version]
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable' if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
env:
INPUT_TAG: ${{ inputs.tag }}
outputs: outputs:
url: ${{ steps.url.outputs.url}} url: ${{ steps.url.outputs.url}}
sha256: ${{ steps.sha256.outputs.sha256}} sha256: ${{ steps.sha256.outputs.sha256}}
@@ -29,8 +27,8 @@ jobs:
id: url id: url
run: | run: |
URL=""; URL="";
if [[ -n "${INPUT_TAG}" ]]; then if [[ -n "${{ inputs.tag }}" ]]; then
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${INPUT_TAG}.tar.gz" URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
else else
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz" URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
fi fi
@@ -48,7 +46,7 @@ jobs:
env: env:
AUR_PR_URL: tbd AUR_PR_URL: tbd
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
with: with:
repository: 'cryptomator/aur' repository: 'cryptomator/aur'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -81,8 +79,8 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification - name: Slack Notification
uses: rtCamp/action-slack-notify@v2
if: github.event_name == 'release' if: github.event_name == 'release'
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot' SLACK_USERNAME: 'Cryptobot'
+10 -22
View File
@@ -7,16 +7,6 @@ on:
description: "Url to the file to upload" description: "Url to the file to upload"
required: true required: true
type: string 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: workflow_dispatch:
inputs: inputs:
url: url:
@@ -40,18 +30,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
fileName: ${{ steps.extractName.outputs.fileName}} fileName: ${{ steps.extractName.outputs.fileName}}
env:
INPUT_URL: ${{ inputs.url }}
steps: steps:
- name: Extract file name - name: Extract file name
id: extractName id: extractName
run: | run: |
url="${INPUT_URL}" url="${{ inputs.url }}"
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
- name: Download file - name: Download file
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body run: curl --remote-name ${{ inputs.url }} -L -o ${{steps.extractName.outputs.fileName}}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: ${{ steps.extractName.outputs.fileName }} name: ${{ steps.extractName.outputs.fileName }}
path: ${{ steps.extractName.outputs.fileName }} path: ${{ steps.extractName.outputs.fileName }}
@@ -60,15 +48,15 @@ jobs:
name: Anti Virus Allowlisting Kaspersky name: Anti Virus Allowlisting Kaspersky
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: download-file needs: download-file
if: inputs.kaspersky if: github.event_name == 'workflow_call' || inputs.kaspersky
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@v4
with: with:
name: ${{ needs.download-file.outputs.fileName }} name: ${{ needs.download-file.outputs.fileName }}
path: upload path: upload
- name: Upload to Kaspersky - name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # v4.6.3 uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with: with:
protocol: ftps protocol: ftps
server: allowlist.kaspersky-labs.com server: allowlist.kaspersky-labs.com
@@ -80,15 +68,15 @@ jobs:
name: Anti Virus Allowlisting Avast name: Anti Virus Allowlisting Avast
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: download-file needs: download-file
if: inputs.avast if: github.event_name == 'workflow_call' || inputs.avast
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@v4
with: with:
name: ${{ needs.download-file.outputs.fileName }} name: ${{ needs.download-file.outputs.fileName }}
path: upload path: upload
- name: Upload to Avast - name: Upload to Avast
uses: wlixcc/SFTP-Deploy-Action@a5ccb9c6211a94cc59404f0fdb2a9936a6dfee64 # v1.2.6 uses: wlixcc/SFTP-Deploy-Action@v1.2.6
with: with:
server: whitelisting.avast.com server: whitelisting.avast.com
port: 22 port: 22
+17 -22
View File
@@ -11,7 +11,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: 25 JAVA_VERSION: 24
defaults: defaults:
run: run:
@@ -22,14 +22,14 @@ jobs:
name: Compile and Test name: Compile and Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 - uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Cache SonarCloud packages - name: Cache SonarCloud packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: actions/cache@v4
with: with:
path: ~/.sonar/cache path: ~/.sonar/cache
key: ${{ runner.os }}-sonar key: ${{ runner.os }}-sonar
@@ -37,7 +37,7 @@ jobs:
- name: Build and Test - name: Build and Test
run: > run: >
xvfb-run xvfb-run
mvn -B verify mvn -B verify -Djavafx.platform=linux
jacoco:report jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Pcoverage -Pcoverage
@@ -49,28 +49,28 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Draft a release - name: Draft a release
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
draft: true draft: true
discussion_category_name: releases discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true generate_release_notes: true
body: |- body: |-
> [!NOTE] :construction: Work in Progress
> 🚧 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 🎉 ### 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). ### Bugfixes 🐛
### Other Changes 📎
--- ---
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 <!-- Don't forget to include the
💾 SHA-256 checksums of release artifacts: 💾 SHA-256 checksums of release artifacts:
@@ -78,9 +78,4 @@ jobs:
``` ```
--> -->
> [!TIP] 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).
> 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: -->
+4 -4
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
JDK_VERSION: '25.0.1+8.0.LTS' JDK_VERSION: '24.0.1+9'
JDK_VENDOR: temurin JDK_VENDOR: temurin
RUNTIME_VERSION_HELPER: > RUNTIME_VERSION_HELPER: >
public class Test { public class Test {
@@ -23,10 +23,10 @@ jobs:
JDK_MAJOR_VERSION: 'toBeFilled' JDK_MAJOR_VERSION: 'toBeFilled'
steps: steps:
- name: Determine current major version - name: Determine current major version
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV" run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$env:GITHUB_ENV"
shell: pwsh shell: pwsh
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }} - name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
java-version: ${{ env.JDK_MAJOR_VERSION}} java-version: ${{ env.JDK_MAJOR_VERSION}}
distribution: ${{ env.JDK_VENDOR }} distribution: ${{ env.JDK_VENDOR }}
@@ -70,7 +70,7 @@ jobs:
} }
- name: Notify - name: Notify
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true' if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 uses: rtCamp/action-slack-notify@v2
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot' SLACK_USERNAME: 'Cryptobot'
+15 -18
View File
@@ -23,12 +23,13 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.1+8.0.LTS' JAVA_VERSION: '24.0.1+9'
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1' COFFEELIBS_JDK: 24
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip' COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3'
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip' OPENJFX_JMODS_AMD64_HASH: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4' 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'
jobs: jobs:
get-version: get-version:
@@ -40,34 +41,30 @@ jobs:
name: Build Debian Package name: Build Debian Package
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: [get-version] needs: [get-version]
env:
INPUT_PPAVER: ${{ inputs.ppaver }}
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- id: deb-version - id: deb-version
name: Determine deb-version name: Determine deb-version
run: | run: |
if [ -n "${INPUT_PPAVER}" ]; then if [ -n "${{inputs.ppaver}}" ]; then
echo "debVersion=${INPUT_PPAVER}" >> "$GITHUB_OUTPUT" echo "debVersion=${{inputs.ppaver }}" >> "$GITHUB_OUTPUT"
else else
echo "debVersion=${{needs.get-version.outputs.semVerStr}}" >> "$GITHUB_OUTPUT" echo "debVersion=${{needs.get-version.outputs.semVerStr}}" >> "$GITHUB_OUTPUT"
fi fi
- name: Install build tools - name: Install build tools
run: | run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update sudo apt-get update
sudo apt-get install devscripts dput sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
env:
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
check-latest: true check-latest: true
cache: 'maven' cache: 'maven'
- name: Run maven - name: Run maven
run: mvn -B clean package -Plinux -DskipTests run: mvn -B clean package -Plinux -Djavafx.platform=linux -DskipTests
- name: Download OpenJFX jmods - name: Download OpenJFX jmods
id: download-jmods id: download-jmods
run: | run: |
@@ -143,7 +140,7 @@ jobs:
run: | run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: linux-deb-package name: linux-deb-package
path: | path: |
+3 -4
View File
@@ -7,13 +7,12 @@ on:
jobs: jobs:
check-dependencies: check-dependencies:
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@1074588008ae3326a2221ea451783280518f0366 # v3.0.1 uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@v1
with: with:
runner-os: 'ubuntu-latest' runner-os: 'ubuntu-latest'
java-distribution: 'temurin' java-distribution: 'temurin'
java-version: 25 java-version: 24
check-command: 'mvn -B validate -Pdependency-check -Djavafx.platform=linux'
secrets: secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }} nvd-api-key: ${{ secrets.NVD_API_KEY }}
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
ossindex-token: ${{ secrets.OSSINDEX_API_TOKEN }}
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
steps: steps:
- name: Get download count of latest releases - name: Get download count of latest releases
id: get-stats id: get-stats
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 uses: actions/github-script@v7
with: with:
script: | script: |
const query = `query($owner:String!, $name:String!) { const query = `query($owner:String!, $name:String!) {
@@ -53,7 +53,7 @@ jobs:
INTERVAL: 900 INTERVAL: 900
JSON_DATA: ${{ steps.get-stats.outputs.result }} JSON_DATA: ${{ steps.get-stats.outputs.result }}
- name: Upload Results - name: Upload Results
uses: fjogeleit/http-request-action@c0b95d02a088b47c1f2f4db04fd8af8bd19eee54 # v1.16.6 uses: fjogeleit/http-request-action@v1
with: with:
url: 'https://graphite-us-central1.grafana.net/metrics' url: 'https://graphite-us-central1.grafana.net/metrics'
method: 'POST' method: 'POST'
+3 -3
View File
@@ -14,7 +14,7 @@ jobs:
- name: Query Discussion Data - name: Query Discussion Data
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted' if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
id: query-data id: query-data
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 uses: actions/github-script@v7
with: with:
script: | script: |
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) { const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
@@ -42,7 +42,7 @@ jobs:
return await github.graphql(query, variables) return await github.graphql(query, variables)
- name: Get Gist - name: Get Gist
id: get-gist id: get-gist
uses: andymckay/get-gist-action@cf3bc8164af24126f7e5979eb6d3dc0c12309bd1 # not_tagged uses: andymckay/get-gist-action@master
with: with:
gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230 gistURL: https://gist.github.com/cryptobot/accba9fb9555e7192271b85606f97230
- name: Merge Error Code Data - name: Merge Error Code Data
@@ -58,7 +58,7 @@ jobs:
env: env:
DISCUSSION: ${{ steps.query-data.outputs.result }} DISCUSSION: ${{ steps.query-data.outputs.result }}
- name: Patch Gist - name: Patch Gist
uses: exuanbo/actions-deploy-gist@47697fceaeea2006a90594ee24eb9cd0a1121ef8 # v1.1.4 uses: exuanbo/actions-deploy-gist@v1
with: with:
token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }} token: ${{ secrets.CRYPTOBOT_GIST_TOKEN }}
gist_id: accba9fb9555e7192271b85606f97230 gist_id: accba9fb9555e7192271b85606f97230
+8 -5
View File
@@ -26,10 +26,13 @@ jobs:
- name: Determine tarball url - name: Determine tarball url
id: url id: url
run: | run: |
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" 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
echo "url=${URL}" >> "$GITHUB_OUTPUT" echo "url=${URL}" >> "$GITHUB_OUTPUT"
env:
TAG: ${{ inputs.tag || github.event.release.tag_name}}
- name: Download source tarball and compute checksum - name: Download source tarball and compute checksum
id: sha512 id: sha512
run: | run: |
@@ -43,7 +46,7 @@ jobs:
env: env:
FLATHUB_PR_URL: tbd FLATHUB_PR_URL: tbd
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
with: with:
repository: 'flathub/org.cryptomator.Cryptomator' repository: 'flathub/org.cryptomator.Cryptomator'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -71,7 +74,7 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification - name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 uses: rtCamp/action-slack-notify@v2
if: github.event_name == 'release' if: github.event_name == 'release'
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
+6 -8
View File
@@ -23,7 +23,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: 25 JAVA_VERSION: 24
jobs: jobs:
determine-version: determine-version:
@@ -35,11 +35,11 @@ jobs:
revNum: ${{ steps.versions.outputs.revNum }} revNum: ${{ steps.versions.outputs.revNum }}
type: ${{ steps.versions.outputs.type}} type: ${{ steps.versions.outputs.type}}
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
@@ -49,8 +49,8 @@ jobs:
run: | run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/} SEM_VER_STR=${GITHUB_REF##*/}
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${VERSION_STRING}" SEM_VER_STR="${{ inputs.version }}"
else else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi fi
@@ -70,9 +70,7 @@ jobs:
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
echo "type=${TYPE}" >> $GITHUB_OUTPUT echo "type=${TYPE}" >> $GITHUB_OUTPUT
env:
VERSION_STRING: ${{ inputs.version }}
- name: Validate Version - name: Validate Version
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0 uses: skymatic/semver-validation-action@v3
with: with:
version: ${{ steps.versions.outputs.semVerStr }} version: ${{ steps.versions.outputs.semVerStr }}
+12 -26
View File
@@ -24,7 +24,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.1+8.0.LTS' JAVA_VERSION: '24.0.1+9'
jobs: jobs:
get-version: get-version:
@@ -44,12 +44,12 @@ jobs:
architecture: x64 architecture: x64
output-suffix: x64 output-suffix: x64
fuse-lib: macFUSE fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-x64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-x64_bin-jmods.zip'
openjfx-sha: '0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52' openjfx-sha: '6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105'
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
@@ -79,7 +79,7 @@ jobs:
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
run: mvn -B clean package -Pmac -DskipTests run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
- name: Patch target dir - name: Patch target dir
run: | run: |
cp LICENSE.txt target cp LICENSE.txt target
@@ -99,7 +99,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}" --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.cryptoki,jdk.crypto.ec,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.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -121,6 +121,7 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m" --java-options "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
@@ -136,7 +137,6 @@ jobs:
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\"" --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true" --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 "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator --mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources --resource-dir dist/mac/resources
@@ -151,23 +151,9 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }} REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} 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 - name: Generate license for dmg
run: > run: >
mvn -B license:add-third-party mvn -B -Djavafx.platform=mac license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf -Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources -Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl -Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -262,7 +248,7 @@ jobs:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }} CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg - name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3 uses: cocoalibs/xcode-notarization-action@v1
with: with:
app-path: 'Cryptomator-*.dmg' app-path: 'Cryptomator-*.dmg'
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }} apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
@@ -283,7 +269,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true continue-on-error: true
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: dmg-${{ matrix.output-suffix }} name: dmg-${{ matrix.output-suffix }}
path: | path: |
@@ -292,7 +278,7 @@ jobs:
if-no-files-found: error if-no-files-found: error
- name: Publish dmg on GitHub Releases - name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
+12 -27
View File
@@ -22,7 +22,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.1+8.0.LTS' JAVA_VERSION: '24.0.1+9'
jobs: jobs:
get-version: get-version:
@@ -42,12 +42,12 @@ jobs:
architecture: aarch64 architecture: aarch64
output-suffix: arm64 output-suffix: arm64
fuse-lib: FUSE-T fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-aarch64_bin-jmods.zip' openjfx-url: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_osx-aarch64_bin-jmods.zip'
openjfx-sha: '13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba' openjfx-sha: 'b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4'
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
@@ -77,7 +77,7 @@ jobs:
- name: Set version - name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
run: mvn -B clean package -Pmac -DskipTests run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
- name: Patch target dir - name: Patch target dir
run: | run: |
cp LICENSE.txt target cp LICENSE.txt target
@@ -97,7 +97,7 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}" --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.cryptoki,jdk.crypto.ec,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.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
@@ -119,6 +119,7 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options "-Xss5m" --java-options "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Dfile.encoding=\"utf-8\""
@@ -134,9 +135,7 @@ jobs:
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\"" --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true" --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 "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--mac-package-identifier org.cryptomator --mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources --resource-dir dist/mac/resources
- name: Patch Cryptomator.app - name: Patch Cryptomator.app
@@ -150,23 +149,9 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }} REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} 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 - name: Generate license for dmg
run: > run: >
mvn -B license:add-third-party mvn -B -Djavafx.platform=mac license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf -Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources -Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl -Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -261,7 +246,7 @@ jobs:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }} CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg - name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3 uses: cocoalibs/xcode-notarization-action@v1
with: with:
app-path: 'Cryptomator-*.dmg' app-path: 'Cryptomator-*.dmg'
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }} apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
@@ -282,7 +267,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true continue-on-error: true
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: dmg-${{ matrix.output-suffix }} name: dmg-${{ matrix.output-suffix }}
path: | path: |
@@ -291,7 +276,7 @@ jobs:
if-no-files-found: error if-no-files-found: error
- name: Publish dmg on GitHub Releases - name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 - uses: actions/stale@v9
with: with:
days-before-stale: 14 days-before-stale: 14
days-before-close: 0 days-before-close: 0
+2 -2
View File
@@ -19,14 +19,14 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Publish asc on GitHub Releases - name: Publish asc on GitHub Releases
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: | files: |
cryptomator-*.tar.gz.asc cryptomator-*.tar.gz.asc
- name: Slack Notification - name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 uses: rtCamp/action-slack-notify@v2
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot' SLACK_USERNAME: 'Cryptobot'
+4 -4
View File
@@ -5,7 +5,7 @@ on:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: 25 JAVA_VERSION: 24
defaults: defaults:
run: run:
@@ -16,11 +16,11 @@ jobs:
name: Compile and Test name: Compile and Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 - uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
cache: 'maven' cache: 'maven'
- name: Build and Test - name: Build and Test
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage run: xvfb-run mvn -B clean install jacoco:report -Pcoverage -Djavafx.platform=linux
+5 -5
View File
@@ -12,16 +12,16 @@ defaults:
env: env:
JAVA_DIST: 'temurin' JAVA_DIST: 'temurin'
JAVA_VERSION: 25 JAVA_VERSION: 23
jobs: jobs:
check-preconditions: check-preconditions:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ env.JAVA_DIST }} distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }} java-version: ${{ env.JAVA_VERSION }}
@@ -49,7 +49,7 @@ jobs:
exit 1 exit 1
fi fi
- name: Cache NVD DB - name: Cache NVD DB
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: actions/cache@v4
with: with:
path: ~/.m2/repository/org/owasp/dependency-check-data/ path: ~/.m2/repository/org/owasp/dependency-check-data/
key: dependency-check-${{ github.run_id }} key: dependency-check-${{ github.run_id }}
@@ -60,6 +60,6 @@ jobs:
- name: Run org.owasp:dependency-check plugin - name: Run org.owasp:dependency-check plugin
id: dependency-check id: dependency-check
continue-on-error: true continue-on-error: true
run: mvn -B verify -Pdependency-check -DskipTests run: mvn -B verify -Pdependency-check -DskipTests -Djavafx.platform=linux
env: env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }} NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 - uses: actions/stale@v9
with: with:
days-before-stale: 365 days-before-stale: 365
days-before-close: 90 days-before-close: 90
+116 -96
View File
@@ -8,9 +8,8 @@ on:
version: version:
description: 'Version' description: 'Version'
required: false required: false
sign: isDebug:
description: 'Sign binaries' description: 'Build debug version with console output'
required: false
type: boolean type: boolean
default: false default: false
push: push:
@@ -22,8 +21,8 @@ on:
env: env:
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_windows-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_windows-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7' OPENJFX_JMODS_AMD64_HASH: 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi' WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a' WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe' WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
@@ -47,13 +46,21 @@ jobs:
include: include:
- arch: x64 - arch: x64
os: windows-latest os: windows-latest
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427 java-dist: 'zulu'
java-version: '25.0.1+8' java-version: '24.0.1+9'
java-package: 'jdk' 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
env:
LOOPBACK_ALIAS: 'cryptomator-vault'
WIN_CONSOLE_FLAG: ''
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ matrix.java-dist }} distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }} java-version: ${{ matrix.java-version }}
@@ -93,7 +100,7 @@ jobs:
- name: Set version - name: Set version
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven - name: Run maven
run: mvn -B clean package -Pwin -DskipTests run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
- name: Patch target dir - name: Patch target dir
run: | run: |
cp LICENSE.txt target cp LICENSE.txt target
@@ -113,12 +120,25 @@ jobs:
--verbose --verbose
--output runtime --output runtime
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}" --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.cryptoki,jdk.crypto.ec,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.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
--strip-native-commands --strip-native-commands
--no-header-files --no-header-files
--no-man-pages --no-man-pages
--strip-debug --strip-debug
--compress zip-0 --compress zip-0
- name: Change win-console flag if debug is active
if: ${{ inputs.isDebug }}
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
- name: Setup patched JDK if necessary
id: setup-patched-jdk
run: |
if [ "${{ matrix.arch }}" == "arm64" ]; then
curl --output patched-jdk.zip -L "https://static.cryptomator.org/desktop/patched-win-jdk-2.zip"
unzip patched-jdk.zip -d patched-jdk
echo "JAVA_HOME=${{ github.workspace }}/patched-jdk/jdk" >> $GITHUB_OUTPUT
else
echo "JAVA_HOME=${JAVA_HOME}" >> $GITHUB_OUTPUT
fi
- name: Run jpackage - name: Run jpackage
run: > run: >
${JAVA_HOME}/bin/jpackage ${JAVA_HOME}/bin/jpackage
@@ -135,6 +155,7 @@ jobs:
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview" --java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win" --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 "-Xss5m"
--java-options "-Xmx256m" --java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
@@ -146,24 +167,33 @@ jobs:
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\"" --java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\"" --java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\"" --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\"" --java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
--java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\"" --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\"" --java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
--java-options "-Dcryptomator.disableUpdateCheck=false" --java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
--resource-dir dist/win/resources --resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico --icon dist/win/resources/Cryptomator.ico
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties" ${WIN_CONSOLE_FLAG}
env:
JAVA_HOME: ${{ steps.setup-patched-jdk.outputs.JAVA_HOME }}
- name: Patch Application Directory - name: Patch Application Directory
run: | run: |
cp dist/win/contrib/* appdir/Cryptomator cp dist/win/contrib/* appdir/Cryptomator
- name: Fix permissions - name: Set LOOPBACK_ALIAS in patchWebDAV.bat
shell: pwsh
run: | run: |
attrib -r appdir/Cryptomator/Cryptomator.exe $patchScript = "appdir\Cryptomator\patchWebDAV.bat"
attrib -r "appdir/Cryptomator/Cryptomator (Debug).exe" try {
(Get-Content $patchScript ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"${{ env.LOOPBACK_ALIAS}}`"" | Set-Content $patchScript
} catch {
Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
exit 1
}
- name: Fix permissions
run: attrib -r appdir/Cryptomator/Cryptomator.exe
shell: pwsh shell: pwsh
- name: Extract jars with DLLs for Codesigning - name: Extract jars with DLLs for Codesigning
shell: pwsh shell: pwsh
@@ -188,27 +218,16 @@ jobs:
New-Item -Path appdir/jpackage-jmod -ItemType Directory 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" & $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" Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
- name: Sign DLLs with Azure Trusted Signing - name: Codesign
if: inputs.sign || github.event_name == 'release' uses: skymatic/code-sign-action@v3
uses: ./.github/actions/win-sign-action
with: with:
base-dir: ${{ github.workspace }}\appdir certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: Cryptomator
timestampUrl: 'http://timestamp.digicert.com'
folder: appdir
recursive: true 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@1074588008ae3326a2221ea451783280518f0366 # no specific version
with:
base-dir: 'appdir'
file-extensions: 'dll,exe,ps1'
recursive: true
sign-description: 'Cryptomator'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Replace DLLs inside jars with signed ones - name: Replace DLLs inside jars with signed ones
shell: pwsh shell: pwsh
run: | run: |
@@ -225,7 +244,7 @@ jobs:
} }
- name: Generate license for MSI - name: Generate license for MSI
run: > run: >
mvn -B license:add-third-party mvn -B license:add-third-party "-Djavafx.platform=win"
"-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.outputDirectory=dist/win/resources" "-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl" "-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
@@ -255,18 +274,18 @@ jobs:
--license-file dist/win/resources/license.rtf --license-file dist/win/resources/license.rtf
--file-associations dist/win/resources/FAvaultFile.properties --file-associations dist/win/resources/FAvaultFile.properties
env: env:
JAVA_HOME: ${{ steps.setup-patched-jdk.outputs.JAVA_HOME }}
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Sign MSI with Azure Trusted Signing - name: Codesign MSI
if: inputs.sign || github.event_name == 'release' uses: skymatic/code-sign-action@v3
uses: ./.github/actions/win-sign-action
with: with:
base-dir: ${{ github.workspace }}\installer certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
file-extensions: msi password: ${{ secrets.WIN_CODESIGN_P12_PW }}
description: 'Cryptomator Installer' certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
tenant-id: ${{ secrets.AZURE_TENANT_ID }} description: Cryptomator Installer
client-id: ${{ secrets.AZURE_CLIENT_ID }} timestampUrl: 'http://timestamp.digicert.com'
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} folder: installer
- name: Add possible alpha/beta tags and architecture to installer name - 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 run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
- name: Create detached GPG signature with key 615D449FE6E6A235 - name: Create detached GPG signature with key 615D449FE6E6A235
@@ -277,7 +296,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: msi-${{ matrix.arch }} name: msi-${{ matrix.arch }}
path: | path: |
@@ -298,22 +317,28 @@ jobs:
java-dist: 'zulu' java-dist: 'zulu'
java-version: '24.0.1+9' java-version: '24.0.1+9'
java-package: 'jdk' 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: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v4
- name: Install wix and extensions - name: Install wix and extensions
run: | run: |
dotnet tool install --global wix --version 6.0.0 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.BootstrapperApplications.wixext/6.0.0 --global
wix.exe extension add WixToolset.Util.wixext/6.0.0 --global wix.exe extension add WixToolset.Util.wixext/6.0.0 --global
- name: Download .msi - name: Download .msi
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@v4
with: with:
name: msi-${{ matrix.arch }} name: msi-${{ matrix.arch }}
path: dist/win/bundle/resources path: dist/win/bundle/resources
- name: Strip version info from msi file name - name: Strip version info from msi file name
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
- name: Setup Java - name: Setup Java
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 uses: actions/setup-java@v4
with: with:
distribution: ${{ matrix.java-dist }} distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }} java-version: ${{ matrix.java-version }}
@@ -322,7 +347,7 @@ jobs:
cache: 'maven' cache: 'maven'
- name: Generate license for exe - name: Generate license for exe
run: > run: >
mvn -B license:add-third-party mvn -B license:add-third-party "-Djavafx.platform=win"
"-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl" "-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources" "-Dlicense.outputDirectory=dist/win/bundle/resources"
@@ -349,6 +374,7 @@ jobs:
working-directory: dist/win working-directory: dist/win
run: > run: >
wix build wix build
-arch ${{ matrix.arch }}
-define BundleName="Cryptomator" -define BundleName="Cryptomator"
-define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}" -define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
-define BundleVendor="Skymatic GmbH" -define BundleVendor="Skymatic GmbH"
@@ -363,51 +389,27 @@ jobs:
- name: Detach burn engine in preparation to sign - name: Detach burn engine in preparation to sign
run: > run: >
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing - name: Codesign burn engine
if: inputs.sign || github.event_name == 'release' uses: skymatic/code-sign-action@v3
uses: ./.github/actions/win-sign-action
with: with:
base-dir: ${{ github.workspace }}\tmp certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
file-extensions: exe password: ${{ secrets.WIN_CODESIGN_P12_PW }}
append-signature: true certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: 'Cryptomator Bundle Installer' description: Cryptomator Installer
tenant-id: ${{ secrets.AZURE_TENANT_ID }} timestampUrl: 'http://timestamp.digicert.com'
client-id: ${{ secrets.AZURE_CLIENT_ID }} folder: tmp
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@1074588008ae3326a2221ea451783280518f0366 # no specific version
with:
base-dir: 'tmp'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Reattach signed burn engine to installer - name: Reattach signed burn engine to installer
run: > run: >
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe 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 - name: Codesign EXE
if: inputs.sign || github.event_name == 'release' uses: skymatic/code-sign-action@v3
uses: ./.github/actions/win-sign-action
with: with:
base-dir: ${{ github.workspace }}\installer certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
file-extensions: exe password: ${{ secrets.WIN_CODESIGN_P12_PW }}
append-signature: true certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
description: 'Cryptomator Bundle Installer' description: Cryptomator Installer
tenant-id: ${{ secrets.AZURE_TENANT_ID }} timestampUrl: 'http://timestamp.digicert.com'
client-id: ${{ secrets.AZURE_CLIENT_ID }} folder: installer
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@1074588008ae3326a2221ea451783280518f0366 # no specific version
with:
base-dir: 'installer'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Add possible alpha/beta tags to installer name - name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
- name: Create detached GPG signature with key 615D449FE6E6A235 - name: Create detached GPG signature with key 615D449FE6E6A235
@@ -418,7 +420,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@v4
with: with:
name: exe-${{ matrix.executable-suffix }} name: exe-${{ matrix.executable-suffix }}
path: | path: |
@@ -433,22 +435,26 @@ jobs:
needs: [ build-msi, build-exe ] needs: [ build-msi, build-exe ]
outputs: outputs:
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }} 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-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
download-url-exe-arm64: ${{ fromJSON(steps.publish.outputs.assets)[3].browser_download_url }}
steps: steps:
- name: Download installers - name: Download installers
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@v4
with: with:
merge-multiple: true merge-multiple: true
- name: Publish installers on GitHub Releases - name: Publish installers on GitHub Releases
id: publish id: publish
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@v2
with: with:
fail_on_unmatched_files: true fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
# do not change ordering of filelist, required for correct job output # do not change ordering of filelist, required for correct job output
files: | files: |
*x64.msi *x64.msi
*arm64.msi
*x64.exe *x64.exe
*arm64.exe
*.asc *.asc
allowlist-msi-x64: allowlist-msi-x64:
@@ -458,6 +464,13 @@ jobs:
url: ${{ needs.publish.outputs.download-url-msi-x64 }} url: ${{ needs.publish.outputs.download-url-msi-x64 }}
secrets: inherit 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: allowlist-exe-x64:
uses: ./.github/workflows/av-whitelist.yml uses: ./.github/workflows/av-whitelist.yml
needs: [ publish, allowlist-msi-x64 ] needs: [ publish, allowlist-msi-x64 ]
@@ -465,6 +478,13 @@ jobs:
url: ${{ needs.publish.outputs.download-url-exe-x64 }} url: ${{ needs.publish.outputs.download-url-exe-x64 }}
secrets: inherit 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: notify-winget:
name: Notify for winget-release name: Notify for winget-release
if: needs.get-version.outputs.versionType == 'stable' if: needs.get-version.outputs.versionType == 'stable'
@@ -472,7 +492,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Slack Notification - name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 uses: rtCamp/action-slack-notify@v2
env: env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot' SLACK_USERNAME: 'Cryptobot'
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Submit package - name: Submit package
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version uses: vedantmgoyal2009/winget-releaser@main
with: with:
identifier: Cryptomator.Cryptomator identifier: Cryptomator.Cryptomator
version: ${{ inputs.tag }} version: ${{ inputs.tag }}
+10 -11
View File
@@ -14,15 +14,16 @@
<option name="dagger.fastInit" value="enabled" /> <option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" /> <option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.57.2/dagger-compiler-2.57.2.jar" /> <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.57.2/dagger-2.57.2.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.55/dagger-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.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$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.57.2/dagger-spi-2.57.2.jar" /> <entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.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.1.21-2.0.2/symbol-processing-api-2.1.21-2.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$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" /> <entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.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$/org/jetbrains/annotations/13.0/annotations-13.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/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/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$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -30,16 +31,14 @@
<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/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/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/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$/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-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-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$/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$/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/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.1.21/kotlin-metadata-jvm-2.1.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
</processorPath> </processorPath>
<module name="cryptomator" /> <module name="cryptomator" />
</profile> </profile>
+5 -3
View File
@@ -1,8 +1,10 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="Deprecation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="DEPRECATED_ATTRIBUTES" /> <inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<inspection_tool class="MarkedForRemoval" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" /> <option name="processCode" value="true" />
<inspection_tool class="RedundantScheduledForRemovalAnnotation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" /> <option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile> </profile>
</component> </component>
+1 -1
View File
@@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_24" project-jdk-name="24" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs> </envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" /> <option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" /> <module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -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=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
-22
View File
@@ -1,22 +0,0 @@
# 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 labels for GUI elements ([#4064](https://github.com/cryptomator/cryptomator/issues/4064), [#4066](https://github.com/cryptomator/cryptomator/pull/4066), [#4055](https://github.com/cryptomator/cryptomator/issues/4055))
### Changed
* Built using JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/issues/4031))
* Modernized Template for GitHub Releases
+5 -7
View File
@@ -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} mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile # compile
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=linux
cp ../../../LICENSE.txt ../../../target cp ../../../LICENSE.txt ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods cp ../../../target/cryptomator-*.jar ../../../target/mods
JAVAFX_VERSION=25 JAVAFX_VERSION=24.0.1
JAVAFX_ARCH="x64" JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256='96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e' JAVAFX_JMODS_SHA256='425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
if [ "${CPU_ARCH}" = "aarch64" ]; then if [ "${CPU_ARCH}" = "aarch64" ]; then
JAVAFX_ARCH="aarch64" JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256='951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1' JAVAFX_JMODS_SHA256='7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
fi fi
# download javaFX jmods # download javaFX jmods
@@ -62,7 +62,7 @@ ${JAVA_HOME}/bin/jlink \
--verbose \ --verbose \
--output runtime \ --output runtime \
--module-path "${JMOD_PATHS}" \ --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.crypto.cryptoki,jdk.crypto.ec,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.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -98,7 +98,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \ --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \ --java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
--resource-dir ../resources --resource-dir ../resources
# transform AppDir # transform AppDir
@@ -115,7 +114,6 @@ 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.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
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/org.cryptomator.Cryptomator.svg
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon 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 ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
@@ -1,9 +0,0 @@
<?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,12 +83,6 @@
</content_rating> </content_rating>
<releases> <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>
<release date="2025-06-24" version="1.17.0"> <release date="2025-06-24" version="1.17.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.17.0</url> <url type="details">https://github.com/cryptomator/cryptomator/releases/1.17.0</url>
</release> </release>
+2 -2
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org> Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils Section: utils
Priority: optional Priority: optional
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1 Build-Depends: debhelper (>=10), coffeelibs-jdk-24 (>= 24.0.1+9-0ppa3), libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0 Standards-Version: 4.5.0
Homepage: https://cryptomator.org Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git Vcs-Git: https://github.com/cryptomator/cryptomator.git
@@ -12,7 +12,7 @@ Package: cryptomator
Architecture: any Architecture: any
Section: utils Section: utils
Priority: optional Priority: optional
Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3, libgtk-3-0 (>= 3.20.0) Depends: ${shlibs:Depends}, ${misc:Depends}, fuse3
Recommends: gvfs-backends, gvfs-fuse, gnome-keyring Recommends: gvfs-backends, gvfs-fuse, gnome-keyring
XB-AppName: Cryptomator XB-AppName: Cryptomator
XB-Category: Utility;Security;FileTools; XB-Category: Utility;Security;FileTools;
+1 -2
View File
@@ -6,5 +6,4 @@ 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.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
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
View File
@@ -25,7 +25,6 @@ case "$1" in
fi fi
xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop 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.vault.xml
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
;; ;;
abort-upgrade|abort-remove|abort-deconfigure) abort-upgrade|abort-remove|abort-deconfigure)
-1
View File
@@ -23,7 +23,6 @@ case "$1" in
xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop 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.vault.xml
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
;; ;;
failed-upgrade) failed-upgrade)
+3 -3
View File
@@ -4,12 +4,11 @@
# Uncomment this to turn on verbose mode. # Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1 #export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-24-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64) ifeq ($(DEB_BUILD_ARCH),amd64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
else ifeq ($(DEB_BUILD_ARCH),arm64) else ifeq ($(DEB_BUILD_ARCH),arm64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
endif endif
@@ -29,7 +28,7 @@ override_dh_auto_build:
$(JAVA_HOME)/bin/jlink \ $(JAVA_HOME)/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JMODS_PATH}" \ --module-path "${JMODS_PATH}" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.cryptoki,jdk.crypto.ec,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.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -46,6 +45,7 @@ override_dh_auto_build:
--vendor "Skymatic GmbH" \ --vendor "Skymatic GmbH" \
--java-options "--enable-preview" \ --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 "--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" \ --copyright "(C) 2016 - 2025 Skymatic GmbH" \
--java-options "-Xss5m" \ --java-options "-Xss5m" \
--java-options "-Xmx256m" \ --java-options "-Xmx256m" \
-1
View File
@@ -1,2 +1 @@
embedded.provisionprofile embedded.provisionprofile
xcuserdata/
-19
View File
@@ -1,19 +0,0 @@
//
// 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()
}
}
@@ -1,314 +0,0 @@
// !$*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 */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -1,67 +0,0 @@
<?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>
+6 -21
View File
@@ -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'` VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
FUSE_LIB="FUSE-T" FUSE_LIB="FUSE-T"
JAVAFX_VERSION=25 JAVAFX_VERSION=24.0.1
JAVAFX_ARCH="undefined" JAVAFX_ARCH="undefined"
JAVAFX_JMODS_SHA256="undefined" JAVAFX_JMODS_SHA256="undefined"
if [ "$(machine)" = "arm64e" ]; then if [ "$(machine)" = "arm64e" ]; then
JAVAFX_ARCH="aarch64" JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256="13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba" JAVAFX_JMODS_SHA256="b5a94a13077507003fa852512bfa33f4fb680bc8076d8002e4227a84c85171d4"
else else
JAVAFX_ARCH="x64" JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256="0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52" JAVAFX_JMODS_SHA256="6e62a426d43c168a488521f904a523f3dd6ee2cf103e08136f2fd465c828a105"
fi fi
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip" 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 fi
# compile # compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac mvn -B -Djavafx.platform=mac -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
@@ -85,7 +85,7 @@ fi
${JAVA_HOME}/bin/jlink \ ${JAVA_HOME}/bin/jlink \
--output runtime \ --output runtime \
--module-path "${JMOD_PATHS}" \ --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.crypto.cryptoki,jdk.crypto.ec,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.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,java.compiler \
--strip-native-commands \ --strip-native-commands \
--no-header-files \ --no-header-files \
--no-man-pages \ --no-man-pages \
@@ -115,7 +115,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dsun.java2d.metal=true" \ --java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \ --java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \ --java-options "-Dcryptomator.logDir=\"@{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.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \ --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.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
@@ -123,7 +122,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \ --java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \ --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \ --java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \ --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \ --mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources --resource-dir ../resources
@@ -134,21 +132,8 @@ 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 sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/ 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 # generate license
mvn -B -f../../../pom.xml license:add-third-party \ mvn -B -Djavafx.platform=mac -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \ -Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \ -Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \ -Dlicense.fileTemplate=resources/licenseTemplate.ftl \
-4
View File
@@ -105,7 +105,6 @@
<array> <array>
<string>c9r</string> <string>c9r</string>
<string>c9s</string> <string>c9s</string>
<string>c9u</string>
</array> </array>
<key>public.mime-type</key> <key>public.mime-type</key>
<array> <array>
@@ -117,8 +116,5 @@
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html --> <!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
<key>NSSupportsAutomaticGraphicsSwitching</key> <key>NSSupportsAutomaticGraphicsSwitching</key>
<true/> <true/>
<!-- register dock tile plugin -->
<key>NSDockTilePlugIn</key>
<string>Cryptomator.docktileplugin</string>
</dict> </dict>
</plist> </plist>
+1 -2
View File
@@ -4,8 +4,7 @@ installer
*.wixobj *.wixobj
*.pdb *.pdb
*.msi *.msi
*Debug.properties
*.exe *.exe
*.jmod *.jmod
resources/jfxJmods.zip resources/jfxJmods.zip
license.rtf license.rtf
+1 -5
View File
@@ -11,10 +11,6 @@ SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator" SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
SET LOOPBACK_ALIAS="cryptomator-vault" SET LOOPBACK_ALIAS="cryptomator-vault"
:: read clean parameter from command line
SET CLEAN=0
IF "%~1"=="clean" SET CLEAN=1
pwsh -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^ pwsh -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^ -AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^ -MainJarGlob "%MAIN_JAR_GLOB%"^
@@ -26,4 +22,4 @@ pwsh -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
-HelpUrl "%HELP_URL%"^ -HelpUrl "%HELP_URL%"^
-UpdateUrl "%UPDATE_URL%"^ -UpdateUrl "%UPDATE_URL%"^
-LoopbackAlias "%LOOPBACK_ALIAS%"^ -LoopbackAlias "%LOOPBACK_ALIAS%"^
-Clean %CLEAN% -Clean 1
+47 -73
View File
@@ -9,15 +9,9 @@ Param(
[Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl, [Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
[Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl, [Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
[Parameter(Mandatory, HelpMessage="Please provide an alias for localhost")][string] $LoopbackAlias, [Parameter(Mandatory, HelpMessage="Please provide an alias for localhost")][string] $LoopbackAlias,
[bool] $clean = $false # if true, cleans up previous build artifacts [bool] $clean
) )
# ============================
# Function Definitions Section
# ============================
function Main {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s $ProgressPreference = 'SilentlyContinue' # disables Invoke-WebRequest's progress bar, which slows down downloads to a few bytes/s
@@ -56,16 +50,16 @@ $version = $(mvn -f $buildDir/../../pom.xml help:evaluate -Dexpression="project.
$semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1' $semVerNo = $version -replace '(\d+\.\d+\.\d+).*','$1'
$revisionNo = $(git rev-list --count HEAD) $revisionNo = $(git rev-list --count HEAD)
Write-Host "`$version=$version" Write-Output "`$version=$version"
Write-Host "`$semVerNo=$semVerNo" Write-Output "`$semVerNo=$semVerNo"
Write-Host "`$revisionNo=$revisionNo" Write-Output "`$revisionNo=$revisionNo"
Write-Host "`$buildDir=$buildDir" Write-Output "`$buildDir=$buildDir"
Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME" Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor" $copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile # compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin &mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin "-Djavafx.platform=win"
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods" Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime # add runtime
@@ -77,7 +71,7 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
## download jfx jmods for X64, while they are part of the Arm64 JDK ## download jfx jmods for X64, while they are part of the Arm64 JDK
$archCode = (Get-CimInstance Win32_Processor).Architecture $archCode = (Get-CimInstance Win32_Processor).Architecture
$archName = switch ($archCode) { $archName = switch ($archCode) {
9 { "x64" } 9 { "x64 (AMD64)" }
12 { "ARM64" } 12 { "ARM64" }
default { "WMI Win32_Processor.Architecture code ($archCode)" } default { "WMI Win32_Processor.Architecture code ($archCode)" }
} }
@@ -86,20 +80,20 @@ switch ($archName) {
'ARM64' { 'ARM64' {
$javafxBaseJmod = Join-Path $Env:JAVA_HOME "jmods\javafx.base.jmod" $javafxBaseJmod = Join-Path $Env:JAVA_HOME "jmods\javafx.base.jmod"
if (!(Test-Path $javafxBaseJmod)) { if (!(Test-Path $javafxBaseJmod)) {
Write-Error "JavaFX module not found in JDK. Please ensure a JDK with JavaFX (including jmods) is installed." Write-Error "JavaFX module not found in JDK. Please ensure full JDK (including jmods) is installed."
exit 1 exit 1
} }
$jmodPaths = "$Env:JAVA_HOME/jmods" $jmodPaths = "$Env:JAVA_HOME/jmods"
} }
'x64' { 'x64 (AMD64)' {
$javaFxVersion='25' $javaFxVersion='24.0.1'
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip" $javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
$javaFxJmodsSHA256 = 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7' $javaFxJmodsSHA256 = 'f13d17c7caf88654fc835f1b4e75a9b0f34a888eb8abef381796c0002e63b03f'
$javaFxJmods = '.\resources\jfxJmods.zip' $javaFxJmods = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $javaFxJmods) ) { if( !(Test-Path -Path $javaFxJmods) ) {
Write-Host "Downloading ${javaFxJmodsUrl}..." Write-Output "Downloading ${javaFxJmodsUrl}..."
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
} }
@@ -133,7 +127,7 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
--verbose ` --verbose `
--output runtime ` --output runtime `
--module-path $jmodPaths ` --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.cryptoki,jdk.crypto.ec,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.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
--strip-native-commands ` --strip-native-commands `
--no-header-files ` --no-header-files `
--no-man-pages ` --no-man-pages `
@@ -145,31 +139,6 @@ if ($clean -and (Test-Path -Path $appPath)) {
Remove-Item -Path $appPath -Force -Recurse Remove-Item -Path $appPath -Force -Recurse
} }
$javaOptions = @(
"--java-options", "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
"--java-options", "-Xss5m"
"--java-options", "-Xmx256m"
"--java-options", "-Dcryptomator.appVersion=`"$semVerNo`""
"--java-options", "-Dfile.encoding=`"utf-8`""
"--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.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
"--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
"--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
"--java-options", "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`""
"--java-options", "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`""
"--java-options", "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`""
"--java-options", "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$AppName/keychain.json;@{userhome}/AppData/Roaming/$AppName/keychain.json`""
"--java-options", "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=`"@{appdata}/$AppName/windowsHelloKeychain.json`""
"--java-options", "-Dcryptomator.showTrayIcon=true"
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
)
# create app dir # create app dir
& "$Env:JAVA_HOME\bin\jpackage" ` & "$Env:JAVA_HOME\bin\jpackage" `
--verbose ` --verbose `
@@ -182,19 +151,31 @@ $javaOptions = @(
--name $AppName ` --name $AppName `
--vendor $Vendor ` --vendor $Vendor `
--copyright $copyright ` --copyright $copyright `
--java-options "--enable-preview" `
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win" `
--java-options "-Xss5m" `
--java-options "-Xmx256m" `
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" ` --app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Djava.net.useSystemProxies=true" `
--java-options "-Dcryptomator.logDir=`"@{localappdata}/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.mountPointsDir=`"@{userhome}/$AppName`"" `
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"@{appdata}/$AppName/keychain.json;@{userhome}/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=`"@{appdata}/$AppName/windowsHelloKeychain.json`"" `
--java-options "-Dcryptomator.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources ` --resource-dir resources `
--icon resources/$AppName.ico ` --icon resources/$AppName.ico
--add-launcher "${AppName} (Debug)=$buildDir\debug-launcher.properties" `
@javaOptions
if ($LASTEXITCODE -ne 0) {
Write-Error "jpackage Appimage failed with exit code $LASTEXITCODE"
return 1;
}
#Create RTF license for msi #Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` &mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
"-Dlicense.thirdPartyFilename=license.rtf" ` "-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" ` "-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\resources\" ` "-Dlicense.outputDirectory=$buildDir\resources\" `
@@ -206,7 +187,14 @@ if ($LASTEXITCODE -ne 0) {
# patch app dir # patch app dir
Copy-Item "contrib\*" -Destination "$AppName" Copy-Item "contrib\*" -Destination "$AppName"
attrib -r "$AppName\$AppName.exe" attrib -r "$AppName\$AppName.exe"
attrib -r "$AppName\${AppName} (Debug).exe" # patch batch script to set hostfile
$webDAVPatcher = "$AppName\patchWebDAV.bat"
try {
(Get-Content $webDAVPatcher ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"$LoopbackAlias`"" | Set-Content $webDAVPatcher
} catch {
Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
exit 1
}
# create .msi # create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources" $Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
@@ -237,7 +225,7 @@ if ($LASTEXITCODE -ne 0) {
} }
#Create RTF license for bundle #Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` &mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
"-Dlicense.thirdPartyFilename=license.rtf" ` "-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" ` "-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" ` "-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
@@ -249,7 +237,7 @@ if ($LASTEXITCODE -ne 0) {
# download Winfsp # download Winfsp
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi' $winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
$winfspMsiHash = '073A70E00F77423E34BED98B86E600DEF93393BA5822204FAC57A29324DB9F7A' $winfspMsiHash = '073A70E00F77423E34BED98B86E600DEF93393BA5822204FAC57A29324DB9F7A'
Write-Host "Downloading ${winfspMsiUrl}..." Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
$computedHash = $(Get-FileHash -Path '.\bundle\resources\winfsp.msi' -Algorithm SHA256).Hash $computedHash = $(Get-FileHash -Path '.\bundle\resources\winfsp.msi' -Algorithm SHA256).Hash
if (! $computedHash.Equals($winfspMsiHash)) { if (! $computedHash.Equals($winfspMsiHash)) {
@@ -263,7 +251,7 @@ if (! $computedHash.Equals($winfspMsiHash)) {
# download legacy-winfsp uninstaller # download legacy-winfsp uninstaller
$winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe' $winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
Write-Host "Downloading ${winfspUninstaller}..." Write-Output "Downloading ${winfspUninstaller}..."
Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
# copy MSI to bundle resources # copy MSI to bundle resources
@@ -283,18 +271,4 @@ Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName
.\bundle\bundleWithWinfsp.wxs ` .\bundle\bundleWithWinfsp.wxs `
-out "installer\$AppName-Installer.exe" -out "installer\$AppName-Installer.exe"
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe" Write-Output "Created EXE installer .\installer\$AppName-Installer.exe"
return 0;
}
# ============================
# Script Execution Starts Here
# ============================
if ($clean) {
Write-Host "Cleaning up previous build artifacts..."
Remove-Item -Path ".\runtime" -Force -Recurse -ErrorAction Ignore -ProgressAction SilentlyContinue
Remove-Item -Path ".\$AppName" -Force -Recurse -ErrorAction Ignore -ProgressAction SilentlyContinue
Remove-Item -Path ".\installer" -Force -Recurse -ErrorAction Ignore -ProgressAction SilentlyContinue
}
return Main
+1 -5
View File
@@ -27,8 +27,6 @@
<ns0:Payload Name="Cryptobot.ico" SourceFile="bundle\resources\Cryptomator.ico"/> <ns0:Payload Name="Cryptobot.ico" SourceFile="bundle\resources\Cryptomator.ico"/>
</ns0:BootstrapperApplication> </ns0:BootstrapperApplication>
<ns0:Variable Name="DISABLEUPDATECHECK" bal:Overridable="yes" Type="string" Value="false"/>
<ns0:Chain> <ns0:Chain>
<ns0:ExePackage Cache="keep" PerMachine="yes" Permanent="no" SourceFile="bundle\resources\winfsp-uninstaller.exe" DisplayName="Removing outdated WinFsp Driver" Description="Executable to remove old winfsp" DetectCondition="false" InstallCondition="(InstalledLegacyWinFspVersion &lt;&gt; v0.0.0.0) AND ((WixBundleAction = 7) OR (WixBundleAction = 5))" UninstallArguments=""> <ns0:ExePackage Cache="keep" PerMachine="yes" Permanent="no" SourceFile="bundle\resources\winfsp-uninstaller.exe" DisplayName="Removing outdated WinFsp Driver" Description="Executable to remove old winfsp" DetectCondition="false" InstallCondition="(InstalledLegacyWinFspVersion &lt;&gt; v0.0.0.0) AND ((WixBundleAction = 7) OR (WixBundleAction = 5))" UninstallArguments="">
<ns0:CommandLine Condition="WixBundleUILevel &lt;= 3" InstallArgument="-q -l &quot;[WixBundleLog].winfsp-uninstaller.log&quot;" RepairArgument="-q" UninstallArgument="-s" /> <ns0:CommandLine Condition="WixBundleUILevel &lt;= 3" InstallArgument="-q -l &quot;[WixBundleLog].winfsp-uninstaller.log&quot;" RepairArgument="-q" UninstallArgument="-s" />
@@ -43,9 +41,7 @@ Do you want to continue?&quot;" RepairArgument="-q" UninstallArgument="-s" />
<ns0:ExitCode Behavior="forceReboot" Value="4" /> <ns0:ExitCode Behavior="forceReboot" Value="4" />
<ns0:ExitCode Behavior="success" Value="5" /> <ns0:ExitCode Behavior="success" Value="5" />
</ns0:ExePackage> </ns0:ExePackage>
<ns0:MsiPackage SourceFile="bundle\resources\Cryptomator.msi" CacheId="cryptomator-bundle-cryptomator" Visible="no"> <ns0:MsiPackage SourceFile="bundle\resources\Cryptomator.msi" CacheId="cryptomator-bundle-cryptomator" Visible="no" />
<ns0:MsiProperty Name="DISABLEUPDATECHECK" Value="[DISABLEUPDATECHECK]"/>
</ns0:MsiPackage>
<ns0:MsiPackage SourceFile="bundle\resources\winfsp.msi" CacheId="cryptomator-bundle-winfsp" Visible="yes" Permanent="yes" /> <ns0:MsiPackage SourceFile="bundle\resources\winfsp.msi" CacheId="cryptomator-bundle-winfsp" Visible="yes" Permanent="yes" />
</ns0:Chain> </ns0:Chain>
</ns0:Bundle> </ns0:Bundle>
-18
View File
@@ -1,18 +0,0 @@
@echo off
:: Batch wrapper for PowerShell script to modify Cryptomator update check settings
:: This is executed as a Custom Action during MSI installation
:: This file must be located in the INSTALLDIR
set "DISABLEUPDATECHECK=%~1"
:: Log for debugging
echo DISABLEUPDATECHECK=%DISABLEUPDATECHECK%
:: Change to INSTALLDIR
cd %~dp0
:: Execute the PowerShell script
powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File ".\patchUpdateCheck.ps1"^
-DisableUpdateCheck "%DISABLEUPDATECHECK%"
:: Return the exit code from PowerShell
exit /b %ERRORLEVEL%
-58
View File
@@ -1,58 +0,0 @@
# PowerShell script to modify Cryptomator.cfg to set disableUpdateCheck property
# This script is executed as a Custom Action during MSI installation
# If the DisableUpdateCheck parameter is set to true, it disables the update check in Cryptomator by modifying the Cryptomator.cfg file.
# NOTE: This file must be located in the same directory as set in the MSI property INSTALLDIR
param(
[Parameter(Mandatory)][string]$DisableUpdateCheck
)
try {
# Log parameters for debugging (visible in MSI verbose logs)
Write-Host "DisableUpdateCheck: $DisableUpdateCheck"
# Parse DisableUpdateCheck value (handle various input formats)
$shouldDisable = $false
if ($DisableUpdateCheck) {
$DisableUpdateCheck = $DisableUpdateCheck.Trim().ToLower()
$shouldDisable = ($DisableUpdateCheck -eq 'true') -or ($DisableUpdateCheck -eq '1') -or ($DisableUpdateCheck -eq 'yes')
}
Write-Host "Setting cryptomator.disableUpdateCheck to: $shouldDisable"
if (-not $shouldDisable) {
Write-Host 'Disable-Update-Check property is by default "false". Skipping config modification.'
exit 0
}
# Determine the .cfg file path
$cfgDir = Join-Path $PSScriptRoot 'app'
$cfgFiles = Get-ChildItem -Path $cfgDir -Filter '*.cfg' -File
if ($cfgFiles.Count -eq 0) {
Write-Error "No .cfg file found in directory: $cfgDir"
exit 1
}
foreach ($file in $cfgFiles) {
$cfgFile = $file.FullName
Write-Host "Modifying configuration file: $cfgFile"
# Read the current configuration
$content = Get-Content $cfgFile -Raw -ErrorAction Stop
# Add the new option based on the property value
# Use regular expressions substitutions to replace the property
$searchExpression = '(?<Prefix>java-options=-Dcryptomator\.disableUpdateCheck)=false'
$replacementExpression = '${Prefix}=true'
$content = $content -replace $searchExpression,$replacementExpression
# Write the modified content back
Set-Content -Path $cfgFile -Value $content -NoNewline
Write-Host "Successfully updated $cfgFile"
}
exit 0
}
catch {
Write-Error "Error modifying configuration file: $_"
exit 1
}
+4 -15
View File
@@ -1,18 +1,7 @@
@echo off @echo off
:: Batch wrapper for PowerShell script to adjust Windows network settings for the Cryptomator WebDAVAdapter :: Default values for Cryptomator builds
:: This is executed as a Custom Action during MSI installation ::REPLACE ME
:: This file must be located in the INSTALLDIR
set "LOOPBACK_ALIAS=%1"
:: Log for debugging
echo LOOPBACK_ALIAS=%LOOPBACK_ALIAS%
:: Change to INSTALLDIR
cd %~dp0 cd %~dp0
:: Execute the PowerShell script powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\patchWebDAV.ps1^ -LoopbackAlias %LOOPBACK_ALIAS%
-LoopbackAlias %LOOPBACK_ALIAS%
:: Return the exit code from PowerShell
exit /b %ERRORLEVEL%
-4
View File
@@ -1,4 +0,0 @@
win-console=true
win-shortcut=false
win-menu=false
description=Debug Launcher with Console for Cryptomator
+15
View File
@@ -0,0 +1,15 @@
@echo off
java ^
-p "mods" ^
-cp "libs/*" ^
-Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator/settings.json" ^
-Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator/ipc.socket" ^
-Dcryptomator.logDir="~/AppData/Roaming/Cryptomator" ^
-Dcryptomator.mountPointsDir="~/Cryptomator" ^
-Dcryptomator.integrationsWin.keychainPaths="~/AppData/Roaming/Cryptomator/keychain.json" ^
-Dcryptomator.integrationsWin.windowsHelloKeychainPaths="~/AppData/Roaming/Cryptomator/windowsHelloKeychain.json" ^
-Xss20m ^
-Xmx512m ^
--enable-preview `
--enable-native-access=org.cryptomator.jfuse.win `
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
+1 -13
View File
@@ -26,7 +26,6 @@
<?define IconFileEncryptedData= "Cryptomator-Vault.ico" ?> <?define IconFileEncryptedData= "Cryptomator-Vault.ico" ?>
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?> <?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
<?define CloseApplicationTarget= "cryptomator.exe" ?> <?define CloseApplicationTarget= "cryptomator.exe" ?>
<?define LoopbackAlias= "cryptomator-vault" ?>
<?include $(var.JpConfigDir)/overrides.wxi ?> <?include $(var.JpConfigDir)/overrides.wxi ?>
@@ -94,7 +93,6 @@
<ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/> <ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/>
</ns0:Extension> </ns0:Extension>
<ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/> <ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
</ns0:ProgId> </ns0:ProgId>
</ns0:Component> </ns0:Component>
</ns0:DirectoryRef> </ns0:DirectoryRef>
@@ -128,18 +126,10 @@
<ns0:Property Id="WixQuietExec64CmdTimeout" Value="20" /> <ns0:Property Id="WixQuietExec64CmdTimeout" Value="20" />
<!-- Note for custom actions: Immediate CAs run BEFORE the files are installed, hence if you depend on installed files, the CAs must be deferred.--> <!-- Note for custom actions: Immediate CAs run BEFORE the files are installed, hence if you depend on installed files, the CAs must be deferred.-->
<!-- Property for controlling update check behavior (can be set via command line) -->
<ns0:Property Id="DISABLEUPDATECHECK" Secure="yes" />
<!-- WebDAV patches --> <!-- WebDAV patches -->
<ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot;" Sequence="execute" Before="PatchWebDAV" /> <ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot;" Sequence="execute" Before="PatchWebDAV" />
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/> <ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Update check configuration -->
<ns0:SetProperty Id="PatchUpdateCheck" Value="&quot;[INSTALLDIR]patchUpdateCheck.bat&quot; &quot;[DISABLEUPDATECHECK]&quot;" Sequence="execute" Before="PatchUpdateCheck" />
<ns0:CustomAction Id="PatchUpdateCheck" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Running App detection and exit --> <!-- Running App detection and exit -->
<ns0:Property Id="FOUNDRUNNINGAPP" Admin="yes"/> <ns0:Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
<util:CloseApplication <util:CloseApplication
@@ -191,8 +181,6 @@
<!-- Skip action on uninstall --> <!-- Skip action on uninstall -->
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file --> <!-- 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="InstallFiles" 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> </ns0:InstallExecuteSequence>
<ns0:InstallUISequence> <ns0:InstallUISequence>
+1 -1
View File
@@ -41,7 +41,7 @@ Media Type of the encrypted data files. Default is "application/vnd.cryptomator.
Close Application settings: Close Application settings:
- CloseApplicationTarget - CloseApplicationTarget
Full name of executable to be checked in the close application util. Default is "cryptomator.exe" Full name of executable to be checkd in the close application util. Default is "cryptomator.exe"
Legacy Installation settings: Legacy Installation settings:
- SkipCryptomatorLegacyCheck - SkipCryptomatorLegacyCheck
+3 -3
View File
@@ -1,6 +1,6 @@
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 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|MIT|The MIT License (MIT)|The MIT License|MIT license 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|GNU Lesser General Public License LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
GPLv2|GNU General Public License Version 2 GPLv2|GNU General Public License Version 2
GPLv2+CE|CDDL + GPLv2 with classpath exception GPLv2+CE|CDDL + GPLv2 with classpath exception
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0 Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
+35 -38
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId> <groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId> <artifactId>cryptomator</artifactId>
<version>1.19.0</version> <version>1.18.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name> <name>Cryptomator Desktop App</name>
<organization> <organization>
@@ -26,68 +26,65 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>25</project.jdk.version> <project.jdk.version>24</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now --> <!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) --> <!-- 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> <nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.10.0-beta2</cryptomator.cryptofs.version> <cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version> <cryptomator.integrations.version>1.6.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version> <cryptomator.integrations.win.version>1.5.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.4.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta3</cryptomator.integrations.linux.version> <cryptomator.integrations.linux.version>1.6.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version> <cryptomator.fuse.version>5.0.5</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version> <cryptomator.webdav.version>2.0.10</cryptomator.webdav.version>
<!-- 3rd party dependencies --> <!-- 3rd party dependencies -->
<commons-lang3.version>3.19.0</commons-lang3.version> <commons-lang3.version>3.17.0</commons-lang3.version>
<dagger.version>2.57.2</dagger.version> <dagger.version>2.56.2</dagger.version>
<easybind.version>2.2</easybind.version> <easybind.version>2.2</easybind.version>
<jackson.version>2.20.0</jackson.version> <jackson.version>2.19.1</jackson.version>
<javafx.version>25</javafx.version> <javafx.version>24.0.1</javafx.version>
<jwt.version>4.5.0</jwt.version> <jwt.version>4.5.0</jwt.version>
<nimbus-jose.version>10.5</nimbus-jose.version> <nimbus-jose.version>9.37.3</nimbus-jose.version>
<logback.version>1.5.19</logback.version> <logback.version>1.5.18</logback.version>
<slf4j.version>2.0.17</slf4j.version> <slf4j.version>2.0.17</slf4j.version>
<tinyoauth2.version>0.8.1</tinyoauth2.version> <tinyoauth2.version>0.8.1</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version> <zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies --> <!-- test dependencies -->
<junit.jupiter.version>5.13.4</junit.jupiter.version> <junit.jupiter.version>5.13.1</junit.jupiter.version>
<mockito.version>5.20.0</mockito.version> <mockito.version>5.18.0</mockito.version>
<hamcrest.version>3.0</hamcrest.version> <hamcrest.version>3.0</hamcrest.version>
<!-- build-time dependencies --> <!-- build-time dependencies -->
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version> <jetbrains.annotations.version>26.0.2</jetbrains.annotations.version>
<dependency-check.version>12.1.5</dependency-check.version> <dependency-check.version>12.1.3</dependency-check.version>
<jacoco.version>0.8.14</jacoco.version> <jacoco.version>0.8.13</jacoco.version>
<license-generator.version>2.7.0</license-generator.version> <license-generator.version>2.5.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version> <junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
<mvn-compiler.version>3.14.1</mvn-compiler.version> <mvn-compiler.version>3.14.0</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version> <mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.8.1</mvn-dependency.version> <mvn-dependency.version>3.8.1</mvn-dependency.version>
<mvn-surefire.version>3.5.4</mvn-surefire.version> <mvn-surefire.version>3.5.3</mvn-surefire.version>
<mvn-jar.version>3.4.2</mvn-jar.version> <mvn-jar.version>3.4.2</mvn-jar.version>
<!-- Property used by surefire to determine jacoco engine --> <!-- Property used by surefire to determine jacoco engine -->
<surefire.jacoco.args></surefire.jacoco.args> <surefire.jacoco.args></surefire.jacoco.args>
</properties> </properties>
<repositories> <!-- TODO: Remove once webdav version 2.0.11 is released -->
<repository> <dependencyManagement>
<name>Central Portal Snapshots</name> <dependencies>
<id>central-portal-snapshots</id> <dependency>
<url>https://central.sonatype.com/repository/maven-snapshots/</url> <groupId>org.cryptomator</groupId>
<releases> <artifactId>webdav-nio-adapter-servlet</artifactId>
<enabled>false</enabled> <version>1.2.9</version>
</releases> </dependency>
<snapshots> </dependencies>
<enabled>true</enabled> </dependencyManagement>
</snapshots>
</repository>
</repositories>
<dependencies> <dependencies>
<!-- Cryptomator Libs --> <!-- Cryptomator Libs -->
@@ -228,7 +225,7 @@
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
<version>3.2.2</version> <version>3.2.1</version>
</dependency> </dependency>
<!-- JUnit / Mockito / Hamcrest --> <!-- JUnit / Mockito / Hamcrest -->
<dependency> <dependency>
@@ -258,7 +255,7 @@
<dependency> <dependency>
<groupId>com.google.jimfs</groupId> <groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId> <artifactId>jimfs</artifactId>
<version>1.3.1</version> <version>1.3.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
+5 -5
View File
@@ -1,4 +1,5 @@
import ch.qos.logback.classic.spi.Configurator; import ch.qos.logback.classic.spi.Configurator;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
@@ -13,12 +14,11 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider; import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider; 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.tray.TrayMenuController;
import org.cryptomator.logging.LogbackConfiguratorFactory; 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.traymenu.AwtTrayMenuController; import org.cryptomator.ui.traymenu.AwtTrayMenuController;
open module org.cryptomator.desktop { open module org.cryptomator.desktop {
@@ -50,12 +50,12 @@ open module org.cryptomator.desktop {
requires io.github.coffeelibs.tinyoauth2client; requires io.github.coffeelibs.tinyoauth2client;
requires org.slf4j; requires org.slf4j;
requires org.apache.commons.lang3; requires org.apache.commons.lang3;
requires com.github.benmanes.caffeine;
/* dagger bs */ /* dagger bs */
requires jakarta.inject; requires jakarta.inject;
requires static javax.inject; requires static javax.inject;
requires java.compiler; requires java.compiler;
requires com.github.benmanes.caffeine;
uses org.cryptomator.common.locationpresets.LocationPresetsProvider; uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
uses SSLContextProvider; uses SSLContextProvider;
@@ -74,6 +74,13 @@ public abstract class CommonsModule {
return new MasterkeyFileAccess(Constants.PEPPER, csprng); return new MasterkeyFileAccess(Constants.PEPPER, csprng);
} }
@Provides
@Singleton
@Named("SemVer")
static Comparator<String> providesSemVerComparator() {
return new SemVerComparator();
}
@Provides @Provides
@Singleton @Singleton
static Optional<RevealPathService> provideRevealPathService() { static Optional<RevealPathService> provideRevealPathService() {
@@ -13,7 +13,5 @@ public interface Constants {
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator"; String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
byte[] PEPPER = new byte[0]; 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,15 +124,6 @@ public class Environment {
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME)); 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() { public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME); return getPath(PLUGIN_DIR_PROP_NAME);
} }
@@ -0,0 +1,160 @@
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());
}
}
@@ -1,18 +0,0 @@
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();
}
@@ -0,0 +1,81 @@
/*******************************************************************************
* 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,7 +167,6 @@ public class Mounter {
usedMountServices.add(mountService); usedMountServices.add(mountService);
var builder = mountService.forFileSystem(cryptoFsRoot); 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 internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
var cleanup = internal.prepare(); var cleanup = internal.prepare();
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup); return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
@@ -1,53 +0,0 @@
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);
}
}
}
@@ -1,33 +0,0 @@
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);
}
}
@@ -1,101 +0,0 @@
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")) //
.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();
}
}
@@ -1,10 +0,0 @@
package org.cryptomator.common.recovery;
public enum RecoveryActionType {
RESTORE_ALL,
RESTORE_MASTERKEY,
RESTORE_VAULT_CONFIG,
RESET_PASSWORD,
SHOW_KEY,
CONVERT_VAULT
}
@@ -1,56 +0,0 @@
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;
}
}
@@ -1,54 +0,0 @@
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,8 +25,8 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation; import javafx.geometry.NodeOrientation;
import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.util.function.Consumer;
public class Settings { public class Settings {
@@ -51,7 +51,6 @@ public class Settings {
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name(); 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"); public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
private final SettingsProvider provider;
public final ObservableList<VaultSettings> directories; public final ObservableList<VaultSettings> directories;
public final BooleanProperty startHidden; public final BooleanProperty startHidden;
public final BooleanProperty autoCloseVaults; public final BooleanProperty autoCloseVaults;
@@ -76,13 +75,13 @@ public class Settings {
public final BooleanProperty checkForUpdates; public final BooleanProperty checkForUpdates;
public final ObjectProperty<Instant> lastUpdateCheckReminder; public final ObjectProperty<Instant> lastUpdateCheckReminder;
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck; public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
public final StringProperty lastUpdateAttemptedByVersion;
public static Settings create(SettingsProvider provider, Environment env) { private Consumer<Settings> saveCmd;
public static Settings create(Environment env) {
var defaults = new SettingsJson(); var defaults = new SettingsJson();
defaults.showTrayIcon = env.showTrayIcon(); defaults.showTrayIcon = env.showTrayIcon();
return new Settings(provider, defaults); return new Settings(defaults);
} }
/** /**
@@ -90,8 +89,7 @@ public class Settings {
* *
* @param json The parsed settings.json * @param json The parsed settings.json
*/ */
Settings(SettingsProvider provider, SettingsJson json) { Settings(SettingsJson json) {
this.provider = provider;
this.directories = FXCollections.observableArrayList(VaultSettings::observables); this.directories = FXCollections.observableArrayList(VaultSettings::observables);
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden); this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults); this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
@@ -116,8 +114,6 @@ public class Settings {
this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled); this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck); this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck);
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck); 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()); this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -147,8 +143,6 @@ public class Settings {
checkForUpdates.addListener(this::somethingChanged); checkForUpdates.addListener(this::somethingChanged);
lastUpdateCheckReminder.addListener(this::somethingChanged); lastUpdateCheckReminder.addListener(this::somethingChanged);
lastSuccessfulUpdateCheck.addListener(this::somethingChanged); lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@@ -210,8 +204,6 @@ public class Settings {
json.checkForUpdatesEnabled = checkForUpdates.get(); json.checkForUpdatesEnabled = checkForUpdates.get();
json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get(); json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get();
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get(); json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
return json; return json;
} }
@@ -224,12 +216,20 @@ public class Settings {
} }
} }
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
provider.scheduleSave(this); // TODO rename to setChangeListener
void setSaveCmd(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
} }
public void saveNow() { private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
provider.saveNow(this); this.save();
}
void save() {
if (saveCmd != null) {
saveCmd.accept(this);
}
} }
} }
@@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
@@ -93,10 +92,4 @@ class SettingsJson {
@JsonProperty("quickAccessService") @JsonProperty("quickAccessService")
String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE; String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE;
@JsonProperty("previouslyUsedVaultDirectory")
Path previouslyUsedVaultDirectory;
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
} }
@@ -26,9 +26,7 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.concurrent.CompletableFuture; import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -63,7 +61,8 @@ public class SettingsProvider implements Supplier<Settings> {
Settings settings = env.getSettingsPath() // Settings settings = env.getSettingsPath() //
.flatMap(this::tryLoad) // .flatMap(this::tryLoad) //
.findFirst() // .findFirst() //
.orElseGet(() -> Settings.create(this, env)); .orElseGet(() -> Settings.create(env));
settings.setSaveCmd(this::scheduleSave);
return settings; return settings;
} }
@@ -72,7 +71,7 @@ public class SettingsProvider implements Supplier<Settings> {
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) { try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
var json = JSON.reader().readValue(in, SettingsJson.class); var json = JSON.reader().readValue(in, SettingsJson.class);
LOG.info("Settings loaded from {}", path); LOG.info("Settings loaded from {}", path);
var settings = new Settings(this, json); var settings = new Settings(json);
return Stream.of(settings); return Stream.of(settings);
} catch (JacksonException e) { } catch (JacksonException e) {
LOG.warn("Failed to parse json file {}", path, e); LOG.warn("Failed to parse json file {}", path, e);
@@ -85,33 +84,19 @@ public class SettingsProvider implements Supplier<Settings> {
} }
} }
void saveNow(Settings settings) { private void scheduleSave(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);
}
}
void scheduleSave(Settings settings) {
scheduleSave(settings, SAVE_DELAY_MS);
}
private Future<?> scheduleSave(Settings settings, long delayMillis) {
if (settings == null) { if (settings == null) {
return CompletableFuture.completedFuture(null); return;
} }
final Path settingsPath = env.getSettingsPath().findFirst().orElseThrow(); // always save to preferred (first) path final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
Runnable saveCommand = () -> this.save(settings, settingsPath); settingsPath.ifPresent(path -> {
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, delayMillis, TimeUnit.MILLISECONDS); Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask); ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
if (previouslyScheduledTask != null) { ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
previouslyScheduledTask.cancel(false); if (previouslyScheduledTask != null) {
} previouslyScheduledTask.cancel(false);
return scheduledTask; }
});
} }
private void save(Settings settings, Path settingsPath) { private void save(Settings settings, Path settingsPath) {
@@ -122,7 +107,7 @@ public class SettingsProvider implements Supplier<Settings> {
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp"); Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
var jsonObj = settings.serialized(); var jsonObj = settings.serialized();
jsonObj.writtenByVersion = env.getAppVersionWithBuildNumber(); jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj); JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
} }
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING); Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
@@ -10,7 +10,7 @@ package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants; import org.cryptomator.common.Constants;
import org.cryptomator.common.FilesystemOwnerSupplier; import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.common.mount.Mounter; import org.cryptomator.common.mount.Mounter;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VaultSettings;
@@ -23,8 +23,7 @@ import org.cryptomator.cryptofs.event.FilesystemEvent;
import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.event.VaultEvent;
import org.cryptomator.event.NotificationManager;
import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.integrations.mount.MountFailedException;
import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException; import org.cryptomator.integrations.mount.UnmountFailedException;
@@ -36,6 +35,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.BooleanBinding;
@@ -75,12 +75,10 @@ public class Vault {
private final BooleanBinding missing; private final BooleanBinding missing;
private final BooleanBinding needsMigration; private final BooleanBinding needsMigration;
private final BooleanBinding unknownError; private final BooleanBinding unknownError;
private final BooleanBinding missingVaultConfig;
private final ObjectBinding<Mountpoint> mountPoint; private final ObjectBinding<Mountpoint> mountPoint;
private final Mounter mounter; private final Mounter mounter;
private final Settings settings; private final Settings settings;
private final FileSystemEventAggregator fileSystemEventAggregator; private final FileSystemEventAggregator fileSystemEventAggregator;
private final NotificationManager notificationManager;
private final BooleanProperty showingStats; private final BooleanProperty showingStats;
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null); private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
@@ -93,8 +91,7 @@ public class Vault {
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, // @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
VaultStats stats, // VaultStats stats, //
Mounter mounter, Settings settings, // Mounter mounter, Settings settings, //
FileSystemEventAggregator fileSystemEventAggregator, // FileSystemEventAggregator fileSystemEventAggregator) {
NotificationManager notificationManager) {
this.vaultSettings = vaultSettings; this.vaultSettings = vaultSettings;
this.configCache = configCache; this.configCache = configCache;
this.cryptoFileSystem = cryptoFileSystem; this.cryptoFileSystem = cryptoFileSystem;
@@ -106,14 +103,12 @@ public class Vault {
this.processing = Bindings.createBooleanBinding(this::isProcessing, state); this.processing = Bindings.createBooleanBinding(this::isProcessing, state);
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state); this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
this.missing = Bindings.createBooleanBinding(this::isMissing, state); this.missing = Bindings.createBooleanBinding(this::isMissing, state);
this.missingVaultConfig = Bindings.createBooleanBinding(this::isMissingVaultConfig, state);
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state); this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state); this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state); this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
this.mounter = mounter; this.mounter = mounter;
this.settings = settings; this.settings = settings;
this.fileSystemEventAggregator = fileSystemEventAggregator; this.fileSystemEventAggregator = fileSystemEventAggregator;
this.notificationManager = notificationManager;
this.showingStats = new SimpleBooleanProperty(false); this.showingStats = new SimpleBooleanProperty(false);
this.quickAccessEntry = new AtomicReference<>(null); this.quickAccessEntry = new AtomicReference<>(null);
} }
@@ -150,17 +145,14 @@ public class Vault {
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get()); LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
} }
var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() // CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withKeyLoader(keyLoader) // .withKeyLoader(keyLoader) //
.withFlags(flags) // .withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) // .withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.withFilesystemEventConsumer(this::consumeVaultEvent); .withFilesystemEventConsumer(this::consumeVaultEvent) //
if (keyLoader instanceof FilesystemOwnerSupplier oo) { .build();
fsPropsBuilder.withOwnerGetter(oo::getOwner); return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build());
} }
private void destroyCryptoFileSystem() { private void destroyCryptoFileSystem() {
@@ -270,7 +262,6 @@ public class Vault {
private void consumeVaultEvent(FilesystemEvent e) { private void consumeVaultEvent(FilesystemEvent e) {
fileSystemEventAggregator.put(this, e); fileSystemEventAggregator.put(this, e);
notificationManager.offer(this, e);
} }
// ****************************************************************************** // ******************************************************************************
@@ -345,14 +336,6 @@ public class Vault {
return state.get() == VaultState.Value.ERROR; 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() { public ReadOnlyStringProperty displayNameProperty() {
return vaultSettings.displayName; return vaultSettings.displayName;
} }
@@ -20,7 +20,7 @@ public class VaultConfigCache {
private final VaultSettings settings; private final VaultSettings settings;
private final AtomicReference<VaultConfig.UnverifiedVaultConfig> config; private final AtomicReference<VaultConfig.UnverifiedVaultConfig> config;
public VaultConfigCache(VaultSettings settings) { VaultConfigCache(VaultSettings settings) {
this.settings = settings; this.settings = settings;
this.config = new AtomicReference<>(null); this.config = new AtomicReference<>(null);
} }
@@ -9,14 +9,13 @@
package org.cryptomator.common.vaults; package org.cryptomator.common.vaults;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.recovery.BackupRestorer; import org.cryptomator.common.Constants;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure; import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.Migrators;
import org.cryptomator.integrations.mount.MountService; import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -35,7 +34,9 @@ import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
import static org.cryptomator.common.vaults.VaultState.Value.*; 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;
@Singleton @Singleton
public class VaultListManager { public class VaultListManager {
@@ -66,12 +67,6 @@ public class VaultListManager {
autoLocker.init(); 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 { public Vault add(Path pathToVault) throws IOException {
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath(); Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) { if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
@@ -119,122 +114,59 @@ public class VaultListManager {
.findAny(); .findAny();
} }
public void addVault(Vault vault) {
Path path = vault.getPath().normalize().toAbsolutePath();
if (!isAlreadyAdded(path)) {
vaultList.add(vault);
}
}
private Vault create(VaultSettings vaultSettings) { private Vault create(VaultSettings vaultSettings) {
var wrapper = new VaultConfigCache(vaultSettings); var wrapper = new VaultConfigCache(vaultSettings);
try { try {
var vaultState = determineVaultState(vaultSettings.path.get()); var vaultState = determineVaultState(vaultSettings.path.get());
initializeLastKnownKeyLoaderIfPossible(vaultSettings, vaultState, wrapper); 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(Constants.DEFAULT_KEY_ID.toString());
}
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault(); return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) { } catch (IOException e) {
LOG.warn("Failed to determine vault state for {}", vaultSettings.path.get(), e); LOG.warn("Failed to determine vault state for " + vaultSettings.path.get(), e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault(); return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
} }
} }
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) { public static VaultState.Value redetermineVaultState(Vault vault) {
VaultState state = vault.stateProperty(); VaultState state = vault.stateProperty();
VaultState.Value previous = state.getValue(); VaultState.Value previousState = state.getValue();
return switch (previousState) {
if (previous.equals(UNLOCKED) || previous.equals(PROCESSING)) { case LOCKED, NEEDS_MIGRATION, MISSING -> {
return previous; try {
} var determinedState = determineVaultState(vault.getPath());
if (determinedState == LOCKED) {
try { vault.getVaultConfigCache().reloadConfig();
VaultState.Value determined = determineVaultState(vault.getPath()); }
state.set(determinedState);
if (determined == LOCKED) { yield determinedState;
vault.getVaultConfigCache().reloadConfig(); } catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
state.set(ERROR);
vault.setLastKnownException(e);
yield ERROR;
}
} }
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;
}
} }
public static VaultState.Value determineVaultState(Path pathToVault) throws IOException { private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
if (!Files.exists(pathToVault)) { if (!Files.exists(pathToVault)) {
return MISSING; return VaultState.Value.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)) { return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) {
case VAULT -> LOCKED; case VAULT -> VaultState.Value.LOCKED;
case UNRELATED -> MISSING; case UNRELATED -> VaultState.Value.MISSING;
case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? NEEDS_MIGRATION : MISSING; case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? //
VaultState.Value.NEEDS_MIGRATION //
: VaultState.Value.MISSING;
}; };
} }
@@ -25,16 +25,6 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
*/ */
MISSING, 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 * Vault requires migration to a newer vault format
*/ */
@@ -6,7 +6,6 @@ import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent; import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent; import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptofs.event.FilesystemEvent;
import javax.inject.Inject; import javax.inject.Inject;
@@ -102,7 +101,6 @@ public class FileSystemEventAggregator {
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext; case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext; case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext; case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext;
}; };
return new FSEventBucket(v, p, event.getClass()); return new FSEventBucket(v, p, event.getClass());
} }
@@ -1,85 +0,0 @@
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,6 +3,25 @@ package org.cryptomator.event;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptofs.event.FilesystemEvent;
public record VaultEvent(Vault v, FilesystemEvent actualEvent) { import java.time.Instant;
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);
}
} }
@@ -53,26 +53,18 @@ class Server implements IpcCommunicator {
@Override @Override
public void listen(IpcMessageListener listener, Executor executor) { public void listen(IpcMessageListener listener, Executor executor) {
executor.execute(() -> { executor.execute(() -> {
int errorCount = 0;
while (serverSocketChannel.isOpen()) { while (serverSocketChannel.isOpen()) {
try (var ch = serverSocketChannel.accept()) { try (var ch = serverSocketChannel.accept()) {
while (ch.isConnected()) { while (ch.isConnected()) {
var msg = IpcMessage.receive(ch); var msg = IpcMessage.receive(ch);
listener.handleMessage(msg); listener.handleMessage(msg);
} }
errorCount = 0;
} catch (AsynchronousCloseException e) { } catch (AsynchronousCloseException e) {
LOG.info("Closing server socket due to closed channel.");
return; // serverSocketChannel closed or listener interrupted return; // serverSocketChannel closed or listener interrupted
} catch (EOFException | ClosedChannelException e) { } catch (EOFException | ClosedChannelException e) {
// continue with next connected client // continue with next connected client
} catch (IOException e) { } catch (IOException e) {
errorCount++;
LOG.error("Failed to read IPC message", e); LOG.error("Failed to read IPC message", e);
if(errorCount > 100) { //apparently something is broken, prevent log spam
LOG.info("Closing server socket due to too many failed requests.");
return;
}
} }
} }
}); });
@@ -26,7 +26,7 @@ public class CreateNewVaultExpertSettingsController implements FxController {
public static final int MAX_SHORTENING_THRESHOLD = 220; public static final int MAX_SHORTENING_THRESHOLD = 220;
public static final int MIN_SHORTENING_THRESHOLD = 36; public static final int MIN_SHORTENING_THRESHOLD = 36;
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/vault/#name-shortening"; private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/architecture/#name-shortening";
private final Stage window; private final Stage window;
private final Lazy<Application> application; private final Lazy<Application> application;
@@ -4,7 +4,6 @@ import dagger.Lazy;
import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.locationpresets.LocationPreset; import org.cryptomator.common.locationpresets.LocationPreset;
import org.cryptomator.common.locationpresets.LocationPresetsProvider; import org.cryptomator.common.locationpresets.LocationPresetsProvider;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
@@ -39,7 +38,6 @@ import javafx.stage.WindowEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
@@ -66,7 +64,6 @@ public class CreateNewVaultLocationController implements FxController {
private final BooleanProperty loadingPresetLocations = new SimpleBooleanProperty(false); private final BooleanProperty loadingPresetLocations = new SimpleBooleanProperty(false);
private final ObservableList<Node> radioButtons; private final ObservableList<Node> radioButtons;
private final ObservableList<Node> sortedRadioButtons; private final ObservableList<Node> sortedRadioButtons;
private final Settings settings;
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH; private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
@@ -85,7 +82,6 @@ public class CreateNewVaultLocationController implements FxController {
@FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, // @FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy<Scene> chooseExpertSettingsScene, //
ObjectProperty<Path> vaultPath, // ObjectProperty<Path> vaultPath, //
@Named("vaultName") StringProperty vaultName, // @Named("vaultName") StringProperty vaultName, //
Settings settings, //
ExecutorService backgroundExecutor, ResourceBundle resourceBundle) { ExecutorService backgroundExecutor, ResourceBundle resourceBundle) {
this.window = window; this.window = window;
this.chooseNameScene = chooseNameScene; this.chooseNameScene = chooseNameScene;
@@ -100,18 +96,6 @@ public class CreateNewVaultLocationController implements FxController {
this.usePresetPath = new SimpleBooleanProperty(); this.usePresetPath = new SimpleBooleanProperty();
this.radioButtons = FXCollections.observableArrayList(); this.radioButtons = FXCollections.observableArrayList();
this.sortedRadioButtons = radioButtons.sorted(this::compareLocationPresets); this.sortedRadioButtons = radioButtons.sorted(this::compareLocationPresets);
this.settings = settings;
Path previouslyUsedDirectory = settings.previouslyUsedVaultDirectory.get();
if (previouslyUsedDirectory != null) {
try {
if (Files.exists(previouslyUsedDirectory) && Files.isDirectory(previouslyUsedDirectory) && isActuallyWritable(previouslyUsedDirectory)) {
this.customVaultPath = previouslyUsedDirectory;
}
} catch (InvalidPathException | NullPointerException e) {
LOG.warn("Invalid previously used vault directory path: {}", previouslyUsedDirectory, e);
}
}
} }
private VaultPathStatus validatePath(Path p) throws NullPointerException { private VaultPathStatus validatePath(Path p) throws NullPointerException {
@@ -212,12 +196,6 @@ public class CreateNewVaultLocationController implements FxController {
@FXML @FXML
public void next() { public void next() {
if (validVaultPath.getValue()) { if (validVaultPath.getValue()) {
if (this.getVaultPath() != null) {
Path parentPath = this.getVaultPath().getParent();
if (parentPath != null) {
this.settings.previouslyUsedVaultDirectory.setValue(parentPath);
}
}
window.setScene(chooseExpertSettingsScene.get()); window.setScene(chooseExpertSettingsScene.get());
} }
} }
@@ -20,6 +20,7 @@ import java.util.ResourceBundle;
public class PasswordStrengthUtil { public class PasswordStrengthUtil {
private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length private 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 static final List<String> SANITIZED_INPUTS = List.of("cryptomator");
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
@@ -47,15 +48,13 @@ public class PasswordStrengthUtil {
} }
public String getStrengthDescription(Number score) { public String getStrengthDescription(Number score) {
return switch (score.intValue()) { if (score.intValue() == -1) {
case -1 -> String.format(resourceBundle.getString("passwordStrength.messageLabel.tooShort"), minPwLength); return String.format(resourceBundle.getString(RESSOURCE_PREFIX + "tooShort"), minPwLength);
case 0 -> resourceBundle.getString("passwordStrength.messageLabel.0"); } else if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
case 1 -> resourceBundle.getString("passwordStrength.messageLabel.1"); return resourceBundle.getString(RESSOURCE_PREFIX + score.intValue());
case 2 -> resourceBundle.getString("passwordStrength.messageLabel.2"); } else {
case 3 -> resourceBundle.getString("passwordStrength.messageLabel.3"); return "";
case 4 -> resourceBundle.getString("passwordStrength.messageLabel.4"); }
default -> "";
};
} }
} }
@@ -38,15 +38,13 @@ public enum FxmlFile {
MIGRATION_RUN("/fxml/migration_run.fxml"), // MIGRATION_RUN("/fxml/migration_run.fxml"), //
MIGRATION_START("/fxml/migration_start.fxml"), // MIGRATION_START("/fxml/migration_start.fxml"), //
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), // MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
NOTIFICATION("/fxml/notification.fxml"), //
PREFERENCES("/fxml/preferences.fxml"), // PREFERENCES("/fxml/preferences.fxml"), //
QUIT("/fxml/quit.fxml"), // QUIT("/fxml/quit.fxml"), //
QUIT_FORCED("/fxml/quit_forced.fxml"), // QUIT_FORCED("/fxml/quit_forced.fxml"), //
RECOVERYKEY_CREATE("/fxml/recoverykey_create.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_RECOVER("/fxml/recoverykey_recover.fxml"), //
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.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"), // RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
SHARE_VAULT("/fxml/share_vault.fxml"), // SHARE_VAULT("/fxml/share_vault.fxml"), //
SIMPLE_DIALOG("/fxml/simple_dialog.fxml"), // SIMPLE_DIALOG("/fxml/simple_dialog.fxml"), //
@@ -60,7 +58,6 @@ public enum FxmlFile {
VAULT_STATISTICS("/fxml/stats.fxml"), // VAULT_STATISTICS("/fxml/stats.fxml"), //
WRONGFILEALERT("/fxml/wrongfilealert.fxml"); WRONGFILEALERT("/fxml/wrongfilealert.fxml");
private final String ressourcePathString; private final String ressourcePathString;
FxmlFile(String ressourcePathString) { FxmlFile(String ressourcePathString) {
@@ -1,56 +0,0 @@
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,8 +12,6 @@ public enum FontAwesome5Icon {
CARET_DOWN("\uF0D7"), // CARET_DOWN("\uF0D7"), //
CARET_RIGHT("\uF0Da"), // CARET_RIGHT("\uF0Da"), //
CHECK("\uF00C"), // CHECK("\uF00C"), //
CHEVRON_LEFT("\uF053"), //
CHEVRON_RIGHT("\uF054"), //
CLOCK("\uF017"), // CLOCK("\uF017"), //
CLIPBOARD("\uF328"), // CLIPBOARD("\uF328"), //
COG("\uF013"), // COG("\uF013"), //
@@ -62,7 +60,6 @@ public enum FontAwesome5Icon {
TRASH("\uF1F8"), // TRASH("\uF1F8"), //
UNLINK("\uf127"), // UNLINK("\uf127"), //
USER_COG("\uf4fe"), // USER_COG("\uf4fe"), //
USER_LOCK("\uf502"), //
WRENCH("\uF0AD"), // WRENCH("\uF0AD"), //
WINDOW_MINIMIZE("\uF2D1"), // WINDOW_MINIMIZE("\uF2D1"), //
; ;
@@ -4,10 +4,8 @@ import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.changepassword.NewPasswordController; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.changepassword.PasswordStrengthUtil; import org.cryptomator.ui.changepassword.PasswordStrengthUtil;
import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.DefaultSceneFactory;
@@ -22,7 +20,6 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyValidateController;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.scene.Scene; import javafx.scene.Scene;
@@ -122,8 +119,8 @@ abstract class ConvertVaultModule {
@Provides @Provides
@IntoMap @IntoMap
@FxControllerKey(RecoveryKeyValidateController.class) @FxControllerKey(RecoveryKeyValidateController.class)
static FxController provideRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, MasterkeyFileAccess masterkeyFileAccess) { static FxController bindRecoveryKeyValidateController(@ConvertVaultWindow Vault vault, @ConvertVaultWindow VaultConfig.UnverifiedVaultConfig vaultConfig, @ConvertVaultWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, masterkeyFileAccess, new SimpleObjectProperty<>(RecoveryActionType.CONVERT_VAULT), new SimpleObjectProperty<>(null)); return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
} }
} }
@@ -194,7 +194,7 @@ public class DecryptFileNamesViewController implements FxController {
} }
} }
//observable getter //obvservable getter
public ObservableValue<String> dropZoneTextProperty() { public ObservableValue<String> dropZoneTextProperty() {
return dropZoneText; return dropZoneText;
@@ -2,7 +2,6 @@ package org.cryptomator.ui.dialogs;
import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.cryptomator.ui.fxapp.FxApplicationScoped;
@@ -20,21 +19,17 @@ public class Dialogs {
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final StageFactory stageFactory; private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private static final String BUTTON_KEY_CLOSE = "generic.button.close";
@Inject @Inject
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) { public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory; this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
} }
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class); private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
private SimpleDialog.Builder createDialogBuilder() { private SimpleDialog.Builder createDialogBuilder() {
return new SimpleDialog.Builder(resourceBundle, stageFactory, sceneFactory); return new SimpleDialog.Builder(resourceBundle, stageFactory);
} }
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) { public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
@@ -52,43 +47,6 @@ 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 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) { public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) {
return createDialogBuilder() // return createDialogBuilder() //
.setOwner(window) // .setOwner(window) //
@@ -111,7 +69,7 @@ public class Dialogs {
.setMessageKey("dokanySupportEnd.message") // .setMessageKey("dokanySupportEnd.message") //
.setDescriptionKey("dokanySupportEnd.description") // .setDescriptionKey("dokanySupportEnd.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) // .setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey(BUTTON_KEY_CLOSE) // .setOkButtonKey("generic.button.close") //
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") // .setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
.setOkAction(Stage::close) // .setOkAction(Stage::close) //
.setCancelAction(cancelAction); .setCancelAction(cancelAction);
@@ -125,20 +83,8 @@ public class Dialogs {
.setDescriptionKey("retryIfReadonly.description") // .setDescriptionKey("retryIfReadonly.description") //
.setIcon(FontAwesome5Icon.EXCLAMATION) // .setIcon(FontAwesome5Icon.EXCLAMATION) //
.setOkButtonKey("retryIfReadonly.retry") // .setOkButtonKey("retryIfReadonly.retry") //
.setCancelButtonKey(BUTTON_KEY_CLOSE) // .setCancelButtonKey("generic.button.close") //
.setOkAction(okAction) // .setOkAction(okAction) //
.setCancelAction(Stage::close); .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,6 +1,5 @@
package org.cryptomator.ui.dialogs; package org.cryptomator.ui.dialogs;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory; import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
@@ -31,15 +30,15 @@ public class SimpleDialog {
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( // FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( //
new SimpleDialogController(resolveText(builder.messageKey, null), // new SimpleDialogController(resolveText(builder.messageKey, null), //
resolveText(builder.descriptionKey, builder.descriptionArgs), // resolveText(builder.descriptionKey, null), //
builder.icon, // builder.icon, //
resolveText(builder.okButtonKey, null), // resolveText(builder.okButtonKey, null), //
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, // builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
() -> builder.okAction.accept(dialogStage), // () -> builder.okAction.accept(dialogStage), //
() -> builder.cancelAction.accept(dialogStage)), // () -> builder.cancelAction.accept(dialogStage)), //
builder.sceneFactory, builder.resourceBundle); Scene::new, builder.resourceBundle);
dialogStage.setScene(loaderFactory.createScene(FxmlFile.SIMPLE_DIALOG)); dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
} }
public void showAndWait() { public void showAndWait() {
@@ -63,22 +62,19 @@ public class SimpleDialog {
private Stage owner; private Stage owner;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final StageFactory stageFactory; private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private String titleKey; private String titleKey;
private String[] titleArgs; private String[] titleArgs;
private String messageKey; private String messageKey;
private String descriptionKey; private String descriptionKey;
private String[] descriptionArgs;
private String okButtonKey; private String okButtonKey;
private String cancelButtonKey; private String cancelButtonKey;
private FontAwesome5Icon icon; private FontAwesome5Icon icon;
private Consumer<Stage> okAction = Stage::close; private Consumer<Stage> okAction = Stage::close;
private Consumer<Stage> cancelAction = Stage::close; private Consumer<Stage> cancelAction = Stage::close;
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) { public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory; this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
} }
public Builder setOwner(Stage owner) { public Builder setOwner(Stage owner) {
@@ -97,9 +93,8 @@ public class SimpleDialog {
return this; return this;
} }
public Builder setDescriptionKey(String descriptionKey, String... args) { public Builder setDescriptionKey(String descriptionKey) {
this.descriptionKey = descriptionKey; this.descriptionKey = descriptionKey;
this.descriptionArgs = args;
return this; return this;
} }
@@ -1,8 +1,5 @@
package org.cryptomator.ui.eventview; 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.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent; import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.event.FileSystemEventAggregator;
@@ -118,7 +115,7 @@ public class EventListCellController implements FxController {
eventActionsMenu.hide(); eventActionsMenu.hide();
eventActionsMenu.getItems().clear(); eventActionsMenu.getItems().clear();
eventTooltip.setText(item.getKey().vault().getDisplayName()); eventTooltip.setText(item.getKey().vault().getDisplayName());
addLocalizedAction("generic.action.dismiss", () -> { addAction("generic.action.dismiss", () -> {
fileSystemEventAggregator.remove(item.getKey()); fileSystemEventAggregator.remove(item.getKey());
}); });
switch (item.getValue().mostRecentEvent()) { switch (item.getValue().mostRecentEvent()) {
@@ -127,41 +124,20 @@ public class EventListCellController implements FxController {
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse); case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse); case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(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) { private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
eventIcon.setValue(FontAwesome5Icon.TIMES); eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message")); eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString()); eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
if (revealService != null) { if (revealService != null) {
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath())); addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
} else { } else {
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString())); addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
} }
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString())); addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
} }
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) { private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
@@ -169,9 +145,9 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message")); eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString()); eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
if (revealService != null) { if (revealService != null) {
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else { } else {
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString())); addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
} }
} }
@@ -180,11 +156,11 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message")); eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString()); eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
if (revealService != null) { if (revealService != null) {
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
} else { } else {
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString())); addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString())); addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
} }
} }
@@ -193,9 +169,9 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message")); eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString()); eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
if (revealService != null) { if (revealService != null) {
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else { } else {
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString())); addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
} }
} }
@@ -204,19 +180,14 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message")); eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString()); eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
if (revealService != null) { if (revealService != null) {
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else { } else {
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString())); addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
} }
} }
private void addLocalizedAction(String localizationKey, Runnable action) { private void addAction(String localizationKey, Runnable action) {
var entryText = resourceBundle.getString(localizationKey); var entry = new MenuItem(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.getStyleClass().addLast("dropdown-button-context-menu-item");
entry.setOnAction(_ -> action.run()); entry.setOnAction(_ -> action.run());
eventActionsMenu.getItems().addLast(entry); eventActionsMenu.getItems().addLast(entry);
@@ -263,17 +234,18 @@ public class EventListCellController implements FxController {
} }
} }
private Path convertVaultPathToSystemPath(String vaultInternalPath) { private Path convertVaultPathToSystemPath(Path p) {
if (!(p instanceof CryptoPath)) {
throw new IllegalArgumentException("Path " + p + " is not a vault path");
}
var v = eventEntry.getValue().getKey().vault(); var v = eventEntry.getValue().getKey().vault();
if (!v.isUnlocked()) { if (!v.isUnlocked()) {
return Path.of(System.getProperty("user.home")); return Path.of(System.getProperty("user.home"));
} }
var mountPoint = v.getMountPoint().uri().getPath(); var mountUri = v.getMountPoint().uri();
if(SystemUtils.IS_OS_WINDOWS) { var internalPath = p.toString().substring(1);
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors return Path.of(mountUri.getPath().concat(internalPath).substring(1));
}
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
} }
private void reveal(RevealPathService s, Path p) { private void reveal(RevealPathService s, Path p) {
@@ -49,7 +49,7 @@ public class EventViewController implements FxController {
} }
/** /**
* Comparison method for the lru cache. During comparison the map is accessed. * Comparison method for the lru cache. During comparsion the map is accessed.
* First the entries are compared by the event timestamp, then vaultId, then identifying path and lastly by class name. * First the entries are compared by the event timestamp, then vaultId, then identifying path and lastly by class name.
* *
* @param left an entry of a {@link FSEventBucket} and its content * @param left an entry of a {@link FSEventBucket} and its content
@@ -30,20 +30,9 @@ public class FxApplication {
private final FxApplicationTerminator applicationTerminator; private final FxApplicationTerminator applicationTerminator;
private final AutoUnlocker autoUnlocker; private final AutoUnlocker autoUnlocker;
private final FxFSEventList fxFSEventList; private final FxFSEventList fxFSEventList;
private final FxNotificationManager notificationManager;
@Inject @Inject
FxApplication(@Named("startupTime") long startupTime, // FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) {
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.startupTime = startupTime;
this.environment = environment; this.environment = environment;
this.settings = settings; this.settings = settings;
@@ -54,7 +43,6 @@ public class FxApplication {
this.applicationTerminator = applicationTerminator; this.applicationTerminator = applicationTerminator;
this.autoUnlocker = autoUnlocker; this.autoUnlocker = autoUnlocker;
this.fxFSEventList = fxFSEventList; this.fxFSEventList = fxFSEventList;
this.notificationManager = notificationManager;
} }
public void start() { public void start() {
@@ -100,7 +88,6 @@ public class FxApplication {
launchEventHandler.startHandlingLaunchEvents(); launchEventHandler.startHandlingLaunchEvents();
fxFSEventList.schedulePollForUpdates(); fxFSEventList.schedulePollForUpdates();
notificationManager.schedulePollForUpdates();
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
} }
@@ -13,21 +13,22 @@ import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.health.HealthCheckComponent; import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.cryptomator.ui.sharevault.ShareVaultComponent; import org.cryptomator.ui.sharevault.ShareVaultComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent; import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.updatereminder.UpdateReminderComponent; import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; 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 javafx.scene.image.Image;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@Module(subcomponents = {TrayMenuComponent.class, // @Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
DecryptNameComponent.class, // DecryptNameComponent.class, //
MainWindowComponent.class, // MainWindowComponent.class, //
PreferencesComponent.class, // PreferencesComponent.class, //
@@ -39,9 +40,7 @@ import java.io.InputStream;
HealthCheckComponent.class, // HealthCheckComponent.class, //
UpdateReminderComponent.class, // UpdateReminderComponent.class, //
ShareVaultComponent.class, // ShareVaultComponent.class, //
EventViewComponent.class, // EventViewComponent.class})
RecoveryKeyComponent.class, //
NotificationComponent.class })
abstract class FxApplicationModule { abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException { private static Image createImageFromResource(String resourceName) throws IOException {
@@ -80,10 +79,4 @@ abstract class FxApplicationModule {
return factory.create(); return factory.create();
} }
@Provides
@FxApplicationScoped
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
return factory.create();
}
} }
@@ -28,7 +28,7 @@ import static org.cryptomator.common.vaults.VaultState.Value.*;
@FxApplicationScoped @FxApplicationScoped
public class FxApplicationTerminator { public class FxApplicationTerminator {
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_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private static final Set<VaultState.Value> STATES_PREVENT_TERMINATION = EnumSet.of(PROCESSING); private static final Set<VaultState.Value> STATES_PREVENT_TERMINATION = EnumSet.of(PROCESSING);
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class); private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
@@ -110,7 +110,7 @@ public class FxApplicationTerminator {
} else if (settings.autoCloseVaults.get() && !preventQuitWithGracefulLock.get()) { } else if (settings.autoCloseVaults.get() && !preventQuitWithGracefulLock.get()) {
var lockAllTask = vaultService.createLockAllTask(vaults.filtered(Vault::isUnlocked), false); var lockAllTask = vaultService.createLockAllTask(vaults.filtered(Vault::isUnlocked), false);
lockAllTask.setOnSucceeded(event -> { lockAllTask.setOnSucceeded(event -> {
LOG.info("Locked remaining vaults was successful."); LOG.info("Locked remaining vaults was succesful.");
exitingResponse.performQuit(); exitingResponse.performQuit();
}); });
lockAllTask.setOnFailed(event -> { lockAllTask.setOnFailed(event -> {
@@ -11,7 +11,6 @@ import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.eventview.EventViewComponent; import org.cryptomator.ui.eventview.EventViewComponent;
import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.notification.NotificationComponent;
import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.quit.QuitComponent;
@@ -55,7 +54,6 @@ public class FxApplicationWindows {
private final LockComponent.Factory lockWorkflowFactory; private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory; private final ErrorComponent.Factory errorWindowFactory;
private final Lazy<EventViewComponent> eventViewWindow; private final Lazy<EventViewComponent> eventViewWindow;
private final Lazy<NotificationComponent> notificationWindow;
private final ExecutorService executor; private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow; private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow; private final ShareVaultComponent.Factory shareVaultWindow;
@@ -75,7 +73,6 @@ public class FxApplicationWindows {
VaultOptionsComponent.Factory vaultOptionsWindow, // VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, // ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, // Lazy<EventViewComponent> eventViewWindow, //
Lazy<NotificationComponent> notificationWindow,
ExecutorService executor, // ExecutorService executor, //
Dialogs dialogs) { Dialogs dialogs) {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
@@ -88,7 +85,6 @@ public class FxApplicationWindows {
this.lockWorkflowFactory = lockWorkflowFactory; this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory; this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow; this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.executor = executor; this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow; this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow; this.shareVaultWindow = shareVaultWindow;
@@ -197,10 +193,6 @@ public class FxApplicationWindows {
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors); 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. * Displays the generic error scene in the given window.
* *
@@ -1,57 +0,0 @@
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,135 @@
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();
}
}
@@ -0,0 +1,93 @@
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;
}
}
@@ -0,0 +1,58 @@
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