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
163 changed files with 906 additions and 5065 deletions
@@ -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
+15 -15
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - 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'
+8 -10
View File
@@ -30,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 --remote-name ${INPUT_URL} -L -o ${{steps.extractName.outputs.fileName}} run: curl --remote-name ${{ inputs.url }} -L -o ${{steps.extractName.outputs.fileName}}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 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 }}
@@ -53,12 +51,12 @@ jobs:
if: github.event_name == 'workflow_call' || inputs.kaspersky if: github.event_name == 'workflow_call' || inputs.kaspersky
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.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
@@ -73,12 +71,12 @@ jobs:
if: github.event_name == 'workflow_call' || inputs.avast if: github.event_name == 'workflow_call' || inputs.avast
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.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
+5 -5
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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,7 +49,7 @@ 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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 uses: softprops/action-gh-release@v2
with: with:
draft: true draft: true
discussion_category_name: releases discussion_category_name: releases
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$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@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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'
+10 -12
View File
@@ -26,10 +26,10 @@ env:
JAVA_VERSION: '24.0.1+9' JAVA_VERSION: '24.0.1+9'
COFFEELIBS_JDK: 24 COFFEELIBS_JDK: 24
COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3' COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e' OPENJFX_JMODS_AMD64_HASH: '425fac742b9fbd095b2ce868cff82d1024620f747c94a7144d0a4879e756146c'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip' OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/24.0.1/openjfx-24.0.1_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4' OPENJFX_JMODS_AARCH64_HASH: '7e02edd0f4ee5527a27c94b0bbba66fcaaff41009119e45d0eca0f96ddfb6e7b'
jobs: jobs:
get-version: get-version:
@@ -41,15 +41,13 @@ 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - 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
@@ -59,14 +57,14 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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: |
@@ -142,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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@v4
with: with:
name: linux-deb-package name: linux-deb-package
path: | path: |
+2 -3
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: 24 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@1297c6fc63a79b147d1676540a3fd9d2e37817c5 # v1.16.5 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - 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 }}
+5 -7
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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 }}
+11 -24
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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\""
@@ -150,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
@@ -261,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 }}
@@ -282,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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@v4
with: with:
name: dmg-${{ matrix.output-suffix }} name: dmg-${{ matrix.output-suffix }}
path: | path: |
@@ -291,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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 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 }}
+11 -25
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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\""
@@ -135,7 +136,6 @@ jobs:
--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.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
@@ -149,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
@@ -260,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 }}
@@ -281,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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@v4
with: with:
name: dmg-${{ matrix.output-suffix }} name: dmg-${{ matrix.output-suffix }}
path: | path: |
@@ -290,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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 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@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 - 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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 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'
+3 -3
View File
@@ -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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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
+4 -5
View File
@@ -19,9 +19,9 @@ jobs:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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 }}
@@ -43,14 +43,13 @@ jobs:
exit 1 exit 1
fi fi
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file - name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
if: ${{ ! (contains(github.event.head_commit.message, '[skip metadata check]') || contains(github.event.head_commit.message, '[metadata check skip]')) }}
run: | run: |
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml" echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
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 }}
@@ -61,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@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 - uses: actions/stale@v9
with: with:
days-before-stale: 365 days-before-stale: 365
days-before-close: 90 days-before-close: 90
+114 -94
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'
@@ -50,10 +49,18 @@ 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
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@v4
- name: Setup Java - name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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@450e322ff2214d0be0b079b63343c894f3ef735f # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - 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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.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@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.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@450e322ff2214d0be0b079b63343c894f3ef735f # 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@450e322ff2214d0be0b079b63343c894f3ef735f # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 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 }}
+1 -1
View File
@@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" 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>
+5 -6
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
@@ -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), coffeelibs-jdk-24 (>= 24.0.1+9-0ppa3), 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;
+2 -1
View File
@@ -28,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 \
@@ -45,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 -20
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\"" \
@@ -133,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 \
-3
View File
@@ -116,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 -12
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 ?>
@@ -127,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
@@ -190,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
+33 -22
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.18.1</version> <version>1.18.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name> <name>Cryptomator Desktop App</name>
<organization> <organization>
@@ -34,47 +34,58 @@
<!-- cryptomator dependencies --> <!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version> <cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.7.0</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.4.1</cryptomator.integrations.mac.version> <cryptomator.integrations.mac.version>1.4.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.6.1</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.13</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>
<!-- TODO: Remove once webdav version 2.0.11 is released -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter-servlet</artifactId>
<version>1.2.9</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies> <dependencies>
<!-- Cryptomator Libs --> <!-- Cryptomator Libs -->
<dependency> <dependency>
@@ -214,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>
@@ -244,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>
@@ -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,6 @@ 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; import java.util.function.Consumer;
@@ -77,7 +75,6 @@ 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;
private Consumer<Settings> saveCmd; private Consumer<Settings> saveCmd;
@@ -117,7 +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.directories.addAll(json.directories.stream().map(VaultSettings::new).toList()); this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -147,7 +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);
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@@ -209,7 +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();
return json; return json;
} }
@@ -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,7 +92,4 @@ class SettingsJson {
@JsonProperty("quickAccessService") @JsonProperty("quickAccessService")
String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE; String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE;
@JsonProperty("previouslyUsedVaultDirectory")
Path previouslyUsedVaultDirectory;
} }
@@ -23,6 +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.VaultEvent;
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;
@@ -34,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;
@@ -73,7 +75,6 @@ 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;
@@ -102,7 +103,6 @@ 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);
@@ -336,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
*/ */
@@ -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;
}
} }
} }
}); });
@@ -18,7 +18,7 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException { public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
try { try {
KeyStore truststore = getTruststore(); KeyStore truststore = getTruststore();
ensureLoaded(truststore); truststore.load(null, null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore); tmf.init(truststore);
@@ -30,13 +30,4 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
throw new SSLContextBuildException(e); throw new SSLContextBuildException(e);
} }
} }
static void ensureLoaded(KeyStore truststore) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
try {
truststore.aliases();
} catch (KeyStoreException e) {
// Not initialized yet (e.g. custom KeyStore SPI); initialize without replacing preloaded stores.
truststore.load(null, null);
}
}
} }
@@ -1,73 +1,21 @@
package org.cryptomator.networking; package org.cryptomator.networking;
import org.cryptomator.common.Nullable;
import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.OperatingSystem;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Properties;
/** /**
* SSLContextProvider for Windows using the Windows certificate store as trust store and the bundled JDK cacerts as fallback * SSLContextProvider for Windows using the Windows certificate store as trust store
* <p> * <p>
* In order to work, the jdk.crypto.mscapi jmod is needed * In order to work, the jdk.crypto.mscapi jmod is needed
*/ */
@OperatingSystem(OperatingSystem.Value.WINDOWS) @OperatingSystem(OperatingSystem.Value.WINDOWS)
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider { public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
private static final Logger LOG = LoggerFactory.getLogger(SSLContextWithWindowsCertStore.class);
private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit"; //default JDK cacerts password
@Override @Override
KeyStore getTruststore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { KeyStore getTruststore() throws KeyStoreException {
var windowsKeyStore = KeyStore.getInstance("WINDOWS-ROOT"); return KeyStore.getInstance("WINDOWS-ROOT");
var jdkKeyStore = getShippedCaCertsStore();
if (jdkKeyStore == null) {
return windowsKeyStore;
}
ensureLoaded(windowsKeyStore);
ensureLoaded(jdkKeyStore);
try {
CombinedKeyStoreSpi spi = CombinedKeyStoreSpi.create(windowsKeyStore, jdkKeyStore);
Provider dummyProvider = new Provider("CombinedKeyStoreProvider", "1.0", "Provides a combined, read-only KeyStore") {};
return new KeyStore(spi, dummyProvider, "CombinedKeyStoreProvider") {};
} catch (IllegalArgumentException e) {
throw new KeyStoreException(e);
}
}
@Nullable
KeyStore getShippedCaCertsStore() {
return getCaCertsStoreByProperties(System.getProperties());
}
//for testability
@VisibleForTesting
@Nullable
KeyStore getCaCertsStoreByProperties(Properties props) {
var javaHome = Path.of(props.getProperty("java.home"));
var trustStorePassword = props.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_TRUSTSTORE_PASSWORD).toCharArray();
for (var candidate : List.of(javaHome.resolve("lib/security/cacerts"), javaHome.resolve("conf/security/cacerts"))) {
try {
if (Files.isRegularFile(candidate)) {
return KeyStore.getInstance(candidate.toFile(), trustStorePassword);
}
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) {
LOG.info("Unable to load fallback cacerts {} file. Skipping fallback.", candidate, e);
}
}
return null;
} }
} }
@@ -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 -> "";
};
} }
} }
@@ -42,10 +42,9 @@ public enum FxmlFile {
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"), //
@@ -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);
} }
} }
@@ -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;
} }
@@ -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
@@ -15,13 +15,15 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent;
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;
@@ -38,8 +40,7 @@ import java.io.InputStream;
HealthCheckComponent.class, // HealthCheckComponent.class, //
UpdateReminderComponent.class, // UpdateReminderComponent.class, //
ShareVaultComponent.class, // ShareVaultComponent.class, //
EventViewComponent.class, // EventViewComponent.class})
RecoveryKeyComponent.class})
abstract class FxApplicationModule { abstract class FxApplicationModule {
private static Image createImageFromResource(String resourceName) throws IOException { private static Image createImageFromResource(String resourceName) throws IOException {
@@ -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 -> {
@@ -46,16 +46,15 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
/** /**
* Determines whether the provided key loader scheme corresponds to a Masterkey File Vault. * Determines whether the provided key loader scheme corresponds to a Masterkey File Vault.
* <p> * <p>
* This method checks if the {@code keyLoader} parameter starts with the known Masterkey File Vault scheme * This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme
* {@link MasterkeyFileLoadingStrategy#SCHEME}. * {@link MasterkeyFileLoadingStrategy#SCHEME}.
* This allows identifying not only exact matches but also variants or extended schemes based on the Masterkey scheme.
* </p> * </p>
* *
* @param keyLoader A string representing the key loader scheme to be checked. * @param keyLoader A string representing the key loader scheme to be checked.
* @return {@code true} if the given key loader scheme starts with the Masterkey File Vault scheme; {@code false} otherwise. * @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise.
*/ */
static boolean isMasterkeyFileVault(String keyLoader) { static boolean isMasterkeyFileVault(String keyLoader) {
return keyLoader.startsWith(MasterkeyFileLoadingStrategy.SCHEME); return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader);
} }
/** /**
@@ -1,18 +1,14 @@
package org.cryptomator.ui.keyloading.masterkeyfile; package org.cryptomator.ui.keyloading.masterkeyfile;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading; import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.binding.StringBinding;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.WindowEvent; import javafx.stage.WindowEvent;
@@ -31,41 +27,17 @@ public class ChooseMasterkeyFileController implements FxController {
private final Stage window; private final Stage window;
private final Vault vault; private final Vault vault;
private final CompletableFuture<Path> result; private final CompletableFuture<Path> result;
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
@FXML
private CheckBox restoreInsteadCheckBox;
@FXML
private Button forwardButton;
@Inject @Inject
public ChooseMasterkeyFileController(@KeyLoading Stage window, // public ChooseMasterkeyFileController(@KeyLoading Stage window, @KeyLoading Vault vault, CompletableFuture<Path> result, ResourceBundle resourceBundle) {
@KeyLoading Vault vault, //
CompletableFuture<Path> result, //
RecoveryKeyComponent.Factory recoveryKeyWindow, //
ResourceBundle resourceBundle) {
this.window = window; this.window = window;
this.vault = vault; this.vault = vault;
this.result = result; this.result = result;
this.recoveryKeyWindow = recoveryKeyWindow;
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.window.setOnHiding(this::windowClosed); this.window.setOnHiding(this::windowClosed);
} }
@FXML
private void initialize() {
restoreInsteadCheckBox.selectedProperty().addListener((_, _, newVal) -> {
if (newVal) {
forwardButton.setText(resourceBundle.getString("addvaultwizard.existing.restore"));
forwardButton.setOnAction(_ -> restoreMasterkey());
} else {
forwardButton.setText(resourceBundle.getString("generic.button.choose"));
forwardButton.setOnAction(_ -> proceed());
}
});
}
@FXML @FXML
public void cancel() { public void cancel() {
window.close(); window.close();
@@ -75,13 +47,6 @@ public class ChooseMasterkeyFileController implements FxController {
result.cancel(true); result.cancel(true);
} }
@FXML
void restoreMasterkey() {
Stage ownerStage = (Stage) window.getOwner();
window.close();
recoveryKeyWindow.create(vault, ownerStage, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_MASTERKEY)).showOnboardingDialogWindow();
}
@FXML @FXML
public void proceed() { public void proceed() {
LOG.trace("proceed()"); LOG.trace("proceed()");
@@ -97,7 +62,7 @@ public class ChooseMasterkeyFileController implements FxController {
//--- Setter & Getter --- //--- Setter & Getter ---
public String getDisplayName() { public String getDisplayName(){
return vault.getDisplayName(); return vault.getDisplayName();
} }
@@ -18,7 +18,6 @@ import org.cryptomator.ui.error.ErrorComponent;
import org.cryptomator.ui.fxapp.FxApplicationTerminator; import org.cryptomator.ui.fxapp.FxApplicationTerminator;
import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.migration.MigrationComponent; import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent; import org.cryptomator.ui.stats.VaultStatisticsComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent; import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
@@ -33,7 +32,7 @@ import javafx.stage.Stage;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class, RecoveryKeyComponent.class}) @Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
abstract class MainWindowModule { abstract class MainWindowModule {
@Provides @Provides
@@ -52,7 +52,7 @@ public class VaultDetailController implements FxController {
case LOCKED -> FontAwesome5Icon.LOCK; case LOCKED -> FontAwesome5Icon.LOCK;
case PROCESSING -> FontAwesome5Icon.SPINNER; case PROCESSING -> FontAwesome5Icon.SPINNER;
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, ALL_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
}; };
} else { } else {
return FontAwesome5Icon.EXCLAMATION_TRIANGLE; return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
@@ -1,26 +1,20 @@
package org.cryptomator.ui.mainwindow; package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.dialogs.Dialogs; import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.File; import java.io.File;
import java.nio.file.Files;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB; import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@MainWindowScoped @MainWindowScoped
public class VaultDetailMissingVaultController implements FxController { public class VaultDetailMissingVaultController implements FxController {
@@ -29,7 +23,6 @@ public class VaultDetailMissingVaultController implements FxController {
private final ObservableList<Vault> vaults; private final ObservableList<Vault> vaults;
private final ResourceBundle resourceBundle; private final ResourceBundle resourceBundle;
private final Stage window; private final Stage window;
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final Dialogs dialogs; private final Dialogs dialogs;
@Inject @Inject
@@ -37,13 +30,11 @@ public class VaultDetailMissingVaultController implements FxController {
ObservableList<Vault> vaults, // ObservableList<Vault> vaults, //
ResourceBundle resourceBundle, // ResourceBundle resourceBundle, //
@MainWindow Stage window, // @MainWindow Stage window, //
Dialogs dialogs, // Dialogs dialogs) {
RecoveryKeyComponent.Factory recoveryKeyWindow) {
this.vault = vault; this.vault = vault;
this.vaults = vaults; this.vaults = vaults;
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.window = window; this.window = window;
this.recoveryKeyWindow = recoveryKeyWindow;
this.dialogs = dialogs; this.dialogs = dialogs;
} }
@@ -57,19 +48,6 @@ public class VaultDetailMissingVaultController implements FxController {
dialogs.prepareRemoveVaultDialog(window, vault.get(), vaults).build().showAndWait(); dialogs.prepareRemoveVaultDialog(window, vault.get(), vaults).build().showAndWait();
} }
@FXML
void restoreVaultConfig() {
if(KeyLoadingStrategy.isHubVault(vault.get().getVaultSettings().lastKnownKeyLoader.get())){
dialogs.prepareContactHubVaultOwner(window).build().showAndWait();
}
else if(Files.exists(vault.get().getPath().resolve(MASTERKEY_FILENAME))){
recoveryKeyWindow.create(vault.get(), window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_VAULT_CONFIG)).showOnboardingDialogWindow();
}
else {
recoveryKeyWindow.create(vault.get(), window, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_ALL)).showOnboardingDialogWindow();
}
}
@FXML @FXML
void changeLocation() { void changeLocation() {
// copied from ChooseExistingVaultController class // copied from ChooseExistingVaultController class
@@ -55,7 +55,7 @@ public class VaultListCellController implements FxController {
case LOCKED -> FontAwesome5Icon.LOCK; case LOCKED -> FontAwesome5Icon.LOCK;
case PROCESSING -> FontAwesome5Icon.SPINNER; case PROCESSING -> FontAwesome5Icon.SPINNER;
case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN; case UNLOCKED -> FontAwesome5Icon.LOCK_OPEN;
case NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, ALL_MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE; case NEEDS_MIGRATION, MISSING, ERROR -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
}; };
} else { } else {
return FontAwesome5Icon.EXCLAMATION_TRIANGLE; return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
@@ -20,13 +20,11 @@ import javafx.stage.Stage;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Objects; import java.util.Objects;
import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.ERROR; 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.LOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.MISSING; import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION; import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED; import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
@MainWindowScoped @MainWindowScoped
public class VaultListContextMenuController implements FxController { public class VaultListContextMenuController implements FxController {
@@ -65,7 +63,7 @@ public class VaultListContextMenuController implements FxController {
this.selectedVaultState = selectedVault.flatMap(Vault::stateProperty).orElse(null); this.selectedVaultState = selectedVault.flatMap(Vault::stateProperty).orElse(null);
this.selectedVaultPassphraseStored = selectedVault.map(this::isPasswordStored).orElse(false); this.selectedVaultPassphraseStored = selectedVault.map(this::isPasswordStored).orElse(false);
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION, ALL_MISSING, VAULT_CONFIG_MISSING)::contains).orElse(false); this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION)::contains).orElse(false);
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false); this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false); this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
} }
@@ -104,12 +102,6 @@ public class VaultListContextMenuController implements FxController {
vaultService.reveal(vault); vaultService.reveal(vault);
} }
@FXML
public void didClickShareVault() {
var vault = Objects.requireNonNull(selectedVault.get());
appWindows.showShareVaultWindow(vault);
}
// Getter and Setter // Getter and Setter
public ObservableValue<Boolean> selectedVaultUnlockableProperty() { public ObservableValue<Boolean> selectedVaultUnlockableProperty() {
@@ -1,16 +1,11 @@
package org.cryptomator.ui.mainwindow; package org.cryptomator.ui.mainwindow;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.recovery.VaultPreparator;
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.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.CryptoFileSystemProvider;
import org.cryptomator.cryptofs.DirStructure; import org.cryptomator.cryptofs.DirStructure;
import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.integrations.mount.MountService;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.common.VaultService;
@@ -18,7 +13,6 @@ import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxFSEventList; import org.cryptomator.ui.fxapp.FxFSEventList;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -28,7 +22,6 @@ import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@@ -44,14 +37,11 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
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.Path; import java.nio.file.Path;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
@@ -60,12 +50,10 @@ import java.util.stream.Collectors;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT; import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
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.ALL_MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.ERROR; 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.LOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.MISSING; import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION; import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
@MainWindowScoped @MainWindowScoped
public class VaultListController implements FxController { public class VaultListController implements FxController {
@@ -87,10 +75,6 @@ public class VaultListController implements FxController {
private final ObservableValue<Double> cellSize; private final ObservableValue<Double> cellSize;
private final Dialogs dialogs; private final Dialogs dialogs;
private final VaultComponent.Factory vaultComponentFactory;
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final List<MountService> mountServices;
public ListView<Vault> vaultList; public ListView<Vault> vaultList;
public StackPane root; public StackPane root;
@FXML @FXML
@@ -110,9 +94,6 @@ public class VaultListController implements FxController {
FxApplicationWindows appWindows, // FxApplicationWindows appWindows, //
Settings settings, // Settings settings, //
Dialogs dialogs, // Dialogs dialogs, //
RecoveryKeyComponent.Factory recoveryKeyWindow, //
VaultComponent.Factory vaultComponentFactory, //
List<MountService> mountServices, //
FxFSEventList fxFSEventList) { FxFSEventList fxFSEventList) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
this.vaults = vaults; this.vaults = vaults;
@@ -124,9 +105,6 @@ public class VaultListController implements FxController {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
this.appWindows = appWindows; this.appWindows = appWindows;
this.dialogs = dialogs; this.dialogs = dialogs;
this.recoveryKeyWindow = recoveryKeyWindow;
this.vaultComponentFactory = vaultComponentFactory;
this.mountServices = mountServices;
this.emptyVaultList = Bindings.isEmpty(vaults); this.emptyVaultList = Bindings.isEmpty(vaults);
this.unreadEvents = fxFSEventList.unreadEventsProperty(); this.unreadEvents = fxFSEventList.unreadEventsProperty();
@@ -226,26 +204,6 @@ public class VaultListController implements FxController {
VaultListManager.redetermineVaultState(newValue); VaultListManager.redetermineVaultState(newValue);
} }
private Optional<Path> chooseValidVaultDirectory() {
DirectoryChooser directoryChooser = new DirectoryChooser();
File selectedDirectory;
do {
selectedDirectory = directoryChooser.showDialog(mainWindow);
if (selectedDirectory == null) {
return Optional.empty();
}
Path selectedPath = selectedDirectory.toPath();
if (!Files.isDirectory(selectedPath.resolve(Constants.DATA_DIR_NAME))) {
dialogs.prepareNoDDirectorySelectedDialog(mainWindow).build().showAndWait();
selectedDirectory = null;
}
} while (selectedDirectory == null);
return Optional.of(selectedDirectory.toPath());
}
@FXML @FXML
public void didClickAddNewVault() { public void didClickAddNewVault() {
addVaultWizard.build().showAddNewVaultWizard(resourceBundle); addVaultWizard.build().showAddNewVaultWizard(resourceBundle);
@@ -256,40 +214,9 @@ public class VaultListController implements FxController {
addVaultWizard.build().showAddExistingVaultWizard(resourceBundle); addVaultWizard.build().showAddExistingVaultWizard(resourceBundle);
} }
@FXML
public void didClickRecoverExistingVault() {
Optional<Path> selectedDirectory = chooseValidVaultDirectory();
if (selectedDirectory.isEmpty()) {
return;
}
Path path = selectedDirectory.get();
Optional<Vault> matchingVaultListEntry = vaultListManager.get(path);
if (matchingVaultListEntry.isPresent()) {
dialogs.prepareRecoveryVaultAlreadyExists(mainWindow, matchingVaultListEntry.get().getDisplayName()) //
.setOkAction(Stage::close) //
.build().showAndWait();
return;
}
Vault preparedVault = VaultPreparator.prepareVault(path, vaultComponentFactory, mountServices, resourceBundle);
VaultListManager.redetermineVaultState(preparedVault);
switch (preparedVault.getState()) {
case VAULT_CONFIG_MISSING -> recoveryKeyWindow.create(preparedVault, mainWindow, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_VAULT_CONFIG)).showOnboardingDialogWindow();
case ALL_MISSING -> recoveryKeyWindow.create(preparedVault, mainWindow, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_ALL)).showOnboardingDialogWindow();
case LOCKED, NEEDS_MIGRATION -> {
vaultListManager.addVault(preparedVault);
dialogs.prepareRecoveryVaultAdded(mainWindow, preparedVault.getDisplayName()).setOkAction(Stage::close).build().showAndWait();
}
default -> LOG.warn("Unhandled vault state during recovery: {}", preparedVault.getState());
}
}
private void pressedShortcutToRemoveVault() { private void pressedShortcutToRemoveVault() {
final var vault = selectedVault.get(); final var vault = selectedVault.get();
if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION, ALL_MISSING, VAULT_CONFIG_MISSING).contains(vault.getState())) { if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION).contains(vault.getState())) {
dialogs.prepareRemoveVaultDialog(mainWindow, vault, vaults).build().showAndWait(); dialogs.prepareRemoveVaultDialog(mainWindow, vault, vaults).build().showAndWait();
} }
} }
@@ -3,13 +3,11 @@ package org.cryptomator.ui.recoverykey;
import dagger.BindsInstance; import dagger.BindsInstance;
import dagger.Lazy; import dagger.Lazy;
import dagger.Subcomponent; import dagger.Subcomponent;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named; import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -26,9 +24,6 @@ public interface RecoveryKeyComponent {
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER)
Lazy<Scene> recoverScene(); Lazy<Scene> recoverScene();
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING)
Lazy<Scene> recoverOnboardingScene();
default void showRecoveryKeyCreationWindow() { default void showRecoveryKeyCreationWindow() {
Stage stage = window(); Stage stage = window();
stage.setScene(creationScene().get()); stage.setScene(creationScene().get());
@@ -43,19 +38,11 @@ public interface RecoveryKeyComponent {
stage.show(); stage.show();
} }
default void showOnboardingDialogWindow() {
Stage stage = window();
stage.setScene(recoverOnboardingScene().get());
stage.sizeToScene();
stage.show();
}
@Subcomponent.Factory @Subcomponent.Factory
interface Factory { interface Factory {
RecoveryKeyComponent create(@BindsInstance @RecoveryKeyWindow Vault vault, // RecoveryKeyComponent create(@BindsInstance @RecoveryKeyWindow Vault vault, @BindsInstance @Named("keyRecoveryOwner") Stage owner);
@BindsInstance @Named("keyRecoveryOwner") Stage owner, //
@BindsInstance @Named("recoverType") ObjectProperty<RecoveryActionType> recoverType);
} }
} }
@@ -1,45 +1,28 @@
package org.cryptomator.ui.recoverykey; package org.cryptomator.ui.recoverykey;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.recovery.CryptoFsInitializer;
import org.cryptomator.common.recovery.MasterkeyService;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.recovery.RecoveryDirectory;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.Animations;
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;
import org.cryptomator.ui.controls.FormattedLabel;
import org.cryptomator.ui.controls.NiceSecurePasswordField; import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.dialogs.Dialogs;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
@RecoveryKeyScoped @RecoveryKeyScoped
public class RecoveryKeyCreationController implements FxController { public class RecoveryKeyCreationController implements FxController {
@@ -47,71 +30,23 @@ public class RecoveryKeyCreationController implements FxController {
private final Stage window; private final Stage window;
private final Lazy<Scene> successScene; private final Lazy<Scene> successScene;
private final Lazy<Scene> recoverykeyExpertSettingsScene;
private final MasterkeyFileAccess masterkeyFileAccess;
private final Vault vault; private final Vault vault;
private final ExecutorService executor; private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory; private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty recoveryKeyProperty; private final StringProperty recoveryKeyProperty;
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
public NiceSecurePasswordField passwordField; public NiceSecurePasswordField passwordField;
private final IntegerProperty shorteningThreshold;
private final ObjectProperty<RecoveryActionType> recoverType;
private final ResourceBundle resourceBundle;
public FormattedLabel descriptionLabel;
public Button cancelButton;
public Button nextButton;
private final VaultListManager vaultListManager;
private final Dialogs dialogs;
@Inject @Inject
public RecoveryKeyCreationController(FxApplicationWindows appWindows, // public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, FxApplicationWindows appWindows, ResourceBundle resourceBundle) {
@RecoveryKeyWindow Stage window, //
@FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverykeyExpertSettingsScene, //
@RecoveryKeyWindow Vault vault, //
RecoveryKeyFactory recoveryKeyFactory, //
MasterkeyFileAccess masterkeyFileAccess, //
ExecutorService executor, //
@RecoveryKeyWindow StringProperty recoveryKey, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle, //
Dialogs dialogs) {
this.window = window; this.window = window;
window.setTitle(resourceBundle.getString("recoveryKey.display.title"));
this.successScene = successScene; this.successScene = successScene;
this.recoverykeyExpertSettingsScene = recoverykeyExpertSettingsScene;
this.vault = vault; this.vault = vault;
this.executor = executor; this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory; this.recoveryKeyFactory = recoveryKeyFactory;
this.recoveryKeyProperty = recoveryKey; this.recoveryKeyProperty = recoveryKey;
this.appWindows = appWindows; this.appWindows = appWindows;
this.recoverType = recoverType;
this.resourceBundle = resourceBundle;
this.masterkeyFileAccess = masterkeyFileAccess;
this.shorteningThreshold = shorteningThreshold;
this.vaultListManager = vaultListManager;
this.dialogs = dialogs;
}
@FXML
public void initialize() {
if (recoverType.get() == RecoveryActionType.SHOW_KEY) {
window.setTitle(resourceBundle.getString("recoveryKey.display.title"));
} else if (recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG) {
window.setTitle(resourceBundle.getString("recover.recoverVaultConfig.title"));
descriptionLabel.formatProperty().set(resourceBundle.getString("recoveryKey.recover.description"));
cancelButton.setOnAction((_) -> back());
cancelButton.setText(resourceBundle.getString("generic.button.back"));
nextButton.setOnAction((_) -> restoreWithPassword());
}
}
@FXML
public void back() {
window.setScene(recoverykeyExpertSettingsScene.get());
window.centerOnScreen();
} }
@FXML @FXML
@@ -136,42 +71,6 @@ public class RecoveryKeyCreationController implements FxController {
executor.submit(task); executor.submit(task);
} }
@FXML
public void restoreWithPassword() {
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
Path recoveryPath = recoveryDirectory.getRecoveryPath();
Path masterkeyFilePath = vault.getPath().resolve(MASTERKEY_FILENAME);
try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, passwordField.getCharacters())) {
var combo = MasterkeyService.detect(masterkey, vault.getPath())
.orElseThrow(() -> new IllegalStateException("Could not detect combo for vault path: " + vault.getPath()));
CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), combo);
}
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
vaultListManager.add(vault.getPath());
}
window.close();
dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) //
.setTitleKey("recover.recoverVaultConfig.title") //
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
.setDescriptionKey("recoveryKey.recover.resetMasterkeyFileSuccess.description")
.build().showAndWait();
} catch (InvalidPassphraseException e) {
LOG.info("Password invalid", e);
Animations.createShakeWindowAnimation(window).play();
} catch (IOException | CryptoException | IllegalStateException e) {
LOG.error("Recovery process failed", e);
appWindows.showErrorWindow(e, window, null);
}
}
@FXML @FXML
public void close() { public void close() {
window.close(); window.close();
@@ -1,123 +0,0 @@
package org.cryptomator.ui.recoverykey;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;
import dagger.Lazy;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.addvaultwizard.CreateNewVaultExpertSettingsController;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.controls.NumericTextField;
@RecoveryKeyScoped
public class RecoveryKeyExpertSettingsController implements FxController {
public static final int MAX_SHORTENING_THRESHOLD = 220;
public static final int MIN_SHORTENING_THRESHOLD = 36;
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/vault/#name-shortening";
private final Stage window;
private final Lazy<Application> application;
private final Vault vault;
private final ObjectProperty<RecoveryActionType> recoverType;
private final IntegerProperty shorteningThreshold;
private final Lazy<Scene> resetPasswordScene;
private final Lazy<Scene> createScene;
private final Lazy<Scene> onBoardingScene;
private final Lazy<Scene> recoverScene;
private final BooleanBinding validShorteningThreshold;
@FXML
public CheckBox expertSettingsCheckBox;
@FXML
public NumericTextField shorteningThresholdTextField;
@Inject
public RecoveryKeyExpertSettingsController(@RecoveryKeyWindow Stage window, //
Lazy<Application> application, //
@RecoveryKeyWindow Vault vault, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_CREATE) Lazy<Scene> createScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING) Lazy<Scene> onBoardingScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene) {
this.window = window;
this.application = application;
this.vault = vault;
this.recoverType = recoverType;
this.shorteningThreshold = shorteningThreshold;
this.resetPasswordScene = resetPasswordScene;
this.createScene = createScene;
this.onBoardingScene = onBoardingScene;
this.recoverScene = recoverScene;
this.validShorteningThreshold = Bindings.createBooleanBinding(this::isValidShorteningThreshold, shorteningThreshold);
}
@FXML
public void initialize() {
shorteningThresholdTextField.setPromptText(MIN_SHORTENING_THRESHOLD + "-" + MAX_SHORTENING_THRESHOLD);
shorteningThresholdTextField.setText(Integer.toString(MAX_SHORTENING_THRESHOLD));
shorteningThresholdTextField.textProperty().addListener((_, _, newValue) -> {
try {
int intValue = Integer.parseInt(newValue);
shorteningThreshold.set(intValue);
} catch (NumberFormatException e) {
shorteningThreshold.set(0); //the value is set to 0 to ensure that an invalid value assignment is detected during a NumberFormatException
}
});
}
@FXML
public void toggleUseExpertSettings() {
if (!expertSettingsCheckBox.isSelected()) {
shorteningThresholdTextField.setText(Integer.toString(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD));
}
}
public void openDocs() {
application.get().getHostServices().showDocument(DOCS_NAME_SHORTENING_URL);
}
public BooleanBinding validShorteningThresholdProperty() {
return validShorteningThreshold;
}
public boolean isValidShorteningThreshold() {
var value = shorteningThreshold.get();
return value >= MIN_SHORTENING_THRESHOLD && value <= MAX_SHORTENING_THRESHOLD;
}
@FXML
public void back() {
if (recoverType.get() == RecoveryActionType.RESTORE_ALL && vault.getState() == VaultState.Value.VAULT_CONFIG_MISSING) {
window.setScene(recoverScene.get());
} else if (recoverType.get() == RecoveryActionType.RESTORE_ALL && vault.getState() == VaultState.Value.ALL_MISSING) {
window.setScene(recoverScene.get());
} else if (recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG) {
window.setScene(onBoardingScene.get());
}
}
@FXML
public void next() {
if (recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG) {
window.setScene(createScene.get());
} else {
window.setScene(resetPasswordScene.get());
}
}
}
@@ -5,12 +5,8 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
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.api.CryptorProvider;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.addvaultwizard.CreateNewVaultExpertSettingsController;
import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxControllerKey;
@@ -23,10 +19,6 @@ import org.cryptomator.ui.common.StageFactory;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
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;
@@ -107,18 +99,12 @@ abstract class RecoveryKeyModule {
} }
@Provides @Provides
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING) @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS)
@RecoveryKeyScoped @RecoveryKeyScoped
static Scene provideRecoveryKeyOnboardingScene(@RecoveryKeyWindow FxmlLoaderFactory fxmlLoaders) { static Scene provideRecoveryKeyResetPasswordSuccessScene(@RecoveryKeyWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.RECOVERYKEY_ONBOARDING); return fxmlLoaders.createScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS);
} }
@Provides
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS)
@RecoveryKeyScoped
static Scene provideRecoveryKeyExpertSettingsScene(@RecoveryKeyWindow FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS);
}
// ------------------ // ------------------
@@ -134,25 +120,6 @@ abstract class RecoveryKeyModule {
return new RecoveryKeyDisplayController(window, vault.getDisplayName(), recoveryKey.get(), localization); return new RecoveryKeyDisplayController(window, vault.getDisplayName(), recoveryKey.get(), localization);
} }
@Provides
@Named("shorteningThreshold")
@RecoveryKeyScoped
static IntegerProperty provideShorteningThreshold() {
return new SimpleIntegerProperty(CreateNewVaultExpertSettingsController.MAX_SHORTENING_THRESHOLD);
}
@Provides
@Named("cipherCombo")
@RecoveryKeyScoped
static ObjectProperty<CryptorProvider.Scheme> provideCipherCombo() {
return new SimpleObjectProperty<>();
}
@Binds
@IntoMap
@FxControllerKey(RecoveryKeyExpertSettingsController.class)
abstract FxController provideRecoveryKeyExpertSettingsController(RecoveryKeyExpertSettingsController controller);
@Binds @Binds
@IntoMap @IntoMap
@FxControllerKey(RecoveryKeyRecoverController.class) @FxControllerKey(RecoveryKeyRecoverController.class)
@@ -170,14 +137,14 @@ abstract class RecoveryKeyModule {
@Binds @Binds
@IntoMap @IntoMap
@FxControllerKey(RecoveryKeyOnboardingController.class) @FxControllerKey(RecoveryKeyResetPasswordSuccessController.class)
abstract FxController bindRecoveryKeyOnboardingController(RecoveryKeyOnboardingController controller); abstract FxController bindRecoveryKeyResetPasswordSuccessController(RecoveryKeyResetPasswordSuccessController controller);
@Provides @Provides
@IntoMap @IntoMap
@FxControllerKey(RecoveryKeyValidateController.class) @FxControllerKey(RecoveryKeyValidateController.class)
static FxController bindRecoveryKeyValidateController(@RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, @Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, @Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo, @Nullable MasterkeyFileAccess masterkeyFileAccess) { static FxController bindRecoveryKeyValidateController(@RecoveryKeyWindow Vault vault, @RecoveryKeyWindow @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory, masterkeyFileAccess, recoverType, cipherCombo); return new RecoveryKeyValidateController(vault, vaultConfig, recoveryKey, recoveryKeyFactory);
} }
@Provides @Provides
@@ -1,176 +0,0 @@
package org.cryptomator.ui.recoverykey;
import dagger.Lazy;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ResourceBundle;
import static org.cryptomator.common.recovery.RecoveryActionType.RESTORE_ALL;
import static org.cryptomator.common.recovery.RecoveryActionType.RESTORE_VAULT_CONFIG;
@RecoveryKeyScoped
public class RecoveryKeyOnboardingController implements FxController {
private final Stage window;
private final Vault vault;
private final Lazy<Scene> recoverykeyRecoverScene;
private final Lazy<Scene> recoverykeyExpertSettingsScene;
private final ObjectProperty<RecoveryActionType> recoverType;
private final ResourceBundle resourceBundle;
public Label titleLabel;
public Label messageLabel;
public Label pleaseConfirm;
public Label secondTextDesc;
@FXML
private CheckBox affirmationBox;
@FXML
private RadioButton recoveryKeyRadio;
@FXML
private RadioButton passwordRadio;
@FXML
private Button nextButton;
@FXML
private VBox chooseMethodeBox;
@FXML
private ToggleGroup methodToggleGroup = new ToggleGroup();
@FXML
private HBox hBox;
@Inject
public RecoveryKeyOnboardingController(@RecoveryKeyWindow Stage window, //
@RecoveryKeyWindow Vault vault, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverykeyRecoverScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverykeyExpertSettingsScene, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
this.recoverykeyRecoverScene = recoverykeyRecoverScene;
this.recoverykeyExpertSettingsScene = recoverykeyExpertSettingsScene;
this.recoverType = recoverType;
this.resourceBundle = resourceBundle;
}
@FXML
public void initialize() {
recoveryKeyRadio.setToggleGroup(methodToggleGroup);
passwordRadio.setToggleGroup(methodToggleGroup);
BooleanBinding showMethodSelection = Bindings.createBooleanBinding(
() -> recoverType.get() == RecoveryActionType.RESTORE_VAULT_CONFIG, recoverType);
chooseMethodeBox.visibleProperty().bind(showMethodSelection);
chooseMethodeBox.managedProperty().bind(showMethodSelection);
nextButton.disableProperty().bind(
affirmationBox.selectedProperty().not()
.or(methodToggleGroup.selectedToggleProperty().isNull().and(showMethodSelection))
);
switch (recoverType.get()) {
case RESTORE_MASTERKEY -> {
window.setTitle(resourceBundle.getString("recover.recoverMasterkey.title"));
messageLabel.setVisible(false);
messageLabel.setManaged(false);
pleaseConfirm.setText(resourceBundle.getString("recover.onBoarding.pleaseConfirm"));
}
case RESTORE_ALL -> {
window.setTitle(resourceBundle.getString("recover.recoverVaultConfig.title"));
messageLabel.setVisible(true);
messageLabel.setManaged(true);
pleaseConfirm.setText(resourceBundle.getString("recover.onBoarding.otherwisePleaseConfirm"));
}
case RESTORE_VAULT_CONFIG -> {
window.setTitle(resourceBundle.getString("recover.recoverVaultConfig.title"));
messageLabel.setVisible(false);
messageLabel.setManaged(false);
pleaseConfirm.setText(resourceBundle.getString("recover.onBoarding.pleaseConfirm"));
}
default -> window.setTitle("");
}
if (vault.getState() == VaultState.Value.ALL_MISSING) {
messageLabel.setText(resourceBundle.getString("recover.onBoarding.allMissing.intro"));
} else {
messageLabel.setText(resourceBundle.getString("recover.onBoarding.intro"));
}
titleLabel.textProperty().bind(Bindings.createStringBinding(() ->
recoverType.get() == RecoveryActionType.RESTORE_MASTERKEY
? resourceBundle.getString("recover.recoverMasterkey.title")
: resourceBundle.getString("recover.recoverVaultConfig.title"), recoverType));
BooleanBinding isRestoreMasterkey = Bindings.createBooleanBinding(
() -> recoverType.get() == RecoveryActionType.RESTORE_MASTERKEY, recoverType);
hBox.minHeightProperty().bind(Bindings.when(isRestoreMasterkey).then(206.0).otherwise(Region.USE_COMPUTED_SIZE));
secondTextDesc.textProperty().bind(Bindings.createStringBinding(() -> {
RecoveryActionType type = recoverType.get();
Toggle sel = methodToggleGroup.getSelectedToggle();
return switch (type) {
case RESTORE_VAULT_CONFIG -> resourceBundle.getString(sel == passwordRadio
? "recover.onBoarding.intro.password"
: "recover.onBoarding.intro.recoveryKey");
case RESTORE_MASTERKEY -> resourceBundle.getString("recover.onBoarding.intro.masterkey.recoveryKey");
case RESTORE_ALL -> resourceBundle.getString("recover.onBoarding.intro.recoveryKey");
default -> "";
};
}, recoverType, methodToggleGroup.selectedToggleProperty()));
showMethodSelection.addListener((_, _, nowShown) -> {
if (nowShown && methodToggleGroup.getSelectedToggle() == null) {
methodToggleGroup.selectToggle(recoveryKeyRadio);
}
});
}
@FXML
public void close() {
window.close();
}
@FXML
public void next() {
switch (recoverType.get()) {
case RESTORE_VAULT_CONFIG, RESTORE_ALL -> {
Object selectedToggle = methodToggleGroup.getSelectedToggle();
if (selectedToggle == recoveryKeyRadio) {
recoverType.set(RESTORE_ALL);
window.setScene(recoverykeyRecoverScene.get());
} else if (selectedToggle == passwordRadio) {
recoverType.set(RESTORE_VAULT_CONFIG);
window.setScene(recoverykeyExpertSettingsScene.get());
} else {
window.setScene(recoverykeyRecoverScene.get());
}
}
case RESTORE_MASTERKEY -> window.setScene(recoverykeyRecoverScene.get());
default -> window.setScene(recoverykeyRecoverScene.get()); // Fallback
}
window.centerOnScreen();
}
}
@@ -1,105 +1,54 @@
package org.cryptomator.ui.recoverykey; package org.cryptomator.ui.recoverykey;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.recovery.RecoveryActionType; import org.cryptomator.common.Nullable;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptofs.VaultConfig;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@RecoveryKeyScoped @RecoveryKeyScoped
public class RecoveryKeyRecoverController implements FxController { public class RecoveryKeyRecoverController implements FxController {
private final Stage window; private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class);
private final Vault vault;
private final Lazy<Scene> nextScene;
private final Lazy<Scene> onBoardingScene;
private final ResourceBundle resourceBundle;
public ObjectProperty<RecoveryActionType> recoverType;
@FXML private final Stage window;
private Button cancelButton; private final Lazy<Scene> resetPasswordScene;
@FXML @FXML
RecoveryKeyValidateController recoveryKeyValidateController; RecoveryKeyValidateController recoveryKeyValidateController;
@Inject @Inject
public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, // public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, ResourceBundle resourceBundle) {
@RecoveryKeyWindow Vault vault, //
ResourceBundle resourceBundle, //
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> expertSettingsScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_ONBOARDING) Lazy<Scene> onBoardingScene, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType) {
this.window = window; this.window = window;
this.vault = vault; window.setTitle(resourceBundle.getString("recoveryKey.recover.title"));
this.resourceBundle = resourceBundle; this.resetPasswordScene = resetPasswordScene;
this.onBoardingScene = onBoardingScene;
this.recoverType = recoverType;
this.nextScene = switch (recoverType.get()) {
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> {
window.setTitle(resourceBundle.getString("recover.recoverVaultConfig.title"));
yield expertSettingsScene;
}
case RESTORE_MASTERKEY -> {
window.setTitle(resourceBundle.getString("recover.recoverMasterkey.title"));
yield resetPasswordScene;
}
case RESET_PASSWORD -> {
window.setTitle(resourceBundle.getString("recoveryKey.recover.title"));
yield resetPasswordScene;
}
case SHOW_KEY -> {
window.setTitle(resourceBundle.getString("recoveryKey.display.title"));
yield resetPasswordScene;
}
default -> throw new IllegalArgumentException("Unexpected recovery action type: " + recoverType.get());
};
} }
@FXML @FXML
public void initialize() { public void initialize() {
if (recoverType.get() == RecoveryActionType.RESET_PASSWORD) {
cancelButton.setText(resourceBundle.getString("generic.button.cancel"));
} else {
cancelButton.setText(resourceBundle.getString("generic.button.back"));
}
} }
@FXML @FXML
public void closeOrReturn() { public void close() {
switch (recoverType.get()) { window.close();
case RESET_PASSWORD -> window.close();
case RESTORE_MASTERKEY -> {
window.setScene(onBoardingScene.get());
window.centerOnScreen();
}
default -> {
if(vault.getState().equals(VaultState.Value.ALL_MISSING)){
recoverType.set(RecoveryActionType.RESTORE_ALL);
}
else {
recoverType.set(RecoveryActionType.RESTORE_VAULT_CONFIG);
}
window.setScene(onBoardingScene.get());
window.centerOnScreen();
}
}
} }
@FXML @FXML
public void recover() { public void recover() {
window.setScene(nextScene.get()); window.setScene(resetPasswordScene.get());
} }
/* Getter/Setter */ /* Getter/Setter */
@@ -1,44 +1,25 @@
package org.cryptomator.ui.recoverykey; package org.cryptomator.ui.recoverykey;
import dagger.Lazy; import dagger.Lazy;
import org.cryptomator.common.recovery.CryptoFsInitializer;
import org.cryptomator.common.recovery.MasterkeyService;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.recovery.RecoveryDirectory;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.changepassword.NewPasswordController;
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;
import org.cryptomator.ui.dialogs.Dialogs; import org.cryptomator.ui.changepassword.NewPasswordController;
import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
@RecoveryKeyScoped @RecoveryKeyScoped
public class RecoveryKeyResetPasswordController implements FxController { public class RecoveryKeyResetPasswordController implements FxController {
@@ -49,140 +30,48 @@ public class RecoveryKeyResetPasswordController implements FxController {
private final RecoveryKeyFactory recoveryKeyFactory; private final RecoveryKeyFactory recoveryKeyFactory;
private final ExecutorService executor; private final ExecutorService executor;
private final StringProperty recoveryKey; private final StringProperty recoveryKey;
private final Lazy<Scene> recoverExpertSettingsScene; private final Lazy<Scene> recoverResetPasswordSuccessScene;
private final Lazy<Scene> recoverykeyRecoverScene;
private final FxApplicationWindows appWindows; private final FxApplicationWindows appWindows;
private final MasterkeyFileAccess masterkeyFileAccess;
private final VaultListManager vaultListManager;
private final IntegerProperty shorteningThreshold;
private final ObjectProperty<RecoveryActionType> recoverType;
private final ObjectProperty<CryptorProvider.Scheme> cipherCombo;
private final ResourceBundle resourceBundle;
private final Dialogs dialogs;
public NewPasswordController newPasswordController; public NewPasswordController newPasswordController;
public Button nextButton;
@Inject @Inject
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, // public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD_SUCCESS) Lazy<Scene> recoverResetPasswordSuccessScene, FxApplicationWindows appWindows) {
@RecoveryKeyWindow Vault vault, //
RecoveryKeyFactory recoveryKeyFactory, //
ExecutorService executor, //
@RecoveryKeyWindow StringProperty recoveryKey, //
@FxmlScene(FxmlFile.RECOVERYKEY_EXPERT_SETTINGS) Lazy<Scene> recoverExpertSettingsScene, //
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverykeyRecoverScene, //
FxApplicationWindows appWindows, //
MasterkeyFileAccess masterkeyFileAccess, //
VaultListManager vaultListManager, //
@Named("shorteningThreshold") IntegerProperty shorteningThreshold, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo, //
ResourceBundle resourceBundle, //
Dialogs dialogs) {
this.window = window; this.window = window;
this.vault = vault; this.vault = vault;
this.recoveryKeyFactory = recoveryKeyFactory; this.recoveryKeyFactory = recoveryKeyFactory;
this.executor = executor; this.executor = executor;
this.recoveryKey = recoveryKey; this.recoveryKey = recoveryKey;
this.recoverExpertSettingsScene = recoverExpertSettingsScene; this.recoverResetPasswordSuccessScene = recoverResetPasswordSuccessScene;
this.recoverykeyRecoverScene = recoverykeyRecoverScene;
this.appWindows = appWindows; this.appWindows = appWindows;
this.masterkeyFileAccess = masterkeyFileAccess;
this.vaultListManager = vaultListManager;
this.shorteningThreshold = shorteningThreshold;
this.cipherCombo = cipherCombo;
this.recoverType = recoverType;
this.resourceBundle = resourceBundle;
this.dialogs = dialogs;
}
@FXML
public void initialize() {
switch (recoverType.get()) {
case RESTORE_MASTERKEY, RESTORE_ALL -> nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn"));
case RESET_PASSWORD -> nextButton.setText(resourceBundle.getString("recoveryKey.recover.resetBtn"));
default -> nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn")); // Fallback
}
} }
@FXML @FXML
public void close() { public void close() {
switch (recoverType.get()) { window.close();
case RESTORE_ALL -> window.setScene(recoverExpertSettingsScene.get());
case RESTORE_MASTERKEY, RESET_PASSWORD -> window.setScene(recoverykeyRecoverScene.get());
default -> window.close();
}
}
@FXML
public void next() {
switch (recoverType.get()) {
case RESTORE_ALL -> restorePassword();
case RESTORE_MASTERKEY, RESET_PASSWORD -> resetPassword();
default -> resetPassword(); // Fallback
}
}
@FXML
public void restorePassword() {
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
Path recoveryPath = recoveryDirectory.getRecoveryPath();
MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters());
try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, recoveryPath.resolve(MASTERKEY_FILENAME), newPasswordController.passwordField.getCharacters())) {
CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), cipherCombo.get());
}
recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME);
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
vaultListManager.add(vault.getPath());
}
window.close();
dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) //
.setTitleKey("recover.recoverVaultConfig.title") //
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
.build().showAndWait();
} catch (IOException | CryptoException e) {
LOG.error("Recovery process failed", e);
appWindows.showErrorWindow(e, window, null);
}
} }
@FXML @FXML
public void resetPassword() { public void resetPassword() {
Task<Void> task = new ResetPasswordTask(); Task<Void> task = new ResetPasswordTask();
task.setOnScheduled(event -> {
task.setOnScheduled(_ -> {
LOG.debug("Using recovery key to reset password for {}.", vault.getDisplayablePath()); LOG.debug("Using recovery key to reset password for {}.", vault.getDisplayablePath());
}); });
task.setOnSucceeded(event -> {
task.setOnSucceeded(_ -> { LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
LOG.debug("Used recovery key to reset password for {}.", vault.getDisplayablePath()); window.setScene(recoverResetPasswordSuccessScene.get());
window.close();
switch (recoverType.get()){
case RESET_PASSWORD -> dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()).build().showAndWait();
case RESTORE_MASTERKEY -> dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()).setTitleKey("recover.recoverMasterkey.title").setMessageKey("recoveryKey.recover.resetMasterkeyFileSuccess.message").build().showAndWait();
default -> dialogs.prepareRecoverPasswordSuccess(window).build().showAndWait(); // Fallback
}
}); });
task.setOnFailed(event -> {
task.setOnFailed(_ -> {
LOG.error("Resetting password failed.", task.getException()); LOG.error("Resetting password failed.", task.getException());
appWindows.showErrorWindow(task.getException(), window, null); appWindows.showErrorWindow(task.getException(), window, null);
}); });
executor.submit(task); executor.submit(task);
} }
private class ResetPasswordTask extends Task<Void> { private class ResetPasswordTask extends Task<Void> {
private static final Logger LOG = LoggerFactory.getLogger(ResetPasswordTask.class); private ResetPasswordTask() {
setOnFailed(event -> LOG.error("Failed to reset password", getException()));
public ResetPasswordTask() {
setOnFailed(_ -> LOG.error("Failed to reset password", getException()));
} }
@Override @Override
@@ -190,6 +79,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
recoveryKeyFactory.newMasterkeyFileWithPassphrase(vault.getPath(), recoveryKey.get(), newPasswordController.passwordField.getCharacters()); recoveryKeyFactory.newMasterkeyFileWithPassphrase(vault.getPath(), recoveryKey.get(), newPasswordController.passwordField.getCharacters());
return null; return null;
} }
} }
/* Getter/Setter */ /* Getter/Setter */
@@ -0,0 +1,24 @@
package org.cryptomator.ui.recoverykey;
import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
@RecoveryKeyScoped
public class RecoveryKeyResetPasswordSuccessController implements FxController {
private final Stage window;
@Inject
public RecoveryKeyResetPasswordSuccessController(@RecoveryKeyWindow Stage window) {
this.window = window;
}
@FXML
public void close() {
window.close();
}
}
@@ -1,23 +1,18 @@
package org.cryptomator.ui.recoverykey; package org.cryptomator.ui.recoverykey;
import com.google.common.base.CharMatcher; import com.google.common.base.CharMatcher;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.cryptomator.common.Nullable; import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.ObservableUtil;
import org.cryptomator.common.recovery.MasterkeyService;
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.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException; import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@@ -27,12 +22,10 @@ import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter; import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import java.io.IOException;
import java.util.NoSuchElementException;
public class RecoveryKeyValidateController implements FxController { public class RecoveryKeyValidateController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyValidateController.class); private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class);
private static final CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' ')); private static final CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' '));
private final Vault vault; private final Vault vault;
@@ -43,23 +36,13 @@ public class RecoveryKeyValidateController implements FxController {
private final ObservableValue<Boolean> recoveryKeyInvalid; private final ObservableValue<Boolean> recoveryKeyInvalid;
private final RecoveryKeyFactory recoveryKeyFactory; private final RecoveryKeyFactory recoveryKeyFactory;
private final ObjectProperty<RecoveryKeyState> recoveryKeyState; private final ObjectProperty<RecoveryKeyState> recoveryKeyState;
private final ObjectProperty<CryptorProvider.Scheme> cipherCombo;
private final AutoCompleter autoCompleter; private final AutoCompleter autoCompleter;
private final ObjectProperty<RecoveryActionType> recoverType;
private final MasterkeyFileAccess masterkeyFileAccess;
private volatile boolean isWrongKey; private volatile boolean isWrongKey;
public TextArea textarea; public TextArea textarea;
public RecoveryKeyValidateController(Vault vault, // public RecoveryKeyValidateController(Vault vault, @Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory) {
@Nullable VaultConfig.UnverifiedVaultConfig vaultConfig, //
StringProperty recoveryKey, //
RecoveryKeyFactory recoveryKeyFactory, //
MasterkeyFileAccess masterkeyFileAccess, //
@Named("recoverType") ObjectProperty<RecoveryActionType> recoverType, //
@Named("cipherCombo") ObjectProperty<CryptorProvider.Scheme> cipherCombo
) {
this.vault = vault; this.vault = vault;
this.unverifiedVaultConfig = vaultConfig; this.unverifiedVaultConfig = vaultConfig;
this.recoveryKey = recoveryKey; this.recoveryKey = recoveryKey;
@@ -69,9 +52,6 @@ public class RecoveryKeyValidateController implements FxController {
this.recoveryKeyCorrect = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.CORRECT::equals, false); this.recoveryKeyCorrect = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.CORRECT::equals, false);
this.recoveryKeyWrong = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.WRONG::equals, false); this.recoveryKeyWrong = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.WRONG::equals, false);
this.recoveryKeyInvalid = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.INVALID::equals, false); this.recoveryKeyInvalid = ObservableUtil.mapWithDefault(recoveryKeyState, RecoveryKeyState.INVALID::equals, false);
this.recoverType = recoverType;
this.cipherCombo = cipherCombo;
this.masterkeyFileAccess = masterkeyFileAccess;
} }
@FXML @FXML
@@ -137,37 +117,14 @@ public class RecoveryKeyValidateController implements FxController {
} }
private void validateRecoveryKey() { private void validateRecoveryKey() {
switch (recoverType.get()) { isWrongKey = false;
case RESTORE_ALL, RESTORE_VAULT_CONFIG -> { var valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null);
try { if (valid) {
var scheme = MasterkeyService.validateRecoveryKeyAndDetectCombo(recoveryKeyFactory, vault, recoveryKey.get(), masterkeyFileAccess); recoveryKeyState.set(RecoveryKeyState.CORRECT);
cipherCombo.set(scheme); } else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig()
recoveryKeyState.set(RecoveryKeyState.CORRECT); recoveryKeyState.set(RecoveryKeyState.WRONG);
} catch (CryptoException e) { } else {
LOG.info("Recovery key is valid but crypto scheme couldn't be determined", e); recoveryKeyState.set(RecoveryKeyState.INVALID);
recoveryKeyState.set(RecoveryKeyState.WRONG);
} catch (IllegalArgumentException e) {
LOG.info("Recovery key is syntactically invalid", e);
recoveryKeyState.set(RecoveryKeyState.INVALID);
} catch (IOException e) {
LOG.warn("IO error while validating recovery key", e);
recoveryKeyState.set(RecoveryKeyState.INVALID);
} catch (NoSuchElementException e) {
LOG.warn("Could not determine scheme from masterkey during recovery key validation, because no valid *.c9r file is present in vault", e);
recoveryKeyState.set(RecoveryKeyState.INVALID);
}
}
case RESTORE_MASTERKEY, RESET_PASSWORD, SHOW_KEY, CONVERT_VAULT -> {
isWrongKey = false;
boolean valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null);
if (valid) {
recoveryKeyState.set(RecoveryKeyState.CORRECT);
} else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig()
recoveryKeyState.set(RecoveryKeyState.WRONG);
} else {
recoveryKeyState.set(RecoveryKeyState.INVALID);
}
}
} }
} }
@@ -19,7 +19,7 @@ public class ShareVaultController implements FxController {
private static final String SCHEME_PREFIX = "hub+"; private static final String SCHEME_PREFIX = "hub+";
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/"; private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults"; private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/en/latest/security/best-practices/#sharing-of-vaults";
private final Stage window; private final Stage window;
private final Lazy<Application> application; private final Lazy<Application> application;
@@ -58,15 +58,9 @@ public class ShareVaultController implements FxController {
private static URI getHubUri(Vault vault) { private static URI getHubUri(Vault vault) {
try { try {
var keyId = new URI(vault.getVaultConfigCache().get().getKeyId().toString()); var keyID = new URI(vault.getVaultConfigCache().get().getKeyId().toString());
assert keyId.getScheme().startsWith(SCHEME_PREFIX); assert keyID.getScheme().startsWith(SCHEME_PREFIX);
var path = keyId.getPath(); return new URI(keyID.getScheme().substring(SCHEME_PREFIX.length()) + "://" + keyID.getHost() + "/app/vaults");
var apiIdx = path.indexOf("/api/");
if (apiIdx < 0) {
throw new IllegalArgumentException("Path does not contain /api/: " + path);
}
var appPath = path.substring(0, apiIdx) + "/app/vaults";
return new URI(keyId.getScheme().substring(SCHEME_PREFIX.length()), keyId.getAuthority(), appPath, null, null);
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
@@ -15,7 +15,6 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.keyloading.KeyLoadingComponent; import org.cryptomator.ui.keyloading.KeyLoadingComponent;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.inject.Named; import javax.inject.Named;
@@ -28,7 +27,7 @@ import javafx.stage.Stage;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@Module(subcomponents = {KeyLoadingComponent.class, RecoveryKeyComponent.class}) @Module(subcomponents = {KeyLoadingComponent.class})
abstract class UnlockModule { abstract class UnlockModule {
@Provides @Provides
@@ -1,16 +1,16 @@
package org.cryptomator.ui.vaultoptions; package org.cryptomator.ui.vaultoptions;
import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.recovery.RecoveryActionType;
import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.changepassword.ChangePasswordComponent; import org.cryptomator.ui.changepassword.ChangePasswordComponent;
import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent; import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -18,6 +18,8 @@ import javafx.stage.Stage;
@VaultOptionsScoped @VaultOptionsScoped
public class MasterkeyOptionsController implements FxController { public class MasterkeyOptionsController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(MasterkeyOptionsController.class);
private final Vault vault; private final Vault vault;
private final Stage window; private final Stage window;
private final ChangePasswordComponent.Builder changePasswordWindow; private final ChangePasswordComponent.Builder changePasswordWindow;
@@ -49,12 +51,12 @@ public class MasterkeyOptionsController implements FxController {
@FXML @FXML
public void showRecoveryKey() { public void showRecoveryKey() {
recoveryKeyWindow.create(vault, window, new SimpleObjectProperty<>(RecoveryActionType.SHOW_KEY)).showRecoveryKeyCreationWindow(); recoveryKeyWindow.create(vault, window).showRecoveryKeyCreationWindow();
} }
@FXML @FXML
public void showRecoverVaultDialog() { public void showRecoverVaultDialog() {
recoveryKeyWindow.create(vault, window, new SimpleObjectProperty<>(RecoveryActionType.RESET_PASSWORD)).showRecoveryKeyRecoverWindow(); recoveryKeyWindow.create(vault, window).showRecoveryKeyRecoverWindow();
} }
@FXML @FXML
@@ -6,12 +6,7 @@
<?import javafx.scene.control.ButtonBar?> <?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?> <VBox xmlns:fx="http://javafx.com/fxml"
<?import javafx.scene.layout.StackPane?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.convertvault.HubToPasswordConvertController" fx:controller="org.cryptomator.ui.convertvault.HubToPasswordConvertController"
minWidth="400" minWidth="400"
@@ -22,16 +17,7 @@
<padding> <padding>
<Insets topRightBottomLeft="12"/> <Insets topRightBottomLeft="12"/>
</padding> </padding>
<Group> <children>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="KEY" glyphSize="24"/>
</StackPane>
</Group>
<VBox spacing="12" HBox.hgrow="ALWAYS" alignment="TOP_CENTER">
<fx:include fx:id="newPassword" source="new_password.fxml"/> <fx:include fx:id="newPassword" source="new_password.fxml"/>
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
@@ -40,7 +26,7 @@
<ButtonBar buttonMinWidth="120" buttonOrder="+CI"> <ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons> <buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="convertBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#convert"> <Button fx:id="convertBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#convert"> <!-- for button logic, see controller -->
<graphic> <graphic>
<FontAwesome5Spinner glyphSize="12"/> <FontAwesome5Spinner glyphSize="12"/>
</graphic> </graphic>
@@ -48,5 +34,5 @@
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox> </VBox>
</VBox> </children>
</HBox> </VBox>
@@ -5,10 +5,7 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.ButtonBar?> <?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?> <VBox xmlns:fx="http://javafx.com/fxml"
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.convertvault.HubToPasswordStartController" fx:controller="org.cryptomator.ui.convertvault.HubToPasswordStartController"
minWidth="400" minWidth="400"
@@ -19,26 +16,18 @@
<padding> <padding>
<Insets topRightBottomLeft="12"/> <Insets topRightBottomLeft="12"/>
</padding> </padding>
<Group> <children>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="SYNC" glyphSize="24"/>
</StackPane>
</Group>
<VBox spacing="12" HBox.hgrow="ALWAYS" alignment="TOP_CENTER">
<fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/> <fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/>
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX"> <VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<buttons> <ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/> <buttons>
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#next" disable="${!controller.validateController.recoveryKeyCorrect}"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
</buttons> <Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#next" disable="${!controller.validateController.recoveryKeyCorrect}"/>
</ButtonBar> </buttons>
</VBox> </ButtonBar>
</HBox> </VBox>
</children>
</VBox>
@@ -40,7 +40,7 @@
<Insets bottom="6" top="6"/> <Insets bottom="6" top="6"/>
</padding> </padding>
</Label> </Label>
<FormattedLabel fx:id="descriptionLabel" format="%recoveryKey.create.description" arg1="${controller.vault.displayName}" wrapText="true"> <FormattedLabel format="%recoveryKey.create.description" arg1="${controller.vault.displayName}" wrapText="true">
<padding> <padding>
<Insets bottom="6"/> <Insets bottom="6"/>
</padding> </padding>
@@ -49,8 +49,8 @@
<Region VBox.vgrow="ALWAYS" minHeight="18"/> <Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX"> <ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons> <buttons>
<Button fx:id="cancelButton" text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="nextButton" text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#createRecoveryKey" disable="${passwordField.text.empty}"/> <Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#createRecoveryKey" disable="${passwordField.text.empty}"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox> </VBox>
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.NumericTextField?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyExpertSettingsController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="QUESTION" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="Expert Settings" wrapText="true">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<CheckBox fx:id="expertSettingsCheckBox" text="%addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox" onAction="#toggleUseExpertSettings"/>
<VBox spacing="6" visible="${expertSettingsCheckBox.selected}">
<HBox spacing="2" HBox.hgrow="NEVER">
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title"/>
<Region prefWidth="2"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
<tooltip>
<Tooltip text="%addvaultwizard.new.expertSettings.shorteningThreshold.tooltip" showDelay="10ms"/>
</tooltip>
</Hyperlink>
</HBox>
<Label text="%recover.expertSettings.shorteningThreshold.title" wrapText="true"/>
<NumericTextField fx:id="shorteningThresholdTextField"/>
<HBox alignment="TOP_RIGHT">
<Region minWidth="4" prefWidth="4" HBox.hgrow="NEVER"/>
<StackPane>
<Label styleClass="label-muted" text="%addvaultwizard.new.expertSettings.shorteningThreshold.invalid" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${!controller.validShorteningThreshold}" managed="${!controller.validShorteningThreshold}" graphicTextGap="6">
<graphic>
<FontAwesome5IconView styleClass="glyph-icon-red" glyph="TIMES"/>
</graphic>
</Label>
<Label styleClass="label-muted" text="%addvaultwizard.new.expertSettings.shorteningThreshold.valid" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.validShorteningThreshold}" managed="${controller.validShorteningThreshold}" graphicTextGap="6">
<graphic>
<FontAwesome5IconView styleClass="glyph-icon-primary" glyph="CHECK"/>
</graphic>
</Label>
</StackPane>
</HBox>
</VBox>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#back"/>
<Button text="%generic.button.next" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#next"/>
</buttons>
</ButtonBar>
</VBox>
</HBox>
@@ -1,88 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:id="hBox"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyOnboardingController"
prefWidth="480"
minHeight="242"
spacing="12"
VBox.vgrow="ALWAYS">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="SYNC" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS" spacing="12">
<Label fx:id="titleLabel" text="%recover.recoverVaultConfig.title" styleClass="label-extra-large"/>
<VBox spacing="6">
<VBox fx:id="chooseMethodeBox" spacing="6">
<Label text="%recover.onBoarding.chooseMethod"/>
<RadioButton fx:id="recoveryKeyRadio" text="%recover.onBoarding.useRecoveryKey" selected="true">
<toggleGroup>
<ToggleGroup fx:id="methodToggleGroup"/>
</toggleGroup>
</RadioButton>
<RadioButton fx:id="passwordRadio" text="%recover.onBoarding.usePassword"/>
</VBox>
<Label fx:id="messageLabel" wrapText="true"/>
<Label fx:id="pleaseConfirm" wrapText="true"/>
<GridPane alignment="CENTER_LEFT">
<padding>
<Insets left="6"/>
</padding>
<columnConstraints>
<ColumnConstraints minWidth="20" halignment="LEFT"/>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints valignment="TOP"/>
<RowConstraints valignment="TOP"/>
<RowConstraints valignment="TOP"/>
</rowConstraints>
<Label text="1." GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<Label text="%recover.onBoarding.intro.ensure" wrapText="true" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label text="2." GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<Label fx:id="secondTextDesc" text="%recover.onBoarding.intro.recoveryKey" wrapText="true" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
</GridPane>
<CheckBox fx:id="affirmationBox" text="%recover.onBoarding.affirmation"/>
</VBox>
<Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX" VBox.vgrow="NEVER">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="nextButton" text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD"
disable="${!affirmationBox.selected}" defaultButton="true" onAction="#next"/>
</buttons>
</ButtonBar>
</VBox>
</HBox>
@@ -3,46 +3,32 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?> <?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?> <VBox xmlns:fx="http://javafx.com/fxml"
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyRecoverController" fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyRecoverController"
minWidth="400" minWidth="400"
maxWidth="400" maxWidth="400"
minHeight="145" minHeight="145"
spacing="12"> spacing="12"
alignment="TOP_CENTER">
<padding> <padding>
<Insets topRightBottomLeft="12"/> <Insets topRightBottomLeft="12"/>
</padding> </padding>
<children> <children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="SYNC" glyphSize="24"/>
</StackPane>
</Group>
<VBox spacing="12" HBox.hgrow="ALWAYS" alignment="TOP_CENTER"> <fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/>
<fx:include fx:id="recoveryKeyValidate" source="recoverykey_validate.fxml"/>
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+BX"> <VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons> <buttons>
<Button fx:id="cancelButton" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#closeOrReturn"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#recover" disable="${!controller.validateController.recoveryKeyCorrect}"/> <Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#recover" disable="${!controller.validateController.recoveryKeyCorrect}"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox> </VBox>
</children> </children>
</HBox> </VBox>
@@ -5,12 +5,7 @@
<?import javafx.scene.control.ButtonBar?> <?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?> <VBox xmlns:fx="http://javafx.com/fxml"
<?import javafx.scene.layout.StackPane?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.Group?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyResetPasswordController" fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyResetPasswordController"
minWidth="400" minWidth="400"
@@ -22,28 +17,17 @@
<Insets topRightBottomLeft="12"/> <Insets topRightBottomLeft="12"/>
</padding> </padding>
<children> <children>
<Group> <fx:include fx:id="newPassword" source="new_password.fxml"/>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="KEY" glyphSize="24"/>
</StackPane>
</Group>
<VBox spacing="12" HBox.hgrow="ALWAYS" alignment="TOP_CENTER">
<fx:include fx:id="newPassword" source="new_password.fxml"/>
<Region VBox.vgrow="ALWAYS"/> <Region VBox.vgrow="ALWAYS"/>
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS"> <VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
<ButtonBar buttonMinWidth="120" buttonOrder="+CI"> <ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons> <buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button fx:id="nextButton" ButtonBar.buttonData="FINISH" defaultButton="true" disable="${!controller.passwordSufficientAndMatching}" onAction="#next"/> <Button text="%recoveryKey.recover.resetBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#resetPassword" disable="${!controller.passwordSufficientAndMatching}"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox>
</VBox> </VBox>
</children> </children>
</HBox> </VBox>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.Region?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyResetPasswordSuccessController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%recoveryKey.recover.resetSuccess.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%recoveryKey.recover.resetSuccess.description" wrapText="true" textAlignment="LEFT"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" cancelButton="true" onAction="#close"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>
@@ -2,40 +2,48 @@
<?import org.cryptomator.ui.controls.FontAwesome5IconView?> <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FormattedLabel?> <?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?> <?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml" <VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyValidateController" fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyValidateController"
minWidth="400"
maxWidth="400" maxWidth="400"
minHeight="145" minHeight="145"
spacing="12"> spacing="12"
alignment="TOP_CENTER">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children> <children>
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/> <FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayName}" wrapText="true"/>
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/> <TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/>
<VBox>
<Label text="Just some Filler" visible="false" managed="${textarea.text.empty}" graphicTextGap="6"> <StackPane>
<Label text="Just some Filler" visible="false" graphicTextGap="6">
<graphic> <graphic>
<FontAwesome5IconView glyph="ANCHOR"/> <FontAwesome5IconView glyph="ANCHOR"/>
</graphic> </graphic>
</Label> </Label>
<Label text="%recoveryKey.recover.correctKey" graphicTextGap="6" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyCorrect}" managed="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyCorrect}"> <Label text="%recoveryKey.recover.correctKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyCorrect}">
<graphic> <graphic>
<FontAwesome5IconView glyph="CHECK"/> <FontAwesome5IconView glyph="CHECK"/>
</graphic> </graphic>
</Label> </Label>
<Label text="%recoveryKey.recover.wrongKey" graphicTextGap="6" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyWrong}" managed="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyWrong}"> <Label text="%recoveryKey.recover.wrongKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyWrong}">
<graphic> <graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/> <FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic> </graphic>
</Label> </Label>
<Label text="%recoveryKey.recover.invalidKey" graphicTextGap="6" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyInvalid}" managed="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyInvalid}"> <Label text="%recoveryKey.recover.invalidKey" graphicTextGap="6" contentDisplay="LEFT" visible="${(!textarea.text.empty) &amp;&amp; controller.recoveryKeyInvalid}">
<graphic> <graphic>
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/> <FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
</graphic> </graphic>
</Label> </Label>
</VBox> </StackPane>
</children> </children>
</VBox> </VBox>
@@ -12,7 +12,6 @@
<?import javafx.scene.Group?> <?import javafx.scene.Group?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import org.cryptomator.ui.controls.FormattedLabel?> <?import org.cryptomator.ui.controls.FormattedLabel?>
<?import javafx.scene.control.CheckBox?>
<HBox xmlns:fx="http://javafx.com/fxml" <HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx" xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.masterkeyfile.ChooseMasterkeyFileController" fx:controller="org.cryptomator.ui.keyloading.masterkeyfile.ChooseMasterkeyFileController"
@@ -41,13 +40,12 @@
</padding> </padding>
</Label> </Label>
<FormattedLabel format="%unlock.chooseMasterkey.description" arg1="${controller.displayName}" wrapText="true"/> <FormattedLabel format="%unlock.chooseMasterkey.description" arg1="${controller.displayName}" wrapText="true"/>
<CheckBox fx:id="restoreInsteadCheckBox" text="%unlock.chooseMasterkey.restoreInstead" wrapText="true"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/> <Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX"> <ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons> <buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel"/> <Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel"/>
<Button fx:id="forwardButton" text="%generic.button.choose" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#proceed"/> <Button text="%generic.button.choose" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#proceed"/>
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</VBox> </VBox>

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