mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bad933c85 |
@@ -26,7 +26,6 @@ body:
|
|||||||
Examples:
|
Examples:
|
||||||
- Operating System: Windows 10
|
- Operating System: Windows 10
|
||||||
- Cryptomator: 1.5.16
|
- Cryptomator: 1.5.16
|
||||||
- OneDrive: 23.226
|
|
||||||
- LibreOffice: 7.1.4
|
- LibreOffice: 7.1.4
|
||||||
value: |
|
value: |
|
||||||
- Operating System:
|
- Operating System:
|
||||||
@@ -44,7 +43,6 @@ body:
|
|||||||
- WinFsp (Local Drive)
|
- WinFsp (Local Drive)
|
||||||
- FUSE-T
|
- FUSE-T
|
||||||
- macFUSE
|
- macFUSE
|
||||||
- FUSE
|
|
||||||
- WebDAV (Windows Explorer)
|
- WebDAV (Windows Explorer)
|
||||||
- WebDAV (AppleScript)
|
- WebDAV (AppleScript)
|
||||||
- WebDAV (gio)
|
- WebDAV (gio)
|
||||||
@@ -97,4 +95,4 @@ body:
|
|||||||
id: further-info
|
id: further-info
|
||||||
attributes:
|
attributes:
|
||||||
label: Anything else?
|
label: Anything else?
|
||||||
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "maven"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
day: "monday"
|
|
||||||
time: "06:00"
|
|
||||||
timezone: "Etc/UTC"
|
|
||||||
groups:
|
|
||||||
java-test-dependencies:
|
|
||||||
patterns:
|
|
||||||
- "org.junit.jupiter:*"
|
|
||||||
- "org.mockito:*"
|
|
||||||
- "org.hamcrest:*"
|
|
||||||
- "com.google.jimfs:jimfs"
|
|
||||||
maven-build-plugins:
|
|
||||||
patterns:
|
|
||||||
- "org.apache.maven.plugins:*"
|
|
||||||
- "org.jacoco:jacoco-maven-plugin"
|
|
||||||
- "org.owasp:dependency-check-maven"
|
|
||||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
|
||||||
- "org.codehaus.mojo:license-maven-plugin"
|
|
||||||
javafx:
|
|
||||||
patterns:
|
|
||||||
- "org.openjfx:*"
|
|
||||||
java-production-dependencies:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
exclude-patterns:
|
|
||||||
- "org.openjfx:*"
|
|
||||||
- "org.apache.maven.plugins:*"
|
|
||||||
- "org.jacoco:jacoco-maven-plugin"
|
|
||||||
- "org.owasp:dependency-check-maven"
|
|
||||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
|
||||||
- "org.codehaus.mojo:license-maven-plugin"
|
|
||||||
- "org.junit.jupiter:*"
|
|
||||||
- "org.mockito:*"
|
|
||||||
- "org.hamcrest:*"
|
|
||||||
- "com.google.jimfs:jimfs"
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/" # even for `.github/workflows`
|
|
||||||
schedule:
|
|
||||||
interval: "monthly"
|
|
||||||
groups:
|
|
||||||
github-actions:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
labels:
|
|
||||||
- "misc:ci"
|
|
||||||
@@ -10,8 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: '21.0.2+13'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -21,71 +20,46 @@ jobs:
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
needs: [get-version]
|
needs: [get-version]
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
appimage-suffix: x86_64
|
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
|
||||||
openjfx-sha: '7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
|
||||||
- os: [self-hosted, Linux, ARM64]
|
|
||||||
appimage-suffix: aarch64
|
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
|
||||||
openjfx-sha: '871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: 'zulu'
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
check-latest: true
|
java-package: 'jdk+fx'
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
|
- name: Ensure major jfx version in pom equals in jdk
|
||||||
- name: Download OpenJFX jmods
|
shell: pwsh
|
||||||
id: download-jmods
|
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
|
||||||
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
|
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
|
||||||
mkdir -p openjfx-jmods
|
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
|
||||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
|
||||||
- name: Ensure major jfx version in pom and in jmods is the same
|
|
||||||
run: |
|
|
||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
|
||||||
|
|
||||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
|
|
||||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
cp target/cryptomator-*.jar target/mods
|
cp target/cryptomator-*.jar target/mods
|
||||||
- name: Run jlink
|
- name: Run jlink
|
||||||
#Remark: no compression is applied for improved build compression later (here appimage)
|
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jlink
|
${JAVA_HOME}/bin/jlink
|
||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
--module-path "${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress zip-0
|
--compress=1
|
||||||
- name: Prepare additional launcher
|
- name: Prepare additional launcher
|
||||||
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||||
env:
|
env:
|
||||||
@@ -103,10 +77,10 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
@@ -118,8 +92,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=false"
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
||||||
--resource-dir dist/linux/resources
|
--resource-dir dist/linux/resources
|
||||||
@@ -130,10 +103,6 @@ jobs:
|
|||||||
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg
|
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg
|
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
|
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
|
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
cp dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
||||||
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
@@ -144,7 +113,7 @@ jobs:
|
|||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
- name: Download AppImageKit
|
- name: Download AppImageKit
|
||||||
run: |
|
run: |
|
||||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o appimagetool.AppImage
|
||||||
chmod +x appimagetool.AppImage
|
chmod +x appimagetool.AppImage
|
||||||
./appimagetool.AppImage --appimage-extract
|
./appimagetool.AppImage --appimage-extract
|
||||||
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
||||||
@@ -156,17 +125,17 @@ jobs:
|
|||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: >
|
run: >
|
||||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
|
||||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
|
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
|
||||||
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
|
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
|
||||||
- name: Create detached GPG signatures
|
- name: Create detached GPG signatures
|
||||||
run: |
|
run: |
|
||||||
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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: appimage-${{ matrix.appimage-suffix }}
|
name: appimage
|
||||||
path: |
|
path: |
|
||||||
cryptomator-*.AppImage
|
cryptomator-*.AppImage
|
||||||
cryptomator-*.AppImage.zsync
|
cryptomator-*.AppImage.zsync
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ on:
|
|||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: 21
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -18,14 +17,14 @@ jobs:
|
|||||||
name: Compile and Test
|
name: Compile and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: 'zulu'
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/.sonar/cache
|
path: ~/.sonar/cache
|
||||||
key: ${{ runner.os }}-sonar
|
key: ${{ runner.os }}-sonar
|
||||||
@@ -36,7 +35,7 @@ jobs:
|
|||||||
mvn -B verify
|
mvn -B verify
|
||||||
jacoco:report
|
jacoco:report
|
||||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||||
-Pcoverage
|
-Pcoverage,dependency-check
|
||||||
-Dsonar.projectKey=cryptomator_cryptomator
|
-Dsonar.projectKey=cryptomator_cryptomator
|
||||||
-Dsonar.organization=cryptomator
|
-Dsonar.organization=cryptomator
|
||||||
-Dsonar.host.url=https://sonarcloud.io
|
-Dsonar.host.url=https://sonarcloud.io
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
name: Checks JDK version for minor updates
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 1 * *' # run once a month at the first day of month
|
|
||||||
|
|
||||||
env:
|
|
||||||
JDK_VERSION: '21.0.1+12'
|
|
||||||
JDK_VENDOR: zulu
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
jdk-current:
|
|
||||||
name: Check out current version
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: ${{ env.JDK_VERSION }}
|
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
|
||||||
check-latest: false
|
|
||||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
|
||||||
id: get-data
|
|
||||||
run: |
|
|
||||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
|
||||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
|
||||||
jdk-latest:
|
|
||||||
name: Checkout latest jdk version
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
|
||||||
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: 21
|
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
|
||||||
check-latest: true
|
|
||||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
|
||||||
id: get-data
|
|
||||||
run: |
|
|
||||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
|
||||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
|
||||||
version=$(cat ${JAVA_HOME}/release | grep "JAVA_RUNTIME_VERSION=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
|
||||||
echo "jdk-version=${version}" >> "$GITHUB_OUTPUT"
|
|
||||||
notify:
|
|
||||||
name: Notifies for jdk update
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [jdk-current, jdk-latest]
|
|
||||||
if: ${{ needs.jdk-latest.outputs.jdk-date }} > ${{ needs.jdk-current.outputs.jdk-date }}
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "JDK update available"
|
|
||||||
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ needs.jdk-latest.outputs.jdk-version }}. See https://github.com/cryptomator/cryptomator/wiki/How-to-update-the-build-JDK for instructions."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
@@ -16,21 +16,16 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: '21.0.2+13'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-x64_bin-jmods.zip'
|
||||||
COFFEELIBS_JDK: 21
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_linux-aarch64_bin-jmods.zip'
|
||||||
COFFEELIBS_JDK_VERSION: '21.0.2+13-0ppa1'
|
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
|
||||||
OPENJFX_JMODS_AMD64_HASH: '7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
|
||||||
OPENJFX_JMODS_AARCH64_HASH: '871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Debian Package
|
name: Build Debian Package
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- id: versions
|
- id: versions
|
||||||
name: Get version information
|
name: Get version information
|
||||||
run: |
|
run: |
|
||||||
@@ -44,25 +39,22 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
|
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.JAVA_VERSION }} libgtk2.0-0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: 'zulu'
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
check-latest: true
|
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: mvn -B clean package -Pdependency-check,linux -DskipTests
|
||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
||||||
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
|
|
||||||
mkdir -p jmods/amd64
|
mkdir -p jmods/amd64
|
||||||
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
||||||
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
||||||
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
|
|
||||||
mkdir -p jmods/aarch64
|
mkdir -p jmods/aarch64
|
||||||
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
||||||
- name: Ensure major jfx version in pom and in jmods is the same
|
- name: Ensure major jfx version in pom and in jmods is the same
|
||||||
@@ -99,8 +91,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r dist/linux/debian/ pkgdir
|
cp -r dist/linux/debian/ pkgdir
|
||||||
export RFC2822_TIMESTAMP=`date --rfc-2822`
|
export RFC2822_TIMESTAMP=`date --rfc-2822`
|
||||||
export DISABLE_UPDATE_CHECK=${{ inputs.dput }}
|
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
|
||||||
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules
|
|
||||||
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
|
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
|
||||||
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
|
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
|
||||||
mv pkgdir cryptomator_${{ inputs.ppaver }}
|
mv pkgdir cryptomator_${{ inputs.ppaver }}
|
||||||
@@ -128,7 +119,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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: linux-deb-package
|
name: linux-deb-package
|
||||||
path: |
|
path: |
|
||||||
@@ -150,4 +141,4 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
artifacts=$(ls | grep cryptomator*.deb)
|
artifacts=$(ls | grep cryptomator*.deb)
|
||||||
gh release upload ${{ github.ref_name }} $artifacts
|
gh release upload ${{ github.ref_name }} $artifacts
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
name: OWASP Maven Dependency Check
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 8 * * 0'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-dependencies:
|
|
||||||
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@v1
|
|
||||||
with:
|
|
||||||
runner-os: 'ubuntu-latest'
|
|
||||||
java-distribution: 'temurin'
|
|
||||||
java-version: 21
|
|
||||||
secrets:
|
|
||||||
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
|
||||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
@@ -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@v7
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const query = `query($owner:String!, $name:String!) {
|
const query = `query($owner:String!, $name:String!) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: Update Error Database
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
discussion:
|
discussion:
|
||||||
types: [created, edited, deleted, category_changed, answered, unanswered]
|
types: [created, edited, category_changed, answered, unanswered]
|
||||||
discussion_comment:
|
discussion_comment:
|
||||||
types: [created, edited, deleted]
|
types: [created, edited, deleted]
|
||||||
|
|
||||||
@@ -12,9 +12,8 @@ jobs:
|
|||||||
if: github.event.discussion.category.name == 'Errors'
|
if: github.event.discussion.category.name == 'Errors'
|
||||||
steps:
|
steps:
|
||||||
- name: Query Discussion Data
|
- name: Query Discussion Data
|
||||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
|
||||||
id: query-data
|
id: query-data
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||||
@@ -48,13 +47,8 @@ jobs:
|
|||||||
- name: Merge Error Code Data
|
- name: Merge Error Code Data
|
||||||
run: |
|
run: |
|
||||||
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
|
jq -c '.' ${{ steps.get-gist.outputs.file }} > original.json
|
||||||
if [ ! -z "$DISCUSSION" ]
|
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
|
||||||
then
|
jq -s '.[0] * .[1]' original.json new.json > merged.json
|
||||||
echo $DISCUSSION | jq -c '.repository.discussion | .comments = .comments.totalCount | {(.id|tostring) : .}' > new.json
|
|
||||||
jq -s '.[0] * .[1]' original.json new.json > merged.json
|
|
||||||
else
|
|
||||||
cat original.json | jq 'del(.[] | select(.url=="https://github.com/cryptomator/cryptomator/discussions/${{ github.event.discussion.number }}"))' > merged.json
|
|
||||||
fi
|
|
||||||
env:
|
env:
|
||||||
DISCUSSION: ${{ steps.query-data.outputs.result }}
|
DISCUSSION: ${{ steps.query-data.outputs.result }}
|
||||||
- name: Patch Gist
|
- name: Patch Gist
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ on:
|
|||||||
value: ${{ jobs.determine-version.outputs.type }}
|
value: ${{ jobs.determine-version.outputs.type }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: 21
|
JAVA_DIST: 'temurin'
|
||||||
|
JAVA_CACHE: 'maven'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine-version:
|
determine-version:
|
||||||
@@ -35,15 +36,15 @@ 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@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: ${{ env.JAVA_CACHE }}
|
||||||
- id: versions
|
- id: versions
|
||||||
name: Get version information
|
name: Get version information
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -8,15 +8,9 @@ on:
|
|||||||
version:
|
version:
|
||||||
description: 'Version'
|
description: 'Version'
|
||||||
required: false
|
required: false
|
||||||
notarize:
|
|
||||||
description: 'Notarize'
|
|
||||||
required: true
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: '21.0.2+13'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -37,66 +31,51 @@ jobs:
|
|||||||
output-suffix: x64
|
output-suffix: x64
|
||||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||||
fuse-lib: macFUSE
|
fuse-lib: macFUSE
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-x64_bin-jmods.zip'
|
|
||||||
openjfx-sha: 'bd6abab20da73d5a968dcf2fd915d81b5fb919340e3bb84979ee9a888a829939'
|
|
||||||
- os: [self-hosted, macOS, ARM64]
|
- os: [self-hosted, macOS, ARM64]
|
||||||
architecture: aarch64
|
architecture: aarch64
|
||||||
output-suffix: arm64
|
output-suffix: arm64
|
||||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||||
fuse-lib: FUSE-T
|
fuse-lib: FUSE-T
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-aarch64_bin-jmods.zip'
|
|
||||||
openjfx-sha: '7afaa1c57a6cc3c384d636e597b9a5364693e2db4aaec0a6e63d2fa964400b58'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: 'zulu'
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
|
java-package: 'jdk+fx'
|
||||||
architecture: ${{ matrix.architecture }}
|
architecture: ${{ matrix.architecture }}
|
||||||
check-latest: true
|
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Download OpenJFX jmods
|
- name: Ensure major jfx version in pom equals in jdk
|
||||||
id: download-jmods
|
if: ${{ !contains(matrix.os, 'self-hosted') }}
|
||||||
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
|
||||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
|
||||||
mkdir -p openjfx-jmods/
|
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
|
||||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
|
||||||
- name: Ensure major jfx version in pom and in jmods is the same
|
|
||||||
run: |
|
|
||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
|
||||||
|
|
||||||
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
|
||||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: mvn -B clean package -Pdependency-check,mac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
cp target/cryptomator-*.jar target/mods
|
cp target/cryptomator-*.jar target/mods
|
||||||
- name: Run jlink
|
- name: Run jlink
|
||||||
#Remark: no compression is applied for improved build compression later (here dmg)
|
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jlink
|
${JAVA_HOME}/bin/jlink
|
||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
--module-path "${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress zip-0
|
--compress=1
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -109,7 +88,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||||
--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=org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
||||||
@@ -223,12 +202,13 @@ jobs:
|
|||||||
--app-drop-link 512 245
|
--app-drop-link 512 245
|
||||||
--eula "dist/mac/dmg/resources/license.rtf"
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
--icon ".background" 128 758
|
--icon ".background" 128 758
|
||||||
|
--icon ".fseventsd" 320 758
|
||||||
--icon ".VolumeIcon.icns" 512 758
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
- name: Notarize .dmg
|
- name: Notarize .dmg
|
||||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: cocoalibs/xcode-notarization-action@v1
|
uses: cocoalibs/xcode-notarization-action@v1
|
||||||
with:
|
with:
|
||||||
app-path: 'Cryptomator-*.dmg'
|
app-path: 'Cryptomator-*.dmg'
|
||||||
@@ -250,7 +230,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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: Cryptomator-*.dmg
|
path: Cryptomator-*.dmg
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v8
|
||||||
with:
|
with:
|
||||||
days-before-stale: 14
|
days-before-stale: 14
|
||||||
days-before-close: 0
|
days-before-close: 0
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: 21
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -17,11 +16,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: 'zulu'
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||||
@@ -6,26 +6,19 @@ on:
|
|||||||
- 'release/**'
|
- 'release/**'
|
||||||
- 'hotfix/**'
|
- 'hotfix/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
JAVA_VERSION: 20
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
env:
|
|
||||||
JAVA_DIST: 'zulu'
|
|
||||||
JAVA_VERSION: 21
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-preconditions:
|
release-check-precondition:
|
||||||
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@v4
|
- uses: actions/checkout@v2
|
||||||
- name: Setup Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
|
||||||
cache: 'maven'
|
|
||||||
- id: validate-pom-version
|
- id: validate-pom-version
|
||||||
name: Validate POM version
|
name: Validate POM version
|
||||||
run: |
|
run: |
|
||||||
@@ -47,19 +40,4 @@ jobs:
|
|||||||
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
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
|
||||||
key: dependency-check-${{ github.run_id }}
|
|
||||||
restore-keys: |
|
|
||||||
dependency-check
|
|
||||||
env:
|
|
||||||
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
|
|
||||||
- name: Run org.owasp:dependency-check plugin
|
|
||||||
id: dependency-check
|
|
||||||
continue-on-error: true
|
|
||||||
run: mvn -B verify -Pdependency-check -DskipTests
|
|
||||||
env:
|
|
||||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
|
||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v8
|
||||||
with:
|
with:
|
||||||
days-before-stale: 365
|
days-before-stale: 365
|
||||||
days-before-close: 90
|
days-before-close: 90
|
||||||
|
|||||||
@@ -10,16 +10,15 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
isDebug:
|
isDebug:
|
||||||
description: 'Build debug version with console output'
|
description: 'Build debug version with console output'
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_VERSION: 20
|
||||||
JAVA_VERSION: '21.0.2+13'
|
JAVA_DIST: 'temurin'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_windows-x64_bin-jmods.zip'
|
JAVA_CACHE: 'maven'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
JFX_JMODS_HASH: 'D00767334C43B8832B5CF10267D34CA8F563D187C4655B73EB6020DD79C054B5'
|
||||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -39,20 +38,20 @@ jobs:
|
|||||||
LOOPBACK_ALIAS: 'cryptomator-vault'
|
LOOPBACK_ALIAS: 'cryptomator-vault'
|
||||||
WIN_CONSOLE_FLAG: ''
|
WIN_CONSOLE_FLAG: ''
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
check-latest: true
|
java-package: 'jdk'
|
||||||
cache: 'maven'
|
cache: ${{ env.JAVA_CACHE }}
|
||||||
- name: Download and extract JavaFX jmods from Gluon
|
- name: Download and extract JavaFX jmods from Gluon
|
||||||
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||||
run: |
|
run: |
|
||||||
curl --output jfxjmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
|
curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}"
|
||||||
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
|
if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) {
|
||||||
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
|
throw "Wrong checksum of JMOD archive downloaded from ${{ env.JFX_JMODS_URL }}.";
|
||||||
}
|
}
|
||||||
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
|
Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods
|
||||||
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
|
Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent}
|
||||||
@@ -73,13 +72,12 @@ 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 -Pdependency-check,win -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
cp target/cryptomator-*.jar target/mods
|
cp target/cryptomator-*.jar target/mods
|
||||||
- name: Run jlink
|
- name: Run jlink
|
||||||
#Remark: no compression is applied for improved build compression later (here msi)
|
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jlink
|
${JAVA_HOME}/bin/jlink
|
||||||
--verbose
|
--verbose
|
||||||
@@ -90,7 +88,7 @@ jobs:
|
|||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress zip-0
|
--compress=1
|
||||||
- name: Change win-console flag if debug is active
|
- name: Change win-console flag if debug is active
|
||||||
if: ${{ inputs.isDebug }}
|
if: ${{ inputs.isDebug }}
|
||||||
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
|
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
|
||||||
@@ -106,7 +104,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
|
||||||
@@ -146,29 +144,9 @@ jobs:
|
|||||||
- name: Fix permissions
|
- name: Fix permissions
|
||||||
run: attrib -r appdir/Cryptomator/Cryptomator.exe
|
run: attrib -r appdir/Cryptomator/Cryptomator.exe
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Extract jars with DLLs for Codesigning
|
- name: Extract integrations DLL for code signing
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --extract integrations.dll }
|
||||||
Add-Type -AssemblyName "System.io.compression.filesystem"
|
|
||||||
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
|
|
||||||
$jarExtractDir = New-Item -Path ".\appdir\jar-extract" -ItemType Directory
|
|
||||||
|
|
||||||
#for all jars inspect
|
|
||||||
Get-ChildItem -Path $jarFolder -Filter "*.jar" | ForEach-Object {
|
|
||||||
$jar = [Io.compression.zipfile]::OpenRead($_.FullName)
|
|
||||||
if (@($jar.Entries | Where-Object {$_.Name.ToString().EndsWith(".dll")} | Select-Object -First 1).Count -gt 0) {
|
|
||||||
#jars containing dlls extract
|
|
||||||
Set-Location $jarExtractDir
|
|
||||||
Expand-Archive -Path $_.FullName
|
|
||||||
}
|
|
||||||
$jar.Dispose()
|
|
||||||
}
|
|
||||||
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
New-Item -Path appdir/jpackage-jmod -ItemType Directory
|
|
||||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
|
||||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
|
||||||
- name: Codesign
|
- name: Codesign
|
||||||
uses: skymatic/code-sign-action@v2
|
uses: skymatic/code-sign-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -177,22 +155,12 @@ jobs:
|
|||||||
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
certificatesha1: 5FC94CE149E5B511E621F53A060AC67CBD446B3A
|
||||||
description: Cryptomator
|
description: Cryptomator
|
||||||
timestampUrl: 'http://timestamp.digicert.com'
|
timestampUrl: 'http://timestamp.digicert.com'
|
||||||
folder: appdir
|
folder: appdir/Cryptomator
|
||||||
recursive: true
|
recursive: true
|
||||||
- name: Replace DLLs inside jars with signed ones
|
- name: Repack signed DLL into jar
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$jarExtractDir = Resolve-Path ".\appdir\jar-extract"
|
gci ./appdir/Cryptomator/app/mods/ -File integrations-win-*.jar | ForEach-Object {Set-Location -Path $_.Directory; jar --file=$($_.FullName) --update integrations.dll; Remove-Item integrations.dll}
|
||||||
$jarFolder = Resolve-Path ".\appdir\Cryptomator\app\mods"
|
|
||||||
Get-ChildItem -Path $jarExtractDir | ForEach-Object {
|
|
||||||
$jarName = $_.Name
|
|
||||||
$jarFile = "${jarFolder}\${jarName}.jar"
|
|
||||||
Set-Location $_
|
|
||||||
Get-ChildItem -Path $_ -Recurse -File "*.dll" | ForEach-Object {
|
|
||||||
# update jar with signed dll
|
|
||||||
jar --file="$jarFile" --update $(Resolve-Path -Relative -Path $_)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- name: Generate license for MSI
|
- name: Generate license for MSI
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party
|
||||||
@@ -214,7 +182,7 @@ jobs:
|
|||||||
--dest installer
|
--dest installer
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||||
--win-menu
|
--win-menu
|
||||||
--win-dir-chooser
|
--win-dir-chooser
|
||||||
@@ -226,7 +194,6 @@ jobs:
|
|||||||
--file-associations dist/win/resources/FAvaultFile.properties
|
--file-associations dist/win/resources/FAvaultFile.properties
|
||||||
env:
|
env:
|
||||||
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
|
|
||||||
- name: Codesign MSI
|
- name: Codesign MSI
|
||||||
uses: skymatic/code-sign-action@v2
|
uses: skymatic/code-sign-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -246,7 +213,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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: msi
|
name: msi
|
||||||
path: |
|
path: |
|
||||||
@@ -268,20 +235,19 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: [get-version, build-msi]
|
needs: [get-version, build-msi]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Download .msi
|
- name: Download .msi
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: msi
|
name: msi
|
||||||
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
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
check-latest: true
|
cache: ${{ env.JAVA_CACHE }}
|
||||||
cache: 'maven'
|
|
||||||
- name: Generate license for exe
|
- name: Generate license for exe
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party
|
||||||
@@ -295,11 +261,8 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Download WinFsp
|
- name: Download WinFsp
|
||||||
run: |
|
run: |
|
||||||
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
|
$winfspUrl = (Select-String -Path ".\dist\win\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||||
shell: pwsh
|
curl --output dist/win/bundle/resources/winfsp.msi -L $winfspUrl
|
||||||
- name: Download Legacy-WinFsp uninstaller
|
|
||||||
run: |
|
|
||||||
curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
|
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Compile to wixObj file
|
- name: Compile to wixObj file
|
||||||
run: >
|
run: >
|
||||||
@@ -309,7 +272,7 @@ jobs:
|
|||||||
-out dist/win/bundle/
|
-out dist/win/bundle/
|
||||||
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
-dBundleVendor="Skymatic GmbH"
|
-dBundleVendor="Skymatic GmbH"
|
||||||
-dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
|
-dBundleCopyright="(C) 2016 - 2023 Skymatic GmbH"
|
||||||
-dAboutUrl="https://cryptomator.org"
|
-dAboutUrl="https://cryptomator.org"
|
||||||
-dHelpUrl="https://cryptomator.org/contact"
|
-dHelpUrl="https://cryptomator.org/contact"
|
||||||
-dUpdateUrl="https://cryptomator.org/downloads/"
|
-dUpdateUrl="https://cryptomator.org/downloads/"
|
||||||
@@ -357,7 +320,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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: exe
|
name: exe
|
||||||
path: |
|
path: |
|
||||||
@@ -381,12 +344,12 @@ jobs:
|
|||||||
needs: [build-msi, build-exe]
|
needs: [build-msi, build-exe]
|
||||||
steps:
|
steps:
|
||||||
- name: Download .msi
|
- name: Download .msi
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: msi
|
name: msi
|
||||||
path: msi
|
path: msi
|
||||||
- name: Download .exe
|
- name: Download .exe
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: exe
|
name: exe
|
||||||
path: exe
|
path: exe
|
||||||
@@ -396,7 +359,7 @@ jobs:
|
|||||||
cp msi/*.msi files
|
cp msi/*.msi files
|
||||||
cp exe/*.exe files
|
cp exe/*.exe files
|
||||||
- name: Upload to Kaspersky
|
- name: Upload to Kaspersky
|
||||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
|
uses: SamKirkland/FTP-Deploy-Action@4.3.3
|
||||||
with:
|
with:
|
||||||
protocol: ftps
|
protocol: ftps
|
||||||
server: allowlist.kaspersky-labs.com
|
server: allowlist.kaspersky-labs.com
|
||||||
@@ -405,7 +368,7 @@ jobs:
|
|||||||
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
|
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
|
||||||
local-dir: files/
|
local-dir: files/
|
||||||
- name: Upload to Avast
|
- name: Upload to Avast
|
||||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
|
uses: SamKirkland/FTP-Deploy-Action@4.3.0
|
||||||
with:
|
with:
|
||||||
protocol: ftp
|
protocol: ftp
|
||||||
server: whitelisting.avast.com
|
server: whitelisting.avast.com
|
||||||
@@ -413,21 +376,3 @@ jobs:
|
|||||||
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
||||||
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
||||||
local-dir: files/
|
local-dir: files/
|
||||||
notify-winget:
|
|
||||||
name: Notify for winget-release
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
needs: [build-msi]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "MSI of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
|
||||||
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release to winget>."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
name: Publish MSI to winget-pkgs
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tag:
|
|
||||||
description: 'Release tag'
|
|
||||||
required: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
winget:
|
|
||||||
name: Publish winget package
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Sync winget-pkgs fork
|
|
||||||
run: |
|
|
||||||
gh repo sync cryptomator/winget-pkgs -b master --force
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
|
||||||
- name: Submit package
|
|
||||||
uses: vedantmgoyal2009/winget-releaser@v2
|
|
||||||
with:
|
|
||||||
identifier: Cryptomator.Cryptomator
|
|
||||||
version: ${{ inputs.tag }}
|
|
||||||
release-tag: ${{ inputs.tag }}
|
|
||||||
installers-regex: '\.msi$'
|
|
||||||
token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
|
||||||
Generated
+12
-11
@@ -14,10 +14,10 @@
|
|||||||
<option name="dagger.fastInit" value="enabled" />
|
<option name="dagger.fastInit" value="enabled" />
|
||||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.48.1/dagger-compiler-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.45/dagger-compiler-2.45.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.45/dagger-2.45.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.45/dagger-producers-2.45.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||||
@@ -26,19 +26,20 @@
|
|||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.45/dagger-spi-2.45.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.7.0-1.0.6/symbol-processing-api-1.7.0-1.0.6.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.7.0/kotlin-stdlib-1.7.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.0/kotlin-stdlib-common-1.7.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.0/kotlin-stdlib-jdk8-1.7.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.0/kotlin-stdlib-jdk7-1.7.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.5.0/kotlinx-metadata-jvm-0.5.0.jar" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
</profile>
|
</profile>
|
||||||
|
|||||||
Generated
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_20_PREVIEW" project-jdk-name="20" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="true" -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -30,10 +30,9 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
|||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="28"></a></td>
|
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="40"></a></td>
|
||||||
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
|
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
|
||||||
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
|
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
|
||||||
<td><a href="https://ente.io/"><img src="https://cryptomator.org/img/sponsors/ente.svg" alt="Ente" height="58"></a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -86,7 +85,7 @@ For more information on the security details visit [cryptomator.org](https://doc
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 21 (e.g. temurin, zulu)
|
* JDK 19 (e.g. temurin)
|
||||||
* Maven 3
|
* Maven 3
|
||||||
|
|
||||||
### Run Maven
|
### Run Maven
|
||||||
|
|||||||
Vendored
+13
-50
@@ -1,5 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
|
||||||
|
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
REVISION_NO=`git rev-list --count HEAD`
|
REVISION_NO=`git rev-list --count HEAD`
|
||||||
@@ -8,60 +7,29 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||||
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
|
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
|
||||||
command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
|
command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
|
||||||
command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
|
|
||||||
|
|
||||||
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||||
SEMVER_STR=${VERSION}
|
SEMVER_STR=${VERSION}
|
||||||
MACHINE_TYPE=$(uname -m)
|
|
||||||
|
|
||||||
if [[ ! "${MACHINE_TYPE}" =~ x86_64|aarch64 ]]; then echo "Platform ${MACHINE_TYPE} not supported"; exit 1; fi
|
|
||||||
|
|
||||||
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
|
||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
|
cp ../launcher.sh ../../../target
|
||||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||||
|
|
||||||
|
|
||||||
# download javaFX jmods
|
|
||||||
OPENJFX_URL='https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
|
||||||
OPENJFX_SHA='7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
|
||||||
OPENJFX_URL_aarch64='https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
|
||||||
OPENJFX_SHA_aarch64='871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
|
||||||
|
|
||||||
if [[ "${MACHINE_TYPE}" = "aarch64" ]]; then
|
|
||||||
OPENJFX_URL="${OPENJFX_URL_aarch64}";
|
|
||||||
OPENJFX_SHA="${OPENJFX_SHA_aarch64}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -L ${OPENJFX_URL} -o openjfx-jmods.zip
|
|
||||||
echo "${OPENJFX_SHA} openjfx-jmods.zip" | shasum -a256 --check
|
|
||||||
mkdir -p openjfx-jmods
|
|
||||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
|
||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
|
||||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
|
|
||||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION})"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# add runtime
|
# add runtime
|
||||||
${JAVA_HOME}/bin/jlink \
|
${JAVA_HOME}/bin/jlink \
|
||||||
--verbose \
|
--verbose \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
--module-path "${JAVA_HOME}/jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
--compress zip-0
|
--compress=1
|
||||||
|
|
||||||
# create app dir
|
# create app dir
|
||||||
envsubst '${SEMVER_STR} ${REVISION_NUM}' < ../launcher-gtk2.properties > launcher-gtk2.properties
|
envsubst '${SEMVER_STR} ${REVISION_NUM}' < ../launcher-gtk2.properties > launcher-gtk2.properties
|
||||||
@@ -76,8 +44,8 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--name Cryptomator \
|
--name Cryptomator \
|
||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" \
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
@@ -89,8 +57,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
|
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||||
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
|
||||||
--resource-dir ../resources
|
--resource-dir ../resources
|
||||||
@@ -102,10 +69,6 @@ envsubst '${REVISION_NO}' < resources/AppDir/bin/cryptomator.sh > Cryptomator.Ap
|
|||||||
cp ../common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
cp ../common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
|
||||||
cp ../common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
cp ../common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
|
||||||
cp ../common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
cp ../common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
|
||||||
cp ../common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg
|
|
||||||
cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg
|
|
||||||
cp ../common/org.cryptomator.Cryptomator.tray.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
|
|
||||||
cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
|
|
||||||
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
||||||
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
@@ -116,17 +79,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
|
|||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
|
|
||||||
# load AppImageTool
|
# load AppImageTool
|
||||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
|
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage -o /tmp/appimagetool.AppImage
|
||||||
chmod +x /tmp/appimagetool.AppImage
|
chmod +x /tmp/appimagetool.AppImage
|
||||||
|
|
||||||
# create AppImage
|
# create AppImage
|
||||||
/tmp/appimagetool.AppImage \
|
/tmp/appimagetool.AppImage \
|
||||||
Cryptomator.AppDir \
|
Cryptomator.AppDir \
|
||||||
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
|
cryptomator-${SEMVER_STR}-x86_64.AppImage \
|
||||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
|
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
|
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-x86_64.AppImage"
|
||||||
echo ""
|
|
||||||
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir runtime squashfs-root openjfx-jmods; rm launcher-gtk2.properties /tmp/appimagetool.AppImage openjfx-jmods.zip"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.AppImage"
|
||||||
|
echo ""
|
||||||
@@ -66,16 +66,6 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release date="2024-02-09" version="1.12.2"/>
|
|
||||||
<release date="2024-02-07" version="1.12.1"/>
|
|
||||||
<release date="2024-02-06" version="1.12.0"/>
|
|
||||||
<release date="2023-12-05" version="1.11.1"/>
|
|
||||||
<release date="2023-11-08" version="1.11.0"/>
|
|
||||||
<release date="2023-09-20" version="1.10.1"/>
|
|
||||||
<release date="2023-09-11" version="1.10.0"/>
|
|
||||||
<release date="2023-08-11" version="1.9.4"/>
|
|
||||||
<release date="2023-08-07" version="1.9.3"/>
|
|
||||||
<release date="2023-07-24" version="1.9.2"/>
|
|
||||||
<release date="2023-06-07" version="1.9.1"/>
|
<release date="2023-06-07" version="1.9.1"/>
|
||||||
<release date="2023-05-30" version="1.9.0"/>
|
<release date="2023-05-30" version="1.9.0"/>
|
||||||
<release date="2023-04-25" version="1.8.0"/>
|
<release date="2023-04-25" version="1.8.0"/>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<style
|
|
||||||
id="current-color-scheme" type="text/css">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#232629;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<g fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor">
|
|
||||||
<path d="m15.591 35.824c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.043c.051 1.03-.161 2.644-.509 2.834zm6.409-6.824h-2l.5-5a2 2 0 1 1 1 0zm-14.544-3.241.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm13.544-10.759a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm14.473 6a8.067 8.067 0 0 0 -8.08 8v2.141a3.891 3.891 0 0 0 -2.893 3.734v5.125a23.166 23.166 0 0 1 -4.174-1.623 7.857 7.857 0 0 1 -.027.878 3.263 3.263 0 0 1 -.729 2.074l-1.794 1.483a.379.379 0 0 1 -.276.188h-4c-1.324 0-2.346-1.336-2.653-3.343a7.058 7.058 0 0 1 .234-3.18 3.477 3.477 0 0 1 1.636-2.157 1.868 1.868 0 0 1 .783-.32h1.5a8.035 8.035 0 0 1 -1.5-5 11.1 11.1 0 0 1 .5-3 2.519 2.519 0 0 0 0-1.5 13.272 13.272 0 0 1 -.5-3.5c6.687-1.936 11 0 11 0s4.319-1.955 11 0"/>
|
|
||||||
<path d="m39 28h-10v-4a3.13 3.13 0 0 1 3-3 3.087 3.087 0 0 1 3 3v1a1.034 1.034 0 0 0 1 1h1a1.034 1.034 0 0 0 1-1v-1a6 6 0 0 0 -12 0v4h-1a2.073 2.073 0 0 0 -2 2v6a2.073 2.073 0 0 0 2 2h14a2.073 2.073 0 0 0 2-2v-6a2.073 2.073 0 0 0 -2-2zm-5.391 5.94a1.609 1.609 0 0 1 -3.217 0v-1.876a1.609 1.609 0 0 1 3.217 0z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,8 +0,0 @@
|
|||||||
<svg height="16" viewBox="0 0 42 42" width="16" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<style id="current-color-scheme" type="text/css">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#232629;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<path d="m32.66 29.319a1.432 1.432 0 0 0 -.66-.319h-1.5a8.125 8.125 0 0 0 1.5-5 11.027 11.027 0 0 0 -.5-3 2.519 2.519 0 0 1 0-1.5 12.987 12.987 0 0 0 .5-3.5c-6.681-1.955-11 0-11 0s-4.313-1.936-11 0a13.272 13.272 0 0 0 .5 3.5 2.519 2.519 0 0 1 0 1.5 11.1 11.1 0 0 0 -.5 3 8.035 8.035 0 0 0 1.5 5h-1.5a1.868 1.868 0 0 0 -.783.319 3.477 3.477 0 0 0 -1.636 2.157 7.058 7.058 0 0 0 -.234 3.18c.307 2.008 1.329 3.344 2.653 3.344h4a.379.379 0 0 0 .277-.187l1.793-1.483a3.263 3.263 0 0 0 .729-2.074 7.857 7.857 0 0 0 .027-.878 23.166 23.166 0 0 0 4.174 1.622 24.4 24.4 0 0 0 4.051-1.614 7.848 7.848 0 0 0 .027.869 3.263 3.263 0 0 0 .729 2.074l1.793 1.484a.61.61 0 0 0 .4.187h4c1.324 0 2.223-1.336 2.529-3.343a7.057 7.057 0 0 0 -.234-3.18 3.477 3.477 0 0 0 -1.635-2.158zm-17.069 6.5c-.019.009-.936.775-1.458 1.208a.418.418 0 0 1 -.627-.111 9.322 9.322 0 0 1 -.3-5.974 15.843 15.843 0 0 0 2.894 2.048c.051 1.03-.161 2.644-.509 2.834zm6.409-6.819h-2l.5-5a2 2 0 1 1 1 0zm6.38 7.921a.418.418 0 0 1 -.627.111c-.522-.433-1.439-1.2-1.458-1.208-.348-.189-.56-1.8-.505-2.828a15.84 15.84 0 0 0 2.9-2.037 9.322 9.322 0 0 1 -.31 5.962zm-20.924-11.162.744-1.366a1.579 1.579 0 0 0 -.019-1.557l.653-1.2c.2.014-.03-.113.165-.14.051-.217-.051-.336 0-.5a3.269 3.269 0 0 0 0-1.5 7.151 7.151 0 0 1 0-3 2.366 2.366 0 0 0 -2.378 1.448 2.409 2.409 0 0 0 .229 2.661l-.7 1.278a1.779 1.779 0 0 0 -1.317.891l-.741 1.372a1.577 1.577 0 0 0 -.019 1.487 3.028 3.028 0 0 0 -2.746 1.525 2.648 2.648 0 0 0 .044 2.631.748.748 0 0 0 .981.266.656.656 0 0 0 .284-.92 1.37 1.37 0 0 1 -.023-1.361 1.6 1.6 0 0 1 2.079-.63 1.408 1.408 0 0 1 .672 1.95 1.546 1.546 0 0 1 -1.2.78.688.688 0 0 0 -.636.749.707.707 0 0 0 .717.6.789.789 0 0 0 .082 0 2.989 2.989 0 0 0 2.322-1.513 2.669 2.669 0 0 0 -.377-3.084 1.767 1.767 0 0 0 1.184-.867zm33.217 1.2a3.021 3.021 0 0 0 -2.658-1.525 1.574 1.574 0 0 0 -.107-1.283l-.745-1.367a1.779 1.779 0 0 0 -1.317-.891l-.7-1.278a2.409 2.409 0 0 0 .229-2.661 2.283 2.283 0 0 0 -2.375-1.454 7.039 7.039 0 0 1 0 3 3.272 3.272 0 0 0 0 1.5c.047.152-.047.3 0 .5.227.04-.069.156.165.14l.653 1.2a1.579 1.579 0 0 0 -.019 1.557l.745 1.367a1.753 1.753 0 0 0 1.045.832 2.66 2.66 0 0 0 -.238 2.916 2.989 2.989 0 0 0 2.326 1.509.79.79 0 0 0 .082 0 .707.707 0 0 0 .717-.6.688.688 0 0 0 -.636-.749 1.546 1.546 0 0 1 -1.2-.78 1.408 1.408 0 0 1 .672-1.95 1.628 1.628 0 0 1 1.179-.089 1.512 1.512 0 0 1 .9.719 1.37 1.37 0 0 1 -.023 1.361.656.656 0 0 0 .284.92.748.748 0 0 0 .981-.266 2.648 2.648 0 0 0 .04-2.633zm-19.673-11.959a13.013 13.013 0 0 1 5-1 21.6 21.6 0 0 1 4.5.5 9.312 9.312 0 0 0 -9.5-8.5c-5.794 0-9.176 4-9.5 8.5a21.858 21.858 0 0 1 4.5-.5 12.819 12.819 0 0 1 5 1zm3.5-5c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2zm-7 0c1.209 0 2.5.866 2.5 2h-5c0-1.134 1.291-2 2.5-2z" fill-rule="evenodd" style="fill:#f2f2f2;fill-opacity:1" class="ColorScheme-Text" fill="currentColor"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB |
Vendored
+1
-1
@@ -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-21 (>= 21.0.2+12-0ppa1), libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-20, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
|
|||||||
Vendored
+2
-2
@@ -4,11 +4,11 @@ Upstream-Contact: Cryptomator <info@cryptomator.org>
|
|||||||
Source: https://cryptomator.org
|
Source: https://cryptomator.org
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: 2016-2024 Skymatic GmbH
|
Copyright: 2016-2023 Skymatic GmbH
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: debian/org.cryptomator.Cryptomator.appdata.xml
|
Files: debian/org.cryptomator.Cryptomator.appdata.xml
|
||||||
Copyright: 2016-2024 Skymatic GmbH
|
Copyright: 2016-2023 Skymatic GmbH
|
||||||
License: FSFAP
|
License: FSFAP
|
||||||
|
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|||||||
-2
@@ -1,8 +1,6 @@
|
|||||||
cryptomator usr/lib
|
cryptomator usr/lib
|
||||||
common/org.cryptomator.Cryptomator.desktop usr/share/applications
|
common/org.cryptomator.Cryptomator.desktop usr/share/applications
|
||||||
common/org.cryptomator.Cryptomator.svg usr/share/icons/hicolor/scalable/apps
|
common/org.cryptomator.Cryptomator.svg usr/share/icons/hicolor/scalable/apps
|
||||||
common/org.cryptomator.Cryptomator.tray.svg usr/share/icons/hicolor/scalable/apps
|
|
||||||
common/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/scalable/apps
|
|
||||||
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
|
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
|
||||||
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
|
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
|
||||||
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
|
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
|
||||||
|
|||||||
Vendored
-2
@@ -1,3 +1 @@
|
|||||||
usr/lib/cryptomator/bin/cryptomator usr/bin/cryptomator
|
usr/lib/cryptomator/bin/cryptomator usr/bin/cryptomator
|
||||||
usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
|
|
||||||
usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
|
|
||||||
Vendored
+6
-9
@@ -4,7 +4,7 @@
|
|||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
|
JAVA_HOME = /usr/lib/jvm/java-20-coffeelibs
|
||||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||||
@@ -24,16 +24,15 @@ override_dh_auto_clean:
|
|||||||
override_dh_auto_build:
|
override_dh_auto_build:
|
||||||
mkdir resources
|
mkdir resources
|
||||||
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
|
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
|
||||||
# Remark: no compression is applied for improved build compression later (here deb)
|
|
||||||
$(JAVA_HOME)/bin/jlink \
|
$(JAVA_HOME)/bin/jlink \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JMODS_PATH}" \
|
--module-path "${JMODS_PATH}" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
--compress zip-0
|
--compress=2
|
||||||
$(JAVA_HOME)/bin/jpackage \
|
$(JAVA_HOME)/bin/jpackage \
|
||||||
--type app-image \
|
--type app-image \
|
||||||
--runtime-image runtime \
|
--runtime-image runtime \
|
||||||
@@ -44,8 +43,8 @@ override_dh_auto_build:
|
|||||||
--name cryptomator \
|
--name cryptomator \
|
||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64" \
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
@@ -56,11 +55,9 @@ override_dh_auto_build:
|
|||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
--java-options "-Dcryptomator.showTrayIcon=false" \
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \
|
|
||||||
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
||||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
|
||||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||||
--resource-dir resources \
|
--resource-dir resources \
|
||||||
--verbose
|
--verbose
|
||||||
|
|||||||
Vendored
+5
-29
@@ -21,7 +21,7 @@ rm -rf runtime dmg *.app *.dmg
|
|||||||
# set variables
|
# set variables
|
||||||
APP_NAME="Cryptomator"
|
APP_NAME="Cryptomator"
|
||||||
VENDOR="Skymatic GmbH"
|
VENDOR="Skymatic GmbH"
|
||||||
COPYRIGHT_YEARS="2016 - 2024"
|
COPYRIGHT_YEARS="2016 - 2023"
|
||||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
@@ -29,14 +29,6 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||||
FUSE_LIB="FUSE-T"
|
FUSE_LIB="FUSE-T"
|
||||||
|
|
||||||
ARCH="undefined"
|
|
||||||
if [ "$(machine)" = "arm64e" ]; then
|
|
||||||
ARCH="aarch64"
|
|
||||||
else
|
|
||||||
ARCH="x64"
|
|
||||||
fi
|
|
||||||
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-${ARCH}_bin-jmods.zip"
|
|
||||||
|
|
||||||
# check preconditions
|
# check preconditions
|
||||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||||
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; }
|
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; }
|
||||||
@@ -46,37 +38,20 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
|
|||||||
if [[ ! `security find-identity -v -p codesigning | grep -w "${CODESIGN_IDENTITY}"` ]]; then echo "Given codesign identity is invalid."; exit 1; fi
|
if [[ ! `security find-identity -v -p codesigning | grep -w "${CODESIGN_IDENTITY}"` ]]; then echo "Given codesign identity is invalid."; exit 1; fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# download and check jmods
|
|
||||||
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
|
|
||||||
mkdir -p openjfx-jmods/
|
|
||||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
|
||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
|
||||||
POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
|
||||||
|
|
||||||
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
|
||||||
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
||||||
cp ../../../LICENSE.txt ../../../target
|
|
||||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||||
|
|
||||||
# add runtime
|
# add runtime
|
||||||
${JAVA_HOME}/bin/jlink \
|
${JAVA_HOME}/bin/jlink \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
--module-path "${JAVA_HOME}/jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
--compress zip-0
|
--compress=1
|
||||||
|
|
||||||
# create app dir
|
# create app dir
|
||||||
${JAVA_HOME}/bin/jpackage \
|
${JAVA_HOME}/bin/jpackage \
|
||||||
@@ -169,5 +144,6 @@ create-dmg \
|
|||||||
--app-drop-link 512 245 \
|
--app-drop-link 512 245 \
|
||||||
--eula "resources/license.rtf" \
|
--eula "resources/license.rtf" \
|
||||||
--icon ".background" 128 758 \
|
--icon ".background" 128 758 \
|
||||||
|
--icon ".fseventsd" 320 758 \
|
||||||
--icon ".VolumeIcon.icns" 512 758 \
|
--icon ".VolumeIcon.icns" 512 758 \
|
||||||
${APP_NAME}-${VERSION_NO}.dmg dmg
|
${APP_NAME}-${VERSION_NO}.dmg dmg
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
\f1\b0 \
|
\f1\b0 \
|
||||||
\
|
\
|
||||||
|
|
||||||
\f0\b \'a9 2016 \'96 2024 Skymatic GmbH
|
\f0\b \'a9 2016 \'96 2023 Skymatic GmbH
|
||||||
\f1\b0 \
|
\f1\b0 \
|
||||||
\
|
\
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -4,6 +4,4 @@ installer
|
|||||||
*.wixobj
|
*.wixobj
|
||||||
*.pdb
|
*.pdb
|
||||||
*.msi
|
*.msi
|
||||||
*.exe
|
|
||||||
*.jmod
|
|
||||||
license.rtf
|
license.rtf
|
||||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ SET HELP_URL="https://cryptomator.org/contact/"
|
|||||||
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
SET LOOPBACK_ALIAS="cryptomator-vault"
|
SET LOOPBACK_ALIAS="cryptomator-vault"
|
||||||
|
|
||||||
powershell -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
|
powershell -NoLogo -ExecutionPolicy Unrestricted -Command .\build.ps1^
|
||||||
-AppName %APPNAME%^
|
-AppName %APPNAME%^
|
||||||
-MainJarGlob "%MAIN_JAR_GLOB%"^
|
-MainJarGlob "%MAIN_JAR_GLOB%"^
|
||||||
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
|
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
|
||||||
|
|||||||
Vendored
+8
-17
@@ -51,11 +51,10 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
## download jfx jmods
|
## download jfx jmods
|
||||||
$jmodsVersion='21.0.1'
|
$jfxJmodsChecksum = 'd00767334c43b8832b5cf10267d34ca8f563d187c4655b73eb6020dd79c054b5'
|
||||||
$jmodsUrl = "https://download2.gluonhq.com/openjfx/${jmodsVersion}/openjfx-${jmodsVersion}_windows-x64_bin-jmods.zip"
|
|
||||||
$jfxJmodsChecksum = 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
|
||||||
$jfxJmodsZip = '.\resources\jfxJmods.zip'
|
$jfxJmodsZip = '.\resources\jfxJmods.zip'
|
||||||
if( !(Test-Path -Path $jfxJmodsZip) ) {
|
if( !(Test-Path -Path $jfxJmodsZip) ) {
|
||||||
|
$jmodsUrl = "https://download2.gluonhq.com/openjfx/20.0.1/openjfx-20.0.1_windows-x64_bin-jmods.zip"
|
||||||
Write-Output "Downloading ${jmodsUrl}..."
|
Write-Output "Downloading ${jmodsUrl}..."
|
||||||
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
|
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
|
||||||
}
|
}
|
||||||
@@ -63,23 +62,21 @@ if( !(Test-Path -Path $jfxJmodsZip) ) {
|
|||||||
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
|
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
|
||||||
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
|
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
|
||||||
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
|
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
|
Expand-Archive -Force -Path $jfxJmodsZip -DestinationPath ".\resources\"
|
||||||
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
|
|
||||||
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
|
||||||
|
|
||||||
## create custom runtime
|
|
||||||
& "$Env:JAVA_HOME\bin\jlink" `
|
& "$Env:JAVA_HOME\bin\jlink" `
|
||||||
--verbose `
|
--verbose `
|
||||||
--output runtime `
|
--output runtime `
|
||||||
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
|
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods-20.0.1" `
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,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 `
|
||||||
--strip-debug `
|
--strip-debug `
|
||||||
--compress "zip-0" #do not compress to have improved msi compression
|
--compress=1
|
||||||
|
|
||||||
$appPath = ".\$AppName"
|
$appPath = ".\$AppName"
|
||||||
if ($clean -and (Test-Path -Path $appPath)) {
|
if ($clean -and (Test-Path -Path $appPath)) {
|
||||||
@@ -144,7 +141,6 @@ try {
|
|||||||
|
|
||||||
# create .msi
|
# create .msi
|
||||||
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
||||||
$Env:JP_WIXHELPER_DIR = "."
|
|
||||||
& "$Env:JAVA_HOME\bin\jpackage" `
|
& "$Env:JAVA_HOME\bin\jpackage" `
|
||||||
--verbose `
|
--verbose `
|
||||||
--type msi `
|
--type msi `
|
||||||
@@ -176,15 +172,10 @@ $Env:JP_WIXHELPER_DIR = "."
|
|||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
||||||
|
|
||||||
# download Winfsp
|
# download Winfsp
|
||||||
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
$winfspMsiUrl= (Select-String -Path ".\bundle\resources\winFspMetaData.wxi" -Pattern '<\?define BundledWinFspDownloadLink="(.+)".*?>').Matches.Groups[1].Value
|
||||||
Write-Output "Downloading ${winfspMsiUrl}..."
|
Write-Output "Downloading ${winfspMsiUrl}..."
|
||||||
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
|
||||||
|
|
||||||
# download legacy-winfsp uninstaller
|
|
||||||
$winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
|
||||||
Write-Output "Downloading ${winfspUninstaller}..."
|
|
||||||
Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
|
|
||||||
|
|
||||||
# copy MSI to bundle resources
|
# copy MSI to bundle resources
|
||||||
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
|
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
|
||||||
|
|
||||||
|
|||||||
Vendored
+20
-25
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
<!-- For Built in variables, see https://wixtoolset.org/docs/tools/burn/builtin-variables/-->
|
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||||
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html-->
|
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/bundle.html-->
|
||||||
<!-- Attributes explicitly not used:
|
<!-- Attributes explicitly not used:
|
||||||
@@ -11,10 +10,21 @@
|
|||||||
AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico">
|
AboutUrl="$(var.AboutUrl)" HelpUrl="$(var.HelpUrl)" UpdateUrl="$(var.UpdateUrl)" Copyright="$(var.BundleCopyright)" IconSourceFile="bundle\resources\Cryptomator.ico">
|
||||||
|
|
||||||
<!-- detect outdated WinFsp installations -->
|
<!-- detect outdated WinFsp installations -->
|
||||||
|
<?include "resources\winFspMetaData.wxi" ?>
|
||||||
<util:ProductSearch
|
<util:ProductSearch
|
||||||
Variable="InstalledLegacyWinFspVersion"
|
Variable="InstalledWinFspVersion"
|
||||||
Result="version"
|
Result="version"
|
||||||
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"/>
|
UpgradeCode="82F812D9-4083-4EF1-8BC8-0F1EDA05B46B"
|
||||||
|
/>
|
||||||
|
<!-- Note: The bundle engine takes the Message format literaly -->
|
||||||
|
<bal:Condition Message=
|
||||||
|
"The WinFsp driver used by Cryptomator is outdated and must be removed before the installation.
|
||||||
|
|
||||||
|
1. Open the view of installed apps
|
||||||
|
2. Search for "WinFsp"
|
||||||
|
3. Uninstall the listed application
|
||||||
|
4. Reboot your device
|
||||||
|
5. Restart this installer">(InstalledWinFspVersion = v0.0.0.0) OR ($(var.BundledWinFspVersion) <= InstalledWinFspVersion)</bal:Condition>
|
||||||
|
|
||||||
<!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/-->
|
<!-- for definition of the standard themes, see https://github.com/wixtoolset/wix3/blob/master/src/ext/BalExtension/wixstdba/Resources/-->
|
||||||
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense">
|
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLargeLicense">
|
||||||
@@ -26,41 +36,26 @@
|
|||||||
SuppressOptionsUI="yes"
|
SuppressOptionsUI="yes"
|
||||||
ThemeFile="bundle\customBootstrapperTheme.xml"
|
ThemeFile="bundle\customBootstrapperTheme.xml"
|
||||||
LocalizationFile="bundle\customBootstrapperTheme.wxl"
|
LocalizationFile="bundle\customBootstrapperTheme.wxl"
|
||||||
LogoFile="bundle\resources\logo.png"/>
|
LogoFile="bundle\resources\logo.png"
|
||||||
|
/>
|
||||||
<Payload SourceFile="bundle\resources\logoSide.png" />
|
<Payload SourceFile="bundle\resources\logoSide.png" />
|
||||||
</BootstrapperApplicationRef>
|
</BootstrapperApplicationRef>
|
||||||
|
|
||||||
<Chain>
|
<Chain>
|
||||||
<ExePackage Cache="yes" PerMachine="yes" Permanent="no"
|
|
||||||
SourceFile="resources\winfsp-uninstaller.exe"
|
|
||||||
DisplayName="Removing outdated WinFsp Driver"
|
|
||||||
Description="Executable to remove old winfsp"
|
|
||||||
DetectCondition="false"
|
|
||||||
InstallCondition="(InstalledLegacyWinFspVersion <> v0.0.0.0) AND ((WixBundleAction = 7) OR (WixBundleAction = 5))">
|
|
||||||
<CommandLine Condition="WixBundleUILevel <= 3" InstallArgument="-q -l "[WixBundleLog].winfsp-uninstaller.log"" RepairArgument="-q" UninstallArgument="-s" />
|
|
||||||
<!-- XML allows line breaks in attributes, hence keep the line breaks here -->
|
|
||||||
<CommandLine Condition="WixBundleUILevel > 3" InstallArgument="-l "[WixBundleLog].winfsp-uninstaller.log" -t "Cryptomator Installer" -m "Cryptomator requires a newer version of the WinFsp driver. The installer will now uninstall WinFsp, possibly reboot, and afterwards proceed with the installation.
|
|
||||||
|
|
||||||
Do you want to continue?"" RepairArgument="-q" UninstallArgument="-s" />
|
|
||||||
<ExitCode Behavior="success" Value="0"/>
|
|
||||||
<ExitCode Behavior="success" Value="1"/>
|
|
||||||
<ExitCode Behavior="error" Value="2"/>
|
|
||||||
<ExitCode Behavior="error" Value="3"/>
|
|
||||||
<ExitCode Behavior="forceReboot" Value="4"/>
|
|
||||||
<ExitCode Behavior="success" Value="5"/>
|
|
||||||
</ExePackage>
|
|
||||||
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html-->
|
<!-- see https://wixtoolset.org/documentation/manual/v3/xsd/wix/msipackage.html-->
|
||||||
<MsiPackage
|
<MsiPackage
|
||||||
SourceFile="resources\Cryptomator.msi"
|
SourceFile="resources\Cryptomator.msi"
|
||||||
CacheId="cryptomator-bundle-cryptomator"
|
CacheId="cryptomator-bundle-cryptomator"
|
||||||
DisplayInternalUI="no"
|
DisplayInternalUI="no"
|
||||||
Visible="no"/>
|
Visible="no"
|
||||||
|
/>
|
||||||
<MsiPackage
|
<MsiPackage
|
||||||
SourceFile="resources\winfsp.msi"
|
SourceFile="resources\winfsp.msi"
|
||||||
CacheId="cryptomator-bundle-winfsp"
|
CacheId="cryptomator-bundle-winfsp"
|
||||||
Visible="yes"
|
Visible="yes"
|
||||||
DisplayInternalUI="no"
|
DisplayInternalUI="no"
|
||||||
Permanent="yes"/>
|
Vital="no"
|
||||||
|
Permanent="yes"
|
||||||
|
/>
|
||||||
</Chain>
|
</Chain>
|
||||||
</Bundle>
|
</Bundle>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
\vieww12000\viewh15840\viewkind0
|
\vieww12000\viewh15840\viewkind0
|
||||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||||
\par
|
\par
|
||||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
|
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
|
||||||
\par
|
\par
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||||
\par
|
\par
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<!-- A version number MUST be prefixed with letter "v", otherwise it is considered a normal string -->
|
||||||
|
<?define BundledWinFspVersion="v1.12.22339" ?>
|
||||||
|
<?define BundledWinFspDownloadLink="https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi" ?> <!-- Only used by external build scripts -->
|
||||||
|
</Include>
|
||||||
Vendored
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
::REPLACE ME
|
::REPLACE ME
|
||||||
|
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\patchWebDAV.ps1^
|
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
|
||||||
-LoopbackAlias %LOOPBACK_ALIAS%
|
-LoopbackAlias %LOOPBACK_ALIAS%
|
||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
:: see comments in file ./version170-migrate-settings.ps1
|
:: see comments in file ./version170-migrate-settings.ps1
|
||||||
|
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1
|
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\version170-migrate-settings.ps1
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
\vieww12000\viewh15840\viewkind0
|
\vieww12000\viewh15840\viewkind0
|
||||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||||
\par
|
\par
|
||||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
|
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
|
||||||
\par
|
\par
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||||
\par
|
\par
|
||||||
|
|||||||
Vendored
+3
-9
@@ -70,7 +70,7 @@
|
|||||||
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll"/>
|
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
|
||||||
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||||
|
|
||||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||||
@@ -132,17 +132,11 @@
|
|||||||
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
|
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
|
||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<Property Id="WixQuietExec64CmdTimeout" Value="20" />
|
|
||||||
<!-- Note for custom actions: Immediate CAs run BEFORE the files are installed, hence if you depend on installed files, the CAs must be deferred.-->
|
|
||||||
<!-- WebDAV patches -->
|
<!-- WebDAV patches -->
|
||||||
<SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat""
|
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
|
||||||
Sequence="execute" Before="PatchWebDAV" />
|
|
||||||
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
|
||||||
|
|
||||||
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
|
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
|
||||||
<SetProperty Id="V170MigrateSettings" Value=""[INSTALLDIR]version170-migrate-settings.bat""
|
<CustomAction Id="V170MigrateSettings" Impersonate="no" ExeCommand="[INSTALLDIR]version170-migrate-settings.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
|
||||||
Sequence="execute" Before="V170MigrateSettings" />
|
|
||||||
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
|
||||||
|
|
||||||
<!-- Running App detection and exit -->
|
<!-- Running App detection and exit -->
|
||||||
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
||||||
|
|||||||
@@ -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.12.2</version>
|
<version>1.10.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -26,52 +26,45 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.jdk.version>21</project.jdk.version>
|
<project.jdk.version>20</project.jdk.version>
|
||||||
|
|
||||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||||
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
<!-- Once hypfvieh, swiesend, purejava and integrations-linux have module-info, remove them-->
|
||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>2.6.8</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.6.6</cryptomator.cryptofs.version>
|
||||||
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.2.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.2.5</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.2.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.3</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.2.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.4.4</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.2.1</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>4.0.0</cryptomator.fuse.version>
|
<cryptomator.fuse.version>3.0.0</cryptomator.fuse.version>
|
||||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||||
<cryptomator.webdav.version>2.0.6</cryptomator.webdav.version>
|
<cryptomator.webdav.version>2.0.3</cryptomator.webdav.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
<dagger.version>2.50</dagger.version>
|
<dagger.version>2.45</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<guava.version>33.0.0-jre</guava.version>
|
<guava.version>32.0.1-jre</guava.version>
|
||||||
<jackson.version>2.16.1</jackson.version>
|
<jackson.version>2.15.2</jackson.version>
|
||||||
<javafx.version>21.0.1</javafx.version>
|
<javafx.version>20.0.1</javafx.version>
|
||||||
<jwt.version>4.4.0</jwt.version>
|
<jwt.version>4.4.0</jwt.version>
|
||||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
<nimbus-jose.version>9.31</nimbus-jose.version>
|
||||||
<logback.version>1.4.14</logback.version>
|
<logback.version>1.4.7</logback.version>
|
||||||
<slf4j.version>2.0.11</slf4j.version>
|
<slf4j.version>2.0.7</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
<tinyoauth2.version>0.5.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.8.2</zxcvbn.version>
|
<zxcvbn.version>1.7.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.10.2</junit.jupiter.version>
|
<junit.jupiter.version>5.9.3</junit.jupiter.version>
|
||||||
<mockito.version>5.10.0</mockito.version>
|
<mockito.version>5.3.1</mockito.version>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>2.2</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
|
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
||||||
<dependency-check.version>9.0.9</dependency-check.version>
|
<dependency-check.version>8.1.2</dependency-check.version>
|
||||||
<jacoco.version>0.8.11</jacoco.version>
|
<jacoco.version>0.8.9</jacoco.version>
|
||||||
<license-generator.version>2.4.0</license-generator.version>
|
|
||||||
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
|
|
||||||
<mvn-compiler.version>3.12.1</mvn-compiler.version>
|
|
||||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
|
||||||
<mvn-dependency.version>3.6.1</mvn-dependency.version>
|
|
||||||
<mvn-surefire.version>3.2.5</mvn-surefire.version>
|
|
||||||
<mvn-jar.version>3.3.0</mvn-jar.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -247,7 +240,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.jimfs</groupId>
|
<groupId>com.google.jimfs</groupId>
|
||||||
<artifactId>jimfs</artifactId>
|
<artifactId>jimfs</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -265,32 +258,32 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>${mvn-compiler.version}</version>
|
<version>3.10.1</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<version>${mvn-resources.version}</version>
|
<version>3.3.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
<version>${mvn-dependency.version}</version>
|
<version>3.3.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>${mvn-surefire.version}</version>
|
<version>3.0.0-M7</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>license-maven-plugin</artifactId>
|
<artifactId>license-maven-plugin</artifactId>
|
||||||
<version>${license-generator.version}</version>
|
<version>2.0.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>${mvn-jar.version}</version>
|
<version>3.3.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
@@ -339,22 +332,8 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>me.fabriciorby</groupId>
|
|
||||||
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
|
|
||||||
<version>${junit-tree-reporter.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>--enable-preview</argLine>
|
<argLine>--enable-preview</argLine>
|
||||||
<reportFormat>plain</reportFormat>
|
|
||||||
<consoleOutputReporter>
|
|
||||||
<disable>true</disable>
|
|
||||||
</consoleOutputReporter>
|
|
||||||
<statelessTestsetInfoReporter
|
|
||||||
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
|
|
||||||
</statelessTestsetInfoReporter>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -460,19 +439,17 @@
|
|||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-maven</artifactId>
|
<artifactId>dependency-check-maven</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<nvdValidForHours>24</nvdValidForHours>
|
<cveValidForHours>24</cveValidForHours>
|
||||||
<failBuildOnCVSS>0</failBuildOnCVSS>
|
<failBuildOnCVSS>0</failBuildOnCVSS>
|
||||||
<skipTestScope>true</skipTestScope>
|
<skipTestScope>true</skipTestScope>
|
||||||
<detail>true</detail>
|
<detail>true</detail>
|
||||||
<suppressionFile>suppression.xml</suppressionFile>
|
<suppressionFile>suppression.xml</suppressionFile>
|
||||||
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>check</goal>
|
<goal>check</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<phase>validate</phase>
|
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import com.tobiasdiez.easybind.EasyBind;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.keychain.KeychainModule;
|
import org.cryptomator.common.keychain.KeychainModule;
|
||||||
import org.cryptomator.common.mount.MountModule;
|
import org.cryptomator.common.mount.MountModule;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
@@ -20,6 +22,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -132,4 +136,13 @@ public abstract class CommonsModule {
|
|||||||
LOG.error("Uncaught exception in " + thread.getName(), throwable);
|
LOG.error("Uncaught exception in " + thread.getName(), throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static ObservableValue<InetSocketAddress> provideServerSocketAddressBinding(Settings settings) {
|
||||||
|
return settings.port.map(port -> {
|
||||||
|
String host = SystemUtils.IS_OS_WINDOWS ? "127.0.0.1" : "localhost";
|
||||||
|
return InetSocketAddress.createUnresolved(host, settings.port.intValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.cryptomator.common;
|
|||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ public class Environment {
|
|||||||
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||||
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||||
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||||
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
|
||||||
|
|
||||||
private Environment() {}
|
private Environment() {}
|
||||||
|
|
||||||
@@ -45,16 +43,15 @@ public class Environment {
|
|||||||
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
|
||||||
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
||||||
|
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
|
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
|
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
|
logCryptomatorSystemProperty(APP_VERSION_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
|
logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
|
||||||
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Environment getInstance() {
|
public static Environment getInstance() {
|
||||||
@@ -77,6 +74,10 @@ public class Environment {
|
|||||||
return getPaths(SETTINGS_PATH_PROP_NAME);
|
return getPaths(SETTINGS_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getP12Path() {
|
||||||
|
return getPaths(P12_PATH_PROP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
public Stream<Path> getIpcSocketPath() {
|
public Stream<Path> getIpcSocketPath() {
|
||||||
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
return getPaths(IPC_SOCKET_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
@@ -85,10 +86,6 @@ public class Environment {
|
|||||||
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<Path> getP12Path() {
|
|
||||||
return getPaths(P12_PATH_PROP_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Path> getLogDir() {
|
public Optional<Path> getLogDir() {
|
||||||
return getPath(LOG_DIR_PROP_NAME);
|
return getPath(LOG_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
@@ -97,12 +94,12 @@ public class Environment {
|
|||||||
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
|
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Path> getMountPointsDir() {
|
public Optional<Path> getPluginDir() {
|
||||||
return getPath(MOUNTPOINT_DIR_PROP_NAME);
|
return getPath(PLUGIN_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMinPwLength() {
|
public Optional<Path> getMountPointsDir() {
|
||||||
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
|
return getPath(MOUNTPOINT_DIR_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,24 +115,20 @@ public class Environment {
|
|||||||
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
|
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Path> getPluginDir() {
|
public int getMinPwLength() {
|
||||||
return getPath(PLUGIN_DIR_PROP_NAME);
|
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean showTrayIcon() {
|
public boolean showTrayIcon() {
|
||||||
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
|
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean disableUpdateCheck() {
|
|
||||||
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Path> getPath(String propertyName) {
|
private Optional<Path> getPath(String propertyName) {
|
||||||
String value = System.getProperty(propertyName);
|
String value = System.getProperty(propertyName);
|
||||||
return Optional.ofNullable(value).map(Paths::get);
|
return Optional.ofNullable(value).map(Paths::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
Stream<Path> getPaths(String propertyName) {
|
Stream<Path> getPaths(String propertyName) {
|
||||||
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
|
Stream<String> rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0));
|
||||||
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
|
return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.cryptomator.common;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -115,7 +114,7 @@ public class ErrorCode {
|
|||||||
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
|
* @param bottomFrames Other stack frames, potentially forming the bottom of the stack of <code>allFrames</code>
|
||||||
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
|
* @return The number of additional frames in <code>allFrames</code>. In most cases this should be equal to the difference in size.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
|
static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) {
|
||||||
if (allFrames.length < bottomFrames.length) {
|
if (allFrames.length < bottomFrames.length) {
|
||||||
// if frames had been stacked on top of bottomFrames, allFrames would be larger
|
// if frames had been stacked on top of bottomFrames, allFrames would be larger
|
||||||
@@ -125,7 +124,7 @@ public class ErrorCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
static <T> int commonSuffixLength(T[] set, T[] subset) {
|
static <T> int commonSuffixLength(T[] set, T[] subset) {
|
||||||
Preconditions.checkArgument(set.length >= subset.length);
|
Preconditions.checkArgument(set.length >= subset.length);
|
||||||
// iterate items backwards as long as they are identical
|
// iterate items backwards as long as they are identical
|
||||||
|
|||||||
+3
-3
@@ -62,7 +62,7 @@ public final class OneDriveWindowsLocationPresetsProvider implements LocationPre
|
|||||||
ProcessBuilder command = new ProcessBuilder(args);
|
ProcessBuilder command = new ProcessBuilder(args);
|
||||||
Process p = command.start();
|
Process p = command.start();
|
||||||
waitForSuccess(p, 3, "`reg query`");
|
waitForSuccess(p, 3, "`reg query`");
|
||||||
return p.inputReader(StandardCharsets.ISO_8859_1).lines().filter(outputFilter);
|
return p.inputReader(StandardCharsets.UTF_8).lines().filter(outputFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -83,8 +83,8 @@ public final class OneDriveWindowsLocationPresetsProvider implements LocationPre
|
|||||||
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
|
throw new TimeoutException(cmdDescription + " timed out after " + timeoutSeconds + "s");
|
||||||
}
|
}
|
||||||
if (process.exitValue() != 0) {
|
if (process.exitValue() != 0) {
|
||||||
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.ISO_8859_1).lines().collect(Collectors.joining("\n"));
|
@SuppressWarnings("resource") var stdout = process.inputReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.ISO_8859_1).lines().collect(Collectors.joining("\n"));
|
@SuppressWarnings("resource") var stderr = process.errorReader(StandardCharsets.UTF_8).lines().collect(Collectors.joining("\n"));
|
||||||
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
|
throw new CommandFailedException(cmdDescription, process.exitValue(), stdout, stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.mount.MountService;
|
||||||
|
|
||||||
|
public record ActualMountService(MountService service, boolean isDesired) {
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.cryptomator.common.mount;
|
|
||||||
|
|
||||||
import org.cryptomator.integrations.mount.MountFailedException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown by {@link Mounter} to indicate that the selected mount service can not be used
|
|
||||||
* due to incompatibilities with a different mount service that is already in use.
|
|
||||||
*/
|
|
||||||
public class ConflictingMountServiceException extends MountFailedException {
|
|
||||||
|
|
||||||
public ConflictingMountServiceException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.cryptomator.common.mount;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class HideawayNotDirectoryException extends IllegalMountPointException {
|
|
||||||
|
|
||||||
private final Path hideaway;
|
|
||||||
|
|
||||||
public HideawayNotDirectoryException(Path path, Path hideaway) {
|
|
||||||
super(path, "Existing hideaway (" + hideaway.toString() + ") for mountpoint is not a directory: " + path.toString());
|
|
||||||
this.hideaway = hideaway;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getHideaway() {
|
|
||||||
return hideaway;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,9 @@
|
|||||||
package org.cryptomator.common.mount;
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that validation or preparation of a mountpoint failed due to a configuration error or an invalid system state.<br>
|
|
||||||
* Instances of this exception are usually caught and displayed to the user in an appropriate fashion, e.g. by {@link org.cryptomator.ui.unlock.UnlockInvalidMountPointController UnlockInvalidMountPointController.}
|
|
||||||
*/
|
|
||||||
public class IllegalMountPointException extends IllegalArgumentException {
|
public class IllegalMountPointException extends IllegalArgumentException {
|
||||||
|
|
||||||
private final Path mountpoint;
|
public IllegalMountPointException(String msg) {
|
||||||
|
|
||||||
public IllegalMountPointException(Path mountpoint) {
|
|
||||||
this(mountpoint, "The provided mountpoint has a problem: " + mountpoint.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IllegalMountPointException(Path mountpoint, String msg) {
|
|
||||||
super(msg);
|
super(msg);
|
||||||
this.mountpoint = mountpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getMountpoint() {
|
}
|
||||||
return mountpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,18 +4,21 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import org.cryptomator.common.ObservableUtil;
|
import org.cryptomator.common.ObservableUtil;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.integrations.mount.Mount;
|
||||||
import org.cryptomator.integrations.mount.MountService;
|
import org.cryptomator.integrations.mount.MountService;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class MountModule {
|
public class MountModule {
|
||||||
|
|
||||||
|
private static final AtomicReference<MountService> formerSelectedMountService = new AtomicReference<>(null);
|
||||||
|
private static final List<String> problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static List<MountService> provideSupportedMountServices() {
|
static List<MountService> provideSupportedMountServices() {
|
||||||
@@ -24,18 +27,46 @@ public class MountModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static ObservableValue<MountService> provideDefaultMountService(List<MountService> mountProviders, Settings settings) {
|
@Named("FUPFMS")
|
||||||
var fallbackProvider = mountProviders.stream().findFirst().get(); //there should always be a mount provider, at least webDAV
|
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
|
||||||
return ObservableUtil.mapWithDefault(settings.mountService, //
|
return new AtomicReference<>(null);
|
||||||
serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), //
|
|
||||||
fallbackProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Named("usedMountServices")
|
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
|
||||||
static Set<MountService> provideSetOfUsedMountServices() {
|
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
||||||
return ConcurrentHashMap.newKeySet();
|
|
||||||
|
var observableMountService = ObservableUtil.mapWithDefault(settings.mountService, //
|
||||||
|
desiredServiceImpl -> { //
|
||||||
|
var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); //
|
||||||
|
var targetedService = serviceFromSettings.orElse(fallbackProvider);
|
||||||
|
return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms);
|
||||||
|
}, //
|
||||||
|
() -> { //
|
||||||
|
return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms);
|
||||||
|
});
|
||||||
|
return observableMountService;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
//see https://github.com/cryptomator/cryptomator/issues/2786
|
||||||
|
private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference<MountService> firstUsedProblematicFuseMountService) {
|
||||||
|
//set the first used problematic fuse service if applicable
|
||||||
|
var targetIsProblematicFuse = isProblematicFuseService(targetedService);
|
||||||
|
if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
|
||||||
|
firstUsedProblematicFuseMountService.set(targetedService);
|
||||||
|
}
|
||||||
|
|
||||||
|
//do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used.
|
||||||
|
if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) {
|
||||||
|
return new ActualMountService(formerSelectedMountService.get(), false);
|
||||||
|
} else {
|
||||||
|
formerSelectedMountService.set(targetedService);
|
||||||
|
return new ActualMountService(targetedService, isDesired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isProblematicFuseService(MountService service) {
|
||||||
|
return problematicFuseMountServices.contains(service.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.cryptomator.common.mount;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class MountPointCleanupFailedException extends IllegalMountPointException {
|
|
||||||
|
|
||||||
public MountPointCleanupFailedException(Path path) {
|
|
||||||
super(path, "Mountpoint could not be cleared: " + path.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.cryptomator.common.mount;
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class MountPointInUseException extends IllegalMountPointException {
|
public class MountPointInUseException extends IllegalMountPointException {
|
||||||
|
|
||||||
public MountPointInUseException(Path path) {
|
public MountPointInUseException(String msg) {
|
||||||
super(path);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.cryptomator.common.mount;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class MountPointNotEmptyDirectoryException extends IllegalMountPointException {
|
|
||||||
|
|
||||||
public MountPointNotEmptyDirectoryException(Path path, String msg) {
|
|
||||||
super(path, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.cryptomator.common.mount;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class MountPointNotExistingException extends IllegalMountPointException {
|
|
||||||
|
|
||||||
public MountPointNotExistingException(Path path, String msg) {
|
|
||||||
super(path, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MountPointNotExistingException(Path path) {
|
|
||||||
super(path, "Mountpoint does not exist: " + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
|
public class MountPointNotExistsException extends IllegalMountPointException {
|
||||||
|
|
||||||
|
public MountPointNotExistsException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.cryptomator.common.mount;
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class MountPointNotSupportedException extends IllegalMountPointException {
|
public class MountPointNotSupportedException extends IllegalMountPointException {
|
||||||
|
|
||||||
public MountPointNotSupportedException(Path path, String msg) {
|
public MountPointNotSupportedException(String msg) {
|
||||||
super(path, msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
|
public class MountPointPreparationException extends RuntimeException {
|
||||||
|
|
||||||
|
public MountPointPreparationException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MountPointPreparationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
package org.cryptomator.common.mount;
|
package org.cryptomator.common.mount;
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.LinkOption;
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.NotDirectoryException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
|
|
||||||
public final class MountWithinParentUtil {
|
public final class MountWithinParentUtil {
|
||||||
|
|
||||||
@@ -21,33 +22,31 @@ public final class MountWithinParentUtil {
|
|||||||
|
|
||||||
private MountWithinParentUtil() {}
|
private MountWithinParentUtil() {}
|
||||||
|
|
||||||
static void prepareParentNoMountPoint(Path mountPoint) throws IllegalMountPointException, IOException {
|
static void prepareParentNoMountPoint(Path mountPoint) throws MountPointPreparationException {
|
||||||
Path hideaway = getHideaway(mountPoint);
|
Path hideaway = getHideaway(mountPoint);
|
||||||
var mpState = getMountPointState(mountPoint);
|
var mpExists = Files.exists(mountPoint, LinkOption.NOFOLLOW_LINKS);
|
||||||
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
|
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
|
||||||
|
|
||||||
if (mpState == MountPointState.BROKEN_JUNCTION) {
|
//TODO: possible improvement by just deleting an _empty_ hideaway
|
||||||
LOG.info("Mountpoint \"{}\" is still a junction. Deleting it.", mountPoint);
|
if (mpExists && hideExists) { //both resources exist (whatever type)
|
||||||
Files.delete(mountPoint); //Throws if mountPoint is also a non-empty folder
|
throw new MountPointPreparationException(new FileAlreadyExistsException(hideaway.toString()));
|
||||||
mpState = MountPointState.NOT_EXISTING;
|
} else if (!mpExists && !hideExists) { //neither mountpoint nor hideaway exist
|
||||||
}
|
throw new MountPointPreparationException(new NoSuchFileException(mountPoint.toString()));
|
||||||
|
} else if (!mpExists) { //only hideaway exists
|
||||||
if (mpState == MountPointState.NOT_EXISTING && !hideExists) { //neither mountpoint nor hideaway exist
|
checkIsDirectory(hideaway);
|
||||||
throw new MountPointNotExistingException(mountPoint);
|
|
||||||
} else if (mpState == MountPointState.NOT_EXISTING) { //only hideaway exists
|
|
||||||
checkIsHideawayDirectory(mountPoint, hideaway);
|
|
||||||
LOG.info("Mountpoint {} seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint);
|
LOG.info("Mountpoint {} seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint);
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
|
||||||
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert mpState == MountPointState.EMPTY_DIR;
|
|
||||||
try {
|
try {
|
||||||
if (hideExists) { //... with hideaway
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
removeResidualHideaway(mountPoint, hideaway);
|
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MountPointPreparationException(e);
|
||||||
|
}
|
||||||
|
} else { //only mountpoint exists
|
||||||
|
try {
|
||||||
|
checkIsDirectory(mountPoint);
|
||||||
|
checkIsEmpty(mountPoint);
|
||||||
|
|
||||||
//... (now) without hideaway
|
|
||||||
Files.move(mountPoint, hideaway);
|
Files.move(mountPoint, hideaway);
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
||||||
@@ -55,66 +54,30 @@ public final class MountWithinParentUtil {
|
|||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
while (!Files.notExists(mountPoint)) {
|
while (!Files.notExists(mountPoint)) {
|
||||||
if (attempts >= 10) {
|
if (attempts >= 10) {
|
||||||
throw new MountPointCleanupFailedException(mountPoint);
|
throw new MountPointPreparationException("Path " + mountPoint + " could not be cleared");
|
||||||
}
|
}
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
attempts++;
|
attempts++;
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MountPointPreparationException(e);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
throw new RuntimeException(e);
|
throw new MountPointPreparationException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static MountPointState getMountPointState(Path path) throws IOException, IllegalMountPointException {
|
|
||||||
if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) {
|
|
||||||
return MountPointState.NOT_EXISTING;
|
|
||||||
}
|
|
||||||
if (!Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isOther()) {
|
|
||||||
checkIsMountPointDirectory(path);
|
|
||||||
checkIsMountPointEmpty(path);
|
|
||||||
return MountPointState.EMPTY_DIR;
|
|
||||||
}
|
|
||||||
if (Files.exists(path /* FOLLOW_LINKS */)) { //Both junction and target exist
|
|
||||||
throw new MountPointInUseException(path);
|
|
||||||
}
|
|
||||||
return MountPointState.BROKEN_JUNCTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
enum MountPointState {
|
|
||||||
|
|
||||||
NOT_EXISTING,
|
|
||||||
|
|
||||||
EMPTY_DIR,
|
|
||||||
|
|
||||||
BROKEN_JUNCTION;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException {
|
|
||||||
checkIsHideawayDirectory(mountPoint, hideaway);
|
|
||||||
Files.delete(hideaway); //Fails if not empty
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cleanup(Path mountPoint) {
|
static void cleanup(Path mountPoint) {
|
||||||
Path hideaway = getHideaway(mountPoint);
|
Path hideaway = getHideaway(mountPoint);
|
||||||
try {
|
try {
|
||||||
waitForMountpointRestoration(mountPoint);
|
waitForMountpointRestoration(mountPoint);
|
||||||
if (Files.notExists(hideaway, LinkOption.NOFOLLOW_LINKS)) {
|
|
||||||
LOG.error("Unable to restore hidden directory to mountpoint \"{}\": Directory does not exist.", mountPoint);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.move(hideaway, mountPoint);
|
Files.move(hideaway, mountPoint);
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false);
|
Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Unable to restore hidden directory to mountpoint \"{}\".", mountPoint, e);
|
LOG.error("Unable to restore hidden directory to mountpoint {}.", mountPoint, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,27 +99,21 @@ public final class MountWithinParentUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkIsMountPointDirectory(Path toCheck) throws IllegalMountPointException {
|
private static void checkIsDirectory(Path toCheck) throws MountPointPreparationException {
|
||||||
if (!Files.isDirectory(toCheck, LinkOption.NOFOLLOW_LINKS)) {
|
if (!Files.isDirectory(toCheck, LinkOption.NOFOLLOW_LINKS)) {
|
||||||
throw new MountPointNotEmptyDirectoryException(toCheck, "Mountpoint is not a directory: " + toCheck);
|
throw new MountPointPreparationException(new NotDirectoryException(toCheck.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkIsHideawayDirectory(Path mountPoint, Path hideawayToCheck) {
|
private static void checkIsEmpty(Path toCheck) throws MountPointPreparationException, IOException {
|
||||||
if (!Files.isDirectory(hideawayToCheck, LinkOption.NOFOLLOW_LINKS)) {
|
|
||||||
throw new HideawayNotDirectoryException(mountPoint, hideawayToCheck);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkIsMountPointEmpty(Path toCheck) throws IllegalMountPointException, IOException {
|
|
||||||
try (var dirStream = Files.list(toCheck)) {
|
try (var dirStream = Files.list(toCheck)) {
|
||||||
if (dirStream.findFirst().isPresent()) {
|
if (dirStream.findFirst().isPresent()) {
|
||||||
throw new MountPointNotEmptyDirectoryException(toCheck, "Mountpoint directory is not empty: " + toCheck);
|
throw new MountPointPreparationException(new DirectoryNotEmptyException(toCheck.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
//visible for testing
|
||||||
static Path getHideaway(Path mountPoint) {
|
static Path getHideaway(Path mountPoint) {
|
||||||
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
|
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ import org.cryptomator.integrations.mount.MountFailedException;
|
|||||||
import org.cryptomator.integrations.mount.MountService;
|
import org.cryptomator.integrations.mount.MountService;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
|
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
|
||||||
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
|
import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
|
||||||
@@ -28,39 +24,24 @@ import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class Mounter {
|
public class Mounter {
|
||||||
|
|
||||||
// mount providers (key) can not be used if any of the conflicting mount providers (values) are already in use
|
|
||||||
private static final Map<String, Set<String>> CONFLICTING_MOUNT_SERVICES = Map.of(
|
|
||||||
"org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.FuseTMountProvider"),
|
|
||||||
"org.cryptomator.frontend.fuse.mount.FuseTMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider")
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Environment env;
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final Environment env;
|
||||||
private final WindowsDriveLetters driveLetters;
|
private final WindowsDriveLetters driveLetters;
|
||||||
private final List<MountService> mountProviders;
|
private final ObservableValue<ActualMountService> mountServiceObservable;
|
||||||
private final Set<MountService> usedMountServices;
|
|
||||||
private final ObservableValue<MountService> defaultMountService;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Mounter(Environment env, //
|
public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue<ActualMountService> mountServiceObservable) {
|
||||||
Settings settings, //
|
|
||||||
WindowsDriveLetters driveLetters, //
|
|
||||||
List<MountService> mountProviders, //
|
|
||||||
@Named("usedMountServices") Set<MountService> usedMountServices, //
|
|
||||||
ObservableValue<MountService> defaultMountService) {
|
|
||||||
this.env = env;
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.env = env;
|
||||||
this.driveLetters = driveLetters;
|
this.driveLetters = driveLetters;
|
||||||
this.mountProviders = mountProviders;
|
this.mountServiceObservable = mountServiceObservable;
|
||||||
this.usedMountServices = usedMountServices;
|
|
||||||
this.defaultMountService = defaultMountService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SettledMounter {
|
private class SettledMounter {
|
||||||
|
|
||||||
private final MountService service;
|
private MountService service;
|
||||||
private final MountBuilder builder;
|
private MountBuilder builder;
|
||||||
private final VaultSettings vaultSettings;
|
private VaultSettings vaultSettings;
|
||||||
|
|
||||||
public SettledMounter(MountService service, MountBuilder builder, VaultSettings vaultSettings) {
|
public SettledMounter(MountService service, MountBuilder builder, VaultSettings vaultSettings) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
@@ -72,13 +53,8 @@ public class Mounter {
|
|||||||
for (var capability : service.capabilities()) {
|
for (var capability : service.capabilities()) {
|
||||||
switch (capability) {
|
switch (capability) {
|
||||||
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
||||||
case LOOPBACK_PORT -> {
|
case LOOPBACK_PORT ->
|
||||||
if (vaultSettings.mountService.getValue() == null) {
|
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
|
||||||
builder.setLoopbackPort(settings.port.get());
|
|
||||||
} else {
|
|
||||||
builder.setLoopbackPort(vaultSettings.port.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
||||||
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
|
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
|
||||||
case MOUNT_FLAGS -> {
|
case MOUNT_FLAGS -> {
|
||||||
@@ -123,7 +99,7 @@ public class Mounter {
|
|||||||
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
|
var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
|
||||||
if (mpIsDriveLetter) {
|
if (mpIsDriveLetter) {
|
||||||
if (driveLetters.getOccupied().contains(userChosenMountPoint)) {
|
if (driveLetters.getOccupied().contains(userChosenMountPoint)) {
|
||||||
throw new MountPointInUseException(userChosenMountPoint);
|
throw new MountPointInUseException(userChosenMountPoint.toString());
|
||||||
}
|
}
|
||||||
} else if (canMountToParent && !canMountToDir) {
|
} else if (canMountToParent && !canMountToDir) {
|
||||||
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
|
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
|
||||||
@@ -139,13 +115,13 @@ public class Mounter {
|
|||||||
|| (!canMountToParent && !mpIsDriveLetter) //
|
|| (!canMountToParent && !mpIsDriveLetter) //
|
||||||
|| (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter);
|
|| (!canMountToDir && !canMountToParent && !canMountToSystem && !canMountToDriveLetter);
|
||||||
if (configNotSupported) {
|
if (configNotSupported) {
|
||||||
throw new MountPointNotSupportedException(userChosenMountPoint, e.getMessage());
|
throw new MountPointNotSupportedException(e.getMessage());
|
||||||
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
|
} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
|
||||||
//mountpoint must exist
|
//mountpoint must exist
|
||||||
throw new MountPointNotExistingException(userChosenMountPoint, e.getMessage());
|
throw new MountPointNotExistsException(e.getMessage());
|
||||||
} else {
|
} else {
|
||||||
//TODO: add specific exception for !canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)
|
//TODO: add specific exception for !canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)
|
||||||
throw new IllegalMountPointException(userChosenMountPoint, e.getMessage());
|
throw new IllegalMountPointException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,26 +131,13 @@ public class Mounter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
||||||
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
|
var mountService = this.mountServiceObservable.getValue().service();
|
||||||
|
|
||||||
if (isConflictingMountService(mountService)) {
|
|
||||||
var msg = STR."\{mountService.getClass()} unavailable due to conflict with either of \{CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName())}";
|
|
||||||
throw new ConflictingMountServiceException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
usedMountServices.add(mountService);
|
|
||||||
|
|
||||||
var builder = mountService.forFileSystem(cryptoFsRoot);
|
var builder = mountService.forFileSystem(cryptoFsRoot);
|
||||||
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
var internal = new SettledMounter(mountService, builder, vaultSettings);
|
||||||
var cleanup = internal.prepare();
|
var cleanup = internal.prepare();
|
||||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConflictingMountService(MountService service) {
|
|
||||||
var conflictingServices = CONFLICTING_MOUNT_SERVICES.getOrDefault(service.getClass().getName(), Set.of());
|
|
||||||
return usedMountServices.stream().map(MountService::getClass).map(Class::getName).anyMatch(conflictingServices::contains);
|
|
||||||
}
|
|
||||||
|
|
||||||
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {
|
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public class Settings {
|
|||||||
public final IntegerProperty windowYPosition;
|
public final IntegerProperty windowYPosition;
|
||||||
public final IntegerProperty windowWidth;
|
public final IntegerProperty windowWidth;
|
||||||
public final IntegerProperty windowHeight;
|
public final IntegerProperty windowHeight;
|
||||||
|
public final StringProperty displayConfiguration;
|
||||||
public final StringProperty language;
|
public final StringProperty language;
|
||||||
public final StringProperty mountService;
|
public final StringProperty mountService;
|
||||||
public final StringProperty lastUpdateCheck;
|
public final StringProperty lastUpdateCheck;
|
||||||
@@ -102,6 +103,7 @@ public class Settings {
|
|||||||
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
||||||
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
||||||
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
||||||
|
this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration);
|
||||||
this.language = new SimpleStringProperty(this, "language", json.language);
|
this.language = new SimpleStringProperty(this, "language", json.language);
|
||||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||||
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
|
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
|
||||||
@@ -129,6 +131,7 @@ public class Settings {
|
|||||||
windowYPosition.addListener(this::somethingChanged);
|
windowYPosition.addListener(this::somethingChanged);
|
||||||
windowWidth.addListener(this::somethingChanged);
|
windowWidth.addListener(this::somethingChanged);
|
||||||
windowHeight.addListener(this::somethingChanged);
|
windowHeight.addListener(this::somethingChanged);
|
||||||
|
displayConfiguration.addListener(this::somethingChanged);
|
||||||
language.addListener(this::somethingChanged);
|
language.addListener(this::somethingChanged);
|
||||||
mountService.addListener(this::somethingChanged);
|
mountService.addListener(this::somethingChanged);
|
||||||
lastUpdateCheck.addListener(this::somethingChanged);
|
lastUpdateCheck.addListener(this::somethingChanged);
|
||||||
@@ -183,6 +186,7 @@ public class Settings {
|
|||||||
json.windowYPosition = windowYPosition.get();
|
json.windowYPosition = windowYPosition.get();
|
||||||
json.windowWidth = windowWidth.get();
|
json.windowWidth = windowWidth.get();
|
||||||
json.windowHeight = windowHeight.get();
|
json.windowHeight = windowHeight.get();
|
||||||
|
json.displayConfiguration = displayConfiguration.get();
|
||||||
json.language = language.get();
|
json.language = language.get();
|
||||||
json.mountService = mountService.get();
|
json.mountService = mountService.get();
|
||||||
json.lastUpdateCheck = lastUpdateCheck.get();
|
json.lastUpdateCheck = lastUpdateCheck.get();
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ class SettingsJson {
|
|||||||
@JsonProperty("theme")
|
@JsonProperty("theme")
|
||||||
UiTheme theme = Settings.DEFAULT_THEME;
|
UiTheme theme = Settings.DEFAULT_THEME;
|
||||||
|
|
||||||
|
@JsonProperty("displayConfiguration")
|
||||||
|
String displayConfiguration;
|
||||||
|
|
||||||
@JsonProperty("keychainProvider")
|
@JsonProperty("keychainProvider")
|
||||||
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
|
String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package org.cryptomator.common.settings;
|
|||||||
import com.google.common.base.CharMatcher;
|
import com.google.common.base.CharMatcher;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
@@ -39,7 +39,6 @@ public class VaultSettings {
|
|||||||
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
||||||
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
||||||
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
||||||
static final int DEFAULT_PORT = 42427;
|
|
||||||
|
|
||||||
private static final Random RNG = new Random();
|
private static final Random RNG = new Random();
|
||||||
|
|
||||||
@@ -56,8 +55,6 @@ public class VaultSettings {
|
|||||||
public final IntegerProperty autoLockIdleSeconds;
|
public final IntegerProperty autoLockIdleSeconds;
|
||||||
public final ObjectProperty<Path> mountPoint;
|
public final ObjectProperty<Path> mountPoint;
|
||||||
public final StringExpression mountName;
|
public final StringExpression mountName;
|
||||||
public final StringProperty mountService;
|
|
||||||
public final IntegerProperty port;
|
|
||||||
|
|
||||||
VaultSettings(VaultSettingsJson json) {
|
VaultSettings(VaultSettingsJson json) {
|
||||||
this.id = json.id;
|
this.id = json.id;
|
||||||
@@ -72,8 +69,6 @@ public class VaultSettings {
|
|||||||
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
|
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
|
||||||
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
|
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
|
||||||
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
|
||||||
this.port = new SimpleIntegerProperty(this, "port", json.port);
|
|
||||||
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
||||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||||
final String name;
|
final String name;
|
||||||
@@ -99,7 +94,7 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
|
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VaultSettings withRandomId() {
|
public static VaultSettings withRandomId() {
|
||||||
@@ -128,12 +123,10 @@ public class VaultSettings {
|
|||||||
json.autoLockWhenIdle = autoLockWhenIdle.get();
|
json.autoLockWhenIdle = autoLockWhenIdle.get();
|
||||||
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
|
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
|
||||||
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||||
json.mountService = mountService.get();
|
|
||||||
json.port = port.get();
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
//visible for testing
|
||||||
static String normalizeDisplayName(String original) {
|
static String normalizeDisplayName(String original) {
|
||||||
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
if (original.isBlank() || ".".equals(original) || "..".equals(original)) {
|
||||||
return "_";
|
return "_";
|
||||||
|
|||||||
@@ -45,12 +45,6 @@ class VaultSettingsJson {
|
|||||||
@JsonProperty("autoLockIdleSeconds")
|
@JsonProperty("autoLockIdleSeconds")
|
||||||
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
||||||
|
|
||||||
@JsonProperty("mountService")
|
|
||||||
String mountService;
|
|
||||||
|
|
||||||
@JsonProperty("port")
|
|
||||||
int port = VaultSettings.DEFAULT_PORT;
|
|
||||||
|
|
||||||
@Deprecated(since = "1.7.0")
|
@Deprecated(since = "1.7.0")
|
||||||
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
String winDriveLetter;
|
String winDriveLetter;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.cryptomator.common.vaults;
|
|||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Constants;
|
import org.cryptomator.common.Constants;
|
||||||
import org.cryptomator.common.mount.Mounter;
|
import org.cryptomator.common.mount.Mounter;
|
||||||
|
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||||
@@ -72,13 +73,7 @@ public class Vault {
|
|||||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Vault(VaultSettings vaultSettings, //
|
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
|
||||||
VaultConfigCache configCache, //
|
|
||||||
AtomicReference<CryptoFileSystem> cryptoFileSystem, //
|
|
||||||
VaultState state, //
|
|
||||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
|
||||||
VaultStats stats, //
|
|
||||||
Mounter mounter) {
|
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.configCache = configCache;
|
this.configCache = configCache;
|
||||||
this.cryptoFileSystem = cryptoFileSystem;
|
this.cryptoFileSystem = cryptoFileSystem;
|
||||||
@@ -319,6 +314,10 @@ public class Vault {
|
|||||||
return vaultSettings.path.get();
|
return vaultSettings.path.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CryptoFileSystem getCryptoFileSystem() {
|
||||||
|
return cryptoFileSystem.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets from the cleartext path its ciphertext counterpart.
|
* Gets from the cleartext path its ciphertext counterpart.
|
||||||
*
|
*
|
||||||
@@ -327,23 +326,7 @@ public class Vault {
|
|||||||
* @throws IllegalStateException if the vault is not unlocked
|
* @throws IllegalStateException if the vault is not unlocked
|
||||||
*/
|
*/
|
||||||
public Path getCiphertextPath(Path cleartextPath) throws IOException {
|
public Path getCiphertextPath(Path cleartextPath) throws IOException {
|
||||||
if (!state.getValue().equals(VaultState.Value.UNLOCKED)) {
|
return state.get().getCiphertextPath(this, cleartextPath);
|
||||||
throw new IllegalStateException("Vault is not unlocked");
|
|
||||||
}
|
|
||||||
var fs = cryptoFileSystem.get();
|
|
||||||
var osPathSeparator = cleartextPath.getFileSystem().getSeparator();
|
|
||||||
var cryptoFsPathSeparator = fs.getSeparator();
|
|
||||||
|
|
||||||
if (getMountPoint() instanceof Mountpoint.WithPath mp) {
|
|
||||||
var absoluteCryptoFsPath = cryptoFsPathSeparator + mp.path().relativize(cleartextPath).toString();
|
|
||||||
if (!cryptoFsPathSeparator.equals(osPathSeparator)) {
|
|
||||||
absoluteCryptoFsPath = absoluteCryptoFsPath.replace(osPathSeparator, cryptoFsPathSeparator);
|
|
||||||
}
|
|
||||||
var cryptoPath = fs.getPath(absoluteCryptoFsPath);
|
|
||||||
return fs.getCiphertextPath(cryptoPath);
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("URI mount points not supported.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VaultConfigCache getVaultConfigCache() {
|
public VaultConfigCache getVaultConfigCache() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ import javax.inject.Inject;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ObservableObjectValue;
|
import javafx.beans.value.ObservableObjectValue;
|
||||||
import javafx.beans.value.ObservableValueBase;
|
import javafx.beans.value.ObservableValueBase;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.Condition;
|
import java.util.concurrent.locks.Condition;
|
||||||
@@ -43,12 +46,40 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
|
|||||||
/**
|
/**
|
||||||
* Vault is unlocked
|
* Vault is unlocked
|
||||||
*/
|
*/
|
||||||
UNLOCKED,
|
UNLOCKED {
|
||||||
|
/**
|
||||||
|
* Gets from the cleartext path its ciphertext counterpart.
|
||||||
|
*
|
||||||
|
* @return Local os path to the ciphertext resource
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
* @throws IllegalStateException if the vault is not unlocked
|
||||||
|
*/
|
||||||
|
Path getCiphertextPath(Vault vault, Path cleartextPath) throws IOException {
|
||||||
|
var fs = vault.getCryptoFileSystem();
|
||||||
|
var osPathSeparator = cleartextPath.getFileSystem().getSeparator();
|
||||||
|
var cryptoFsPathSeparator = fs.getSeparator();
|
||||||
|
|
||||||
|
if (vault.getMountPoint() instanceof Mountpoint.WithPath mp) {
|
||||||
|
var absoluteCryptoFsPath = cryptoFsPathSeparator + mp.path().relativize(cleartextPath).toString();
|
||||||
|
if (!cryptoFsPathSeparator.equals(osPathSeparator)) {
|
||||||
|
absoluteCryptoFsPath = absoluteCryptoFsPath.replace(osPathSeparator, cryptoFsPathSeparator);
|
||||||
|
}
|
||||||
|
var cryptoPath = fs.getPath(absoluteCryptoFsPath);
|
||||||
|
return fs.getCiphertextPath(cryptoPath);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("URI mount points not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unknown state due to preceding unrecoverable exceptions.
|
* Unknown state due to preceding unrecoverable exceptions.
|
||||||
*/
|
*/
|
||||||
ERROR;
|
ERROR;
|
||||||
|
|
||||||
|
Path getCiphertextPath(Vault vault, Path cleartextPath) throws IOException {
|
||||||
|
throw new IllegalStateException("Vault is not unlocked");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicReference<Value> value;
|
private final AtomicReference<Value> value;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ class FileOpenRequestHandler {
|
|||||||
handleLaunchArgs(FileSystems.getDefault(), args);
|
handleLaunchArgs(FileSystems.getDefault(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
void handleLaunchArgs(FileSystem fs, List<String> args) {
|
void handleLaunchArgs(FileSystem fs, List<String> args) {
|
||||||
Collection<Path> pathsToOpen = args.stream().map(str -> {
|
Collection<Path> pathsToOpen = args.stream().map(str -> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import ch.qos.logback.classic.Logger;
|
|||||||
import ch.qos.logback.classic.LoggerContext;
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||||
import ch.qos.logback.classic.spi.Configurator;
|
import ch.qos.logback.classic.spi.Configurator;
|
||||||
import ch.qos.logback.classic.spi.ConfiguratorRank;
|
|
||||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
import ch.qos.logback.core.Appender;
|
import ch.qos.logback.core.Appender;
|
||||||
import ch.qos.logback.core.ConsoleAppender;
|
import ch.qos.logback.core.ConsoleAppender;
|
||||||
@@ -20,7 +19,6 @@ import org.cryptomator.common.Environment;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ConfiguratorRank(ConfiguratorRank.CUSTOM_NORMAL_PRIORITY)
|
|
||||||
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
|
public class LogbackConfigurator extends ContextAwareBase implements Configurator {
|
||||||
|
|
||||||
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
private static final String LOG_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ public abstract class AddVaultModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@AddVaultWizardWindow
|
@AddVaultWizardWindow
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage) {
|
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||||
Stage stage = factory.create();
|
Stage stage = factory.create();
|
||||||
|
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(primaryStage);
|
stage.initOwner(primaryStage);
|
||||||
@@ -89,6 +90,13 @@ public abstract class AddVaultModule {
|
|||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
|
||||||
|
@AddVaultWizardScoped
|
||||||
|
static Scene provideWelcomeScene(@AddVaultWizardWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.ADDVAULT_WELCOME);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
|
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@@ -140,6 +148,11 @@ public abstract class AddVaultModule {
|
|||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(AddVaultWelcomeController.class)
|
||||||
|
abstract FxController bindWelcomeController(AddVaultWelcomeController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(ChooseExistingVaultController.class)
|
@FxControllerKey(ChooseExistingVaultController.class)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.cryptomator.ui.addvaultwizard;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
@AddVaultWizardScoped
|
||||||
|
public class AddVaultWelcomeController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AddVaultWelcomeController.class);
|
||||||
|
private final Stage window;
|
||||||
|
private final Lazy<Scene> chooseExistingVaultScene;
|
||||||
|
private final Lazy<Scene> createNewVaultScene;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AddVaultWelcomeController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_EXISTING) Lazy<Scene> chooseExistingVaultScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> createNewVaultScene) {
|
||||||
|
this.window = window;
|
||||||
|
this.chooseExistingVaultScene = chooseExistingVaultScene;
|
||||||
|
this.createNewVaultScene = createNewVaultScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createNewVault() {
|
||||||
|
LOG.debug("AddVaultWelcomeController.createNewVault()");
|
||||||
|
window.setScene(createNewVaultScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void chooseExistingVault() {
|
||||||
|
LOG.debug("AddVaultWelcomeController.chooseExistingVault()");
|
||||||
|
window.setScene(chooseExistingVaultScene.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@Subcomponent(modules = {AddVaultModule.class})
|
@Subcomponent(modules = {AddVaultModule.class})
|
||||||
@@ -21,23 +20,12 @@ public interface AddVaultWizardComponent {
|
|||||||
@AddVaultWizardWindow
|
@AddVaultWizardWindow
|
||||||
Stage window();
|
Stage window();
|
||||||
|
|
||||||
@FxmlScene(FxmlFile.ADDVAULT_NEW_NAME)
|
@FxmlScene(FxmlFile.ADDVAULT_WELCOME)
|
||||||
Lazy<Scene> sceneNew();
|
Lazy<Scene> scene();
|
||||||
@FxmlScene(FxmlFile.ADDVAULT_EXISTING)
|
|
||||||
Lazy<Scene> sceneExisting();
|
|
||||||
|
|
||||||
default void showAddNewVaultWizard(ResourceBundle resourceBundle) {
|
default void showAddVaultWizard() {
|
||||||
Stage stage = window();
|
Stage stage = window();
|
||||||
stage.setScene(sceneNew().get());
|
stage.setScene(scene().get());
|
||||||
stage.setTitle(resourceBundle.getString("addvaultwizard.new.title"));
|
|
||||||
stage.sizeToScene();
|
|
||||||
stage.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
default void showAddExistingVaultWizard(ResourceBundle resourceBundle) {
|
|
||||||
Stage stage = window();
|
|
||||||
stage.setScene(sceneExisting().get());
|
|
||||||
stage.setTitle(resourceBundle.getString("addvaultwizard.existing.title"));
|
|
||||||
stage.sizeToScene();
|
stage.sizeToScene();
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(ChooseExistingVaultController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ChooseExistingVaultController.class);
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
private final Lazy<Scene> welcomeScene;
|
||||||
private final Lazy<Scene> successScene;
|
private final Lazy<Scene> successScene;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final ObjectProperty<Path> vaultPath;
|
private final ObjectProperty<Path> vaultPath;
|
||||||
@@ -44,15 +45,9 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
private final ObservableValue<Image> screenshot;
|
private final ObservableValue<Image> screenshot;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, //
|
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, FxApplicationStyle applicationStyle) {
|
||||||
@FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, //
|
|
||||||
FxApplicationWindows appWindows, //
|
|
||||||
ObjectProperty<Path> vaultPath, //
|
|
||||||
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
|
|
||||||
VaultListManager vaultListManager, //
|
|
||||||
ResourceBundle resourceBundle, //
|
|
||||||
FxApplicationStyle applicationStyle) {
|
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.welcomeScene = welcomeScene;
|
||||||
this.successScene = successScene;
|
this.successScene = successScene;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultPath = vaultPath;
|
this.vaultPath = vaultPath;
|
||||||
@@ -75,6 +70,11 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
|
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(welcomeScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void chooseFileAndNext() {
|
public void chooseFileAndNext() {
|
||||||
FileChooser fileChooser = new FileChooser();
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import javafx.scene.Scene;
|
|||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
@@ -26,17 +27,16 @@ public class CreateNewVaultNameController implements FxController {
|
|||||||
|
|
||||||
public TextField textField;
|
public TextField textField;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
private final Lazy<Scene> welcomeScene;
|
||||||
private final Lazy<Scene> chooseLocationScene;
|
private final Lazy<Scene> chooseLocationScene;
|
||||||
private final ObjectProperty<Path> vaultPath;
|
private final ObjectProperty<Path> vaultPath;
|
||||||
private final StringProperty vaultName;
|
private final StringProperty vaultName;
|
||||||
private final BooleanBinding validVaultName;
|
private final BooleanBinding validVaultName;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, //
|
CreateNewVaultNameController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
|
||||||
@FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, //
|
|
||||||
ObjectProperty<Path> vaultPath, //
|
|
||||||
@Named("vaultName") StringProperty vaultName) {
|
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.welcomeScene = welcomeScene;
|
||||||
this.chooseLocationScene = chooseLocationScene;
|
this.chooseLocationScene = chooseLocationScene;
|
||||||
this.vaultPath = vaultPath;
|
this.vaultPath = vaultPath;
|
||||||
this.vaultName = vaultName;
|
this.vaultName = vaultName;
|
||||||
@@ -58,6 +58,11 @@ public class CreateNewVaultNameController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(welcomeScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void next() {
|
public void next() {
|
||||||
window.setScene(chooseLocationScene.get());
|
window.setScene(chooseLocationScene.get());
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.cryptomator.ui.addvaultwizard;
|
package org.cryptomator.ui.addvaultwizard;
|
||||||
|
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
@@ -53,7 +51,7 @@ public class ReadmeGenerator {
|
|||||||
resourceBundle.getString("addvault.new.readme.accessLocation.4")));
|
resourceBundle.getString("addvault.new.readme.accessLocation.4")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
String createDocument(Iterable<String> paragraphs) {
|
String createDocument(Iterable<String> paragraphs) {
|
||||||
StringBuilder sb = new StringBuilder(RTF_HEADER);
|
StringBuilder sb = new StringBuilder(RTF_HEADER);
|
||||||
for (String p : paragraphs) {
|
for (String p : paragraphs) {
|
||||||
@@ -65,7 +63,7 @@ public class ReadmeGenerator {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
// visible for testing
|
||||||
String escapeNonAsciiChars(CharSequence input) {
|
String escapeNonAsciiChars(CharSequence input) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
appendEscaped(sb, input);
|
appendEscaped(sb, input);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public enum FxmlFile {
|
|||||||
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
|
ADDVAULT_NEW_PASSWORD("/fxml/addvault_new_password.fxml"), //
|
||||||
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
|
ADDVAULT_NEW_RECOVERYKEY("/fxml/addvault_new_recoverykey.fxml"), //
|
||||||
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
ADDVAULT_SUCCESS("/fxml/addvault_success.fxml"), //
|
||||||
|
ADDVAULT_WELCOME("/fxml/addvault_welcome.fxml"), //
|
||||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||||
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
|
CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), //
|
||||||
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
||||||
@@ -20,14 +21,10 @@ public enum FxmlFile {
|
|||||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||||
HUB_LEGACY_REGISTER_DEVICE("/fxml/hub_legacy_register_device.fxml"), //
|
|
||||||
HUB_LEGACY_REGISTER_SUCCESS("/fxml/hub_legacy_register_success.fxml"), //
|
|
||||||
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
|
|
||||||
HUB_REGISTER_DEVICE_ALREADY_EXISTS("/fxml/hub_register_device_already_exists.fxml"), //
|
|
||||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
|
|
||||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||||
|
HUB_REGISTER_SUCCESS("/fxml/hub_register_success.fxml"), //
|
||||||
|
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"),
|
||||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
||||||
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
|
|
||||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||||
@@ -45,10 +42,8 @@ public enum FxmlFile {
|
|||||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||||
SHARE_VAULT("/fxml/share_vault.fxml"), //
|
|
||||||
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
||||||
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
||||||
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
|
|
||||||
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
|
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
|
||||||
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
||||||
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
|
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
|
||||||
|
|||||||
@@ -47,14 +47,12 @@ public enum FontAwesome5Icon {
|
|||||||
QUESTION_CIRCLE("\uf059"), //
|
QUESTION_CIRCLE("\uf059"), //
|
||||||
REDO("\uF01E"), //
|
REDO("\uF01E"), //
|
||||||
SEARCH("\uF002"), //
|
SEARCH("\uF002"), //
|
||||||
SHARE("\uF064"), //
|
|
||||||
SPINNER("\uF110"), //
|
SPINNER("\uF110"), //
|
||||||
STETHOSCOPE("\uF0f1"), //
|
STETHOSCOPE("\uF0f1"), //
|
||||||
SYNC("\uF021"), //
|
SYNC("\uF021"), //
|
||||||
TIMES("\uF00D"), //
|
TIMES("\uF00D"), //
|
||||||
TRASH("\uF1F8"), //
|
TRASH("\uF1F8"), //
|
||||||
UNLINK("\uf127"), //
|
UNLINK("\uf127"), //
|
||||||
USER_COG("\uf4fe"), //
|
|
||||||
WRENCH("\uF0AD"), //
|
WRENCH("\uF0AD"), //
|
||||||
WINDOW_MINIMIZE("\uF2D1"), //
|
WINDOW_MINIMIZE("\uF2D1"), //
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.cryptomator.ui.common.FxmlFile;
|
|||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -117,7 +116,7 @@ public class HubToPasswordConvertController implements FxController {
|
|||||||
}, Platform::runLater); //
|
}, Platform::runLater); //
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
//visible for testing
|
||||||
void convertInternal() throws CompletionException, IllegalArgumentException {
|
void convertInternal() throws CompletionException, IllegalArgumentException {
|
||||||
var passphrase = newPasswordController.getNewPassword();
|
var passphrase = newPasswordController.getNewPassword();
|
||||||
var vaultPath = vault.getPath();
|
var vaultPath = vault.getPath();
|
||||||
@@ -142,7 +141,7 @@ public class HubToPasswordConvertController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
//visible for testing
|
||||||
void backupHubConfig(Path hubConfigPath) throws IOException {
|
void backupHubConfig(Path hubConfigPath) throws IOException {
|
||||||
byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath);
|
byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath);
|
||||||
Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX);
|
Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX);
|
||||||
@@ -150,7 +149,7 @@ public class HubToPasswordConvertController implements FxController {
|
|||||||
LOG.debug("Successfully created hub config backup {}", backupPath.getFileName());
|
LOG.debug("Successfully created hub config backup {}", backupPath.getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
//visible for testing
|
||||||
Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException {
|
Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException {
|
||||||
var unverifiedVaultConfig = vault.getVaultConfigCache().get();
|
var unverifiedVaultConfig = vault.getVaultConfigCache().get();
|
||||||
try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
|
try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import java.net.http.HttpClient;
|
|||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -43,21 +42,19 @@ public class ErrorController implements FxController {
|
|||||||
|
|
||||||
private static final ObjectMapper JSON = new ObjectMapper();
|
private static final ObjectMapper JSON = new ObjectMapper();
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class);
|
||||||
private static final String USER_AGENT_FORMAT = "Cryptomator/%s (Build %s) (%s %s %s)";
|
private static final String ERROR_CODES_URL = "https://gist.githubusercontent.com/cryptobot/accba9fb9555e7192271b85606f97230/raw/errorcodes.json";
|
||||||
private static final String ERROR_CODES_URL_FORMAT = "https://api.cryptomator.org/desktop/error-codes.json?error-code=%s";
|
|
||||||
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
|
private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s";
|
||||||
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
|
private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s";
|
||||||
private static final String SEARCH_ERRORCODE_DELIM = " OR ";
|
private static final String SEARCH_ERRORCODE_DELIM = " OR ";
|
||||||
private static final String REPORT_BODY_TEMPLATE = """
|
private static final String REPORT_BODY_TEMPLATE = """
|
||||||
<!-- 💚 Thank you for reporting this error. -->
|
|
||||||
OS: %s / %s
|
OS: %s / %s
|
||||||
App: %s / %s
|
App: %s / %s
|
||||||
|
|
||||||
<!-- ✏ Please describe what happened as accurately as possible. -->
|
<!-- ✏ Please describe what happened as accurately as possible. -->
|
||||||
Description:
|
|
||||||
|
<!-- 📋 Please also copy and paste the detail text from the error window. -->
|
||||||
<!-- 📋 Please also copy and paste the details from the error window. -->
|
|
||||||
Details:
|
<!-- ℹ Text enclosed like this (chevrons, exclamation mark, two dashes) is not visible to others! -->
|
||||||
|
|
||||||
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
|
<!-- ❗ If the description or the detail text is missing, the discussion will be deleted. -->
|
||||||
""";
|
""";
|
||||||
@@ -68,13 +65,11 @@ public class ErrorController implements FxController {
|
|||||||
private final Scene previousScene;
|
private final Scene previousScene;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final ExecutorService executorService;
|
|
||||||
|
|
||||||
private final BooleanProperty copiedDetails = new SimpleBooleanProperty();
|
private final BooleanProperty copiedDetails = new SimpleBooleanProperty();
|
||||||
private final ObjectProperty<ErrorDiscussion> matchingErrorDiscussion = new SimpleObjectProperty<>();
|
private final ObjectProperty<ErrorDiscussion> matchingErrorDiscussion = new SimpleObjectProperty<>();
|
||||||
private final BooleanExpression errorSolutionFound = matchingErrorDiscussion.isNotNull();
|
private final BooleanExpression errorSolutionFound = matchingErrorDiscussion.isNotNull();
|
||||||
private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty();
|
private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty();
|
||||||
private final BooleanProperty askedForLookupDatabasePermission = new SimpleBooleanProperty();
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) {
|
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) {
|
||||||
@@ -84,7 +79,15 @@ public class ErrorController implements FxController {
|
|||||||
this.previousScene = previousScene;
|
this.previousScene = previousScene;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.executorService = executorService;
|
|
||||||
|
isLoadingHttpResponse.set(true);
|
||||||
|
HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
|
||||||
|
HttpRequest httpRequest = HttpRequest.newBuilder()//
|
||||||
|
.uri(URI.create(ERROR_CODES_URL))//
|
||||||
|
.build();
|
||||||
|
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())//
|
||||||
|
.thenAcceptAsync(this::loadHttpResponse, executorService)//
|
||||||
|
.whenCompleteAsync((r, e) -> isLoadingHttpResponse.set(false), Platform::runLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -137,32 +140,6 @@ public class ErrorController implements FxController {
|
|||||||
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> copiedDetails.set(false));
|
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> copiedDetails.set(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void dismiss() {
|
|
||||||
askedForLookupDatabasePermission.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void lookUpSolution() {
|
|
||||||
String userAgent = USER_AGENT_FORMAT.formatted( //
|
|
||||||
environment.getAppVersion(), //
|
|
||||||
environment.getBuildNumber().orElse("undefined"), //
|
|
||||||
System.getProperty("os.name"), //
|
|
||||||
System.getProperty("os.version"), //
|
|
||||||
System.getProperty("os.arch"));
|
|
||||||
isLoadingHttpResponse.set(true);
|
|
||||||
askedForLookupDatabasePermission.set(true);
|
|
||||||
HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
|
|
||||||
HttpRequest httpRequest = HttpRequest.newBuilder()//
|
|
||||||
.header("User-Agent", userAgent)
|
|
||||||
.timeout(Duration.ofSeconds(10))
|
|
||||||
.uri(URI.create(ERROR_CODES_URL_FORMAT.formatted(URLEncoder.encode(errorCode.toString(),StandardCharsets.UTF_8))))//
|
|
||||||
.build();
|
|
||||||
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())//
|
|
||||||
.thenAcceptAsync(this::loadHttpResponse, executorService)//
|
|
||||||
.whenCompleteAsync((r, e) -> isLoadingHttpResponse.set(false), Platform::runLater);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadHttpResponse(HttpResponse<InputStream> response) {
|
private void loadHttpResponse(HttpResponse<InputStream> response) {
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
LOG.error("Status code {} when trying to load {} ", response.statusCode(), response.uri());
|
LOG.error("Status code {} when trying to load {} ", response.statusCode(), response.uri());
|
||||||
@@ -316,12 +293,4 @@ public class ErrorController implements FxController {
|
|||||||
return isLoadingHttpResponse.get();
|
return isLoadingHttpResponse.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanProperty askedForLookupDatabasePermissionProperty() {
|
|
||||||
return askedForLookupDatabasePermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getAskedForLookupDatabasePermission() {
|
|
||||||
return askedForLookupDatabasePermission.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.cryptomator.ui.fxapp;
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.Environment;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -18,7 +17,6 @@ public class FxApplication {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||||
|
|
||||||
private final long startupTime;
|
private final long startupTime;
|
||||||
private final Environment environment;
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final AppLaunchEventHandler launchEventHandler;
|
private final AppLaunchEventHandler launchEventHandler;
|
||||||
private final Lazy<TrayMenuComponent> trayMenu;
|
private final Lazy<TrayMenuComponent> trayMenu;
|
||||||
@@ -28,9 +26,8 @@ public class FxApplication {
|
|||||||
private final AutoUnlocker autoUnlocker;
|
private final AutoUnlocker autoUnlocker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||||
this.startupTime = startupTime;
|
this.startupTime = startupTime;
|
||||||
this.environment = environment;
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.launchEventHandler = launchEventHandler;
|
this.launchEventHandler = launchEventHandler;
|
||||||
this.trayMenu = trayMenu;
|
this.trayMenu = trayMenu;
|
||||||
@@ -71,9 +68,7 @@ public class FxApplication {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!environment.disableUpdateCheck()) {
|
appWindows.checkAndShowUpdateReminderWindow();
|
||||||
appWindows.checkAndShowUpdateReminderWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents();
|
launchEventHandler.startHandlingLaunchEvents();
|
||||||
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ 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.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;
|
||||||
@@ -23,17 +22,7 @@ import javafx.scene.image.Image;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
|
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class, UpdateReminderComponent.class})
|
||||||
MainWindowComponent.class, //
|
|
||||||
PreferencesComponent.class, //
|
|
||||||
VaultOptionsComponent.class, //
|
|
||||||
UnlockComponent.class, //
|
|
||||||
LockComponent.class, //
|
|
||||||
QuitComponent.class, //
|
|
||||||
ErrorComponent.class, //
|
|
||||||
HealthCheckComponent.class, //
|
|
||||||
UpdateReminderComponent.class, //
|
|
||||||
ShareVaultComponent.class})
|
|
||||||
abstract class FxApplicationModule {
|
abstract class FxApplicationModule {
|
||||||
|
|
||||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
|||||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.cryptomator.ui.quit.QuitComponent;
|
import org.cryptomator.ui.quit.QuitComponent;
|
||||||
import org.cryptomator.ui.sharevault.ShareVaultComponent;
|
|
||||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||||
import org.cryptomator.ui.unlock.UnlockWorkflow;
|
import org.cryptomator.ui.unlock.UnlockWorkflow;
|
||||||
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
||||||
@@ -52,7 +51,6 @@ public class FxApplicationWindows {
|
|||||||
private final ErrorComponent.Factory errorWindowFactory;
|
private final ErrorComponent.Factory errorWindowFactory;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
|
||||||
private final FilteredList<Window> visibleWindows;
|
private final FilteredList<Window> visibleWindows;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -66,7 +64,6 @@ public class FxApplicationWindows {
|
|||||||
LockComponent.Factory lockWorkflowFactory, //
|
LockComponent.Factory lockWorkflowFactory, //
|
||||||
ErrorComponent.Factory errorWindowFactory, //
|
ErrorComponent.Factory errorWindowFactory, //
|
||||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
ShareVaultComponent.Factory shareVaultWindow, //
|
|
||||||
ExecutorService executor) {
|
ExecutorService executor) {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
this.trayIntegration = trayIntegration;
|
this.trayIntegration = trayIntegration;
|
||||||
@@ -79,7 +76,6 @@ public class FxApplicationWindows {
|
|||||||
this.errorWindowFactory = errorWindowFactory;
|
this.errorWindowFactory = errorWindowFactory;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.shareVaultWindow = shareVaultWindow;
|
|
||||||
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,10 +122,6 @@ public class FxApplicationWindows {
|
|||||||
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
|
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showShareVaultWindow(Vault vault) {
|
|
||||||
CompletableFuture.runAsync(() -> shareVaultWindow.create(vault).showShareVaultWindow(), Platform::runLater);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
|
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
|
||||||
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
|
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,23 +22,23 @@ public class UpdateChecker {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
||||||
private static final Duration AUTOCHECK_DELAY = Duration.seconds(5);
|
private static final Duration AUTOCHECK_DELAY = Duration.seconds(5);
|
||||||
|
|
||||||
private final Environment env;
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final String currentVersion;
|
||||||
private final StringProperty latestVersionProperty;
|
private final StringProperty latestVersionProperty;
|
||||||
private final Comparator<String> semVerComparator;
|
private final Comparator<String> semVerComparator;
|
||||||
private final ScheduledService<String> updateCheckerService;
|
private final ScheduledService<String> updateCheckerService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
|
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
|
||||||
this.env = env;
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.latestVersionProperty = latestVersionProperty;
|
this.latestVersionProperty = latestVersionProperty;
|
||||||
this.semVerComparator = semVerComparator;
|
this.semVerComparator = semVerComparator;
|
||||||
this.updateCheckerService = updateCheckerService;
|
this.updateCheckerService = updateCheckerService;
|
||||||
|
this.currentVersion = env.getAppVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void automaticallyCheckForUpdatesIfEnabled() {
|
public void automaticallyCheckForUpdatesIfEnabled() {
|
||||||
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
|
if (settings.checkForUpdates.get()) {
|
||||||
startCheckingForUpdates(AUTOCHECK_DELAY);
|
startCheckingForUpdates(AUTOCHECK_DELAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,9 +63,9 @@ public class UpdateChecker {
|
|||||||
|
|
||||||
private void checkSucceeded(WorkerStateEvent event) {
|
private void checkSucceeded(WorkerStateEvent event) {
|
||||||
String latestVersion = updateCheckerService.getValue();
|
String latestVersion = updateCheckerService.getValue();
|
||||||
LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion);
|
LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion);
|
||||||
|
|
||||||
if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) {
|
if (semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
||||||
// update is available
|
// update is available
|
||||||
latestVersionProperty.set(latestVersion);
|
latestVersionProperty.set(latestVersion);
|
||||||
} else {
|
} else {
|
||||||
@@ -88,7 +88,7 @@ public class UpdateChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrentVersion() {
|
public String getCurrentVersion() {
|
||||||
return env.getAppVersion();
|
return currentVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public abstract class UpdateCheckerModule {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerModule.class);
|
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerModule.class);
|
||||||
|
|
||||||
private static final URI LATEST_VERSION_URI = URI.create("https://api.cryptomator.org/desktop/latest-version.json");
|
private static final URI LATEST_VERSION_URI = URI.create("https://api.cryptomator.org/updates/latestVersion.json");
|
||||||
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
|
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
|
||||||
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
|
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
|
||||||
|
|
||||||
@@ -63,7 +63,6 @@ public abstract class UpdateCheckerModule {
|
|||||||
return HttpRequest.newBuilder() //
|
return HttpRequest.newBuilder() //
|
||||||
.uri(LATEST_VERSION_URI) //
|
.uri(LATEST_VERSION_URI) //
|
||||||
.header("User-Agent", userAgent) //
|
.header("User-Agent", userAgent) //
|
||||||
.timeout(java.time.Duration.ofSeconds(10))
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,16 +101,16 @@ public class StartController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadingKeyFailed(Throwable t) {
|
private void loadingKeyFailed(Throwable e) {
|
||||||
switch (t) {
|
switch (e) {
|
||||||
case UnlockCancelledException e -> {} // ok // TODO: rename to _ with JEP 443
|
case UnlockCancelledException uce -> {} //ok
|
||||||
case VaultKeyInvalidException e -> { // TODO: rename to _ with JEP 443
|
case VaultKeyInvalidException vkie -> {
|
||||||
LOG.error("Invalid key"); // TODO: specific error screen
|
LOG.error("Invalid key"); //TODO: specific error screen
|
||||||
appWindows.showErrorWindow(e, window, null);
|
appWindows.showErrorWindow(e, window, null);
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
LOG.error("Failed to load key.", t);
|
LOG.error("Failed to load key.", e);
|
||||||
appWindows.showErrorWindow(t, window, null);
|
appWindows.showErrorWindow(e, window, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ public class AuthFlowController implements FxController {
|
|||||||
private final String deviceId;
|
private final String deviceId;
|
||||||
private final HubConfig hubConfig;
|
private final HubConfig hubConfig;
|
||||||
private final AtomicReference<String> tokenRef;
|
private final AtomicReference<String> tokenRef;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<JWEObject> result;
|
||||||
private final Lazy<Scene> receiveKeyScene;
|
private final Lazy<Scene> receiveKeyScene;
|
||||||
private final ObjectProperty<URI> authUri;
|
private final ObjectProperty<URI> authUri;
|
||||||
private AuthFlowTask task;
|
private AuthFlowTask task;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
|
public AuthFlowController(Application application, @KeyLoading Stage window, ExecutorService executor, @Named("deviceId") String deviceId, HubConfig hubConfig, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene) {
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.github.coffeelibs.tinyoauth2client.AuthFlow;
|
||||||
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
|
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
|
||||||
import io.github.coffeelibs.tinyoauth2client.http.response.Response;
|
import io.github.coffeelibs.tinyoauth2client.http.response.Response;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
class AuthFlowTask extends Task<String> {
|
class AuthFlowTask extends Task<String> {
|
||||||
@@ -22,7 +21,7 @@ class AuthFlowTask extends Task<String> {
|
|||||||
/**
|
/**
|
||||||
* Spawns a server and waits for the redirectUri to be called.
|
* Spawns a server and waits for the redirectUri to be called.
|
||||||
*
|
*
|
||||||
* @param hubConfig Configuration object holding parameters required by {@link io.github.coffeelibs.tinyoauth2client.AuthorizationCodeGrant}
|
* @param hubConfig Configuration object holding parameters required by {@link AuthFlow}
|
||||||
* @param redirectUriConsumer A callback invoked with the redirectUri, as soon as the server has started
|
* @param redirectUriConsumer A callback invoked with the redirectUri, as soon as the server has started
|
||||||
*/
|
*/
|
||||||
public AuthFlowTask(HubConfig hubConfig, AuthFlowContext authFlowContext, Consumer<URI> redirectUriConsumer) {
|
public AuthFlowTask(HubConfig hubConfig, AuthFlowContext authFlowContext, Consumer<URI> redirectUriConsumer) {
|
||||||
@@ -35,11 +34,10 @@ class AuthFlowTask extends Task<String> {
|
|||||||
protected String call() throws IOException, InterruptedException {
|
protected String call() throws IOException, InterruptedException {
|
||||||
var response = TinyOAuth2.client(hubConfig.clientId) //
|
var response = TinyOAuth2.client(hubConfig.clientId) //
|
||||||
.withTokenEndpoint(URI.create(hubConfig.tokenEndpoint)) //
|
.withTokenEndpoint(URI.create(hubConfig.tokenEndpoint)) //
|
||||||
.withRequestTimeout(Duration.ofSeconds(10)) //
|
.authFlow(URI.create(hubConfig.authEndpoint)) //
|
||||||
.authorizationCodeGrant(URI.create(hubConfig.authEndpoint)) //
|
|
||||||
.setSuccessResponse(Response.redirect(URI.create(hubConfig.authSuccessUrl + "&device=" + authFlowContext.deviceId()))) //
|
.setSuccessResponse(Response.redirect(URI.create(hubConfig.authSuccessUrl + "&device=" + authFlowContext.deviceId()))) //
|
||||||
.setErrorResponse(Response.redirect(URI.create(hubConfig.authErrorUrl + "&device=" + authFlowContext.deviceId()))) //
|
.setErrorResponse(Response.redirect(URI.create(hubConfig.authErrorUrl + "&device=" + authFlowContext.deviceId()))) //
|
||||||
.authorize(HttpClient.newHttpClient(), redirectUriConsumer);
|
.authorize(redirectUriConsumer);
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
throw new NotOkResponseException("Authorization returned status code " + response.statusCode());
|
throw new NotOkResponseException("Authorization returned status code " + response.statusCode());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
|
record CreateDeviceDto(String id, String name, String publicKey) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
|
||||||
|
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown, when Hub registerDevice-Request returns with 409
|
|
||||||
*/
|
|
||||||
class DeviceAlreadyExistsException extends MasterkeyLoadingFailedException {
|
|
||||||
public DeviceAlreadyExistsException() {
|
|
||||||
super("Device already registered on this Hub instance");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
class HttpHelper {
|
||||||
|
|
||||||
|
public static String readBody(HttpResponse<InputStream> response) throws IOException {
|
||||||
|
try (var in = response.body(); var reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||||
|
return CharStreams.toString(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
// needs to be accessible by JSON decoder
|
// needs to be accessible by JSON decoder
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@@ -13,50 +9,8 @@ public class HubConfig {
|
|||||||
public String clientId;
|
public String clientId;
|
||||||
public String authEndpoint;
|
public String authEndpoint;
|
||||||
public String tokenEndpoint;
|
public String tokenEndpoint;
|
||||||
|
public String devicesResourceUrl;
|
||||||
public String authSuccessUrl;
|
public String authSuccessUrl;
|
||||||
public String authErrorUrl;
|
public String authErrorUrl;
|
||||||
public @Nullable String apiBaseUrl;
|
|
||||||
@Deprecated // use apiBaseUrl + "/devices/"
|
|
||||||
public String devicesResourceUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of String template processors to construct URIs related to this Hub instance.
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
public final URIProcessors URIs = new URIProcessors();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the URI pointing to the <code>/api/</code> base resource.
|
|
||||||
*
|
|
||||||
* @return <code>/api/</code> URI
|
|
||||||
* @apiNote URI is guaranteed to end on <code>/</code>
|
|
||||||
* @see #URIs
|
|
||||||
*/
|
|
||||||
public URI getApiBaseUrl() {
|
|
||||||
if (apiBaseUrl != null) {
|
|
||||||
// make sure to end on "/":
|
|
||||||
return URI.create(apiBaseUrl + "/").normalize();
|
|
||||||
} else { // legacy approach
|
|
||||||
assert devicesResourceUrl != null;
|
|
||||||
// make sure to end on "/":
|
|
||||||
return URI.create(devicesResourceUrl + "/..").normalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public URI getWebappBaseUrl() {
|
|
||||||
return getApiBaseUrl().resolve("../app/");
|
|
||||||
}
|
|
||||||
|
|
||||||
public class URIProcessors {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves paths relative to the <code>/api/</code> endpoint of this Hub instance.
|
|
||||||
*/
|
|
||||||
public final StringTemplate.Processor<URI, RuntimeException> API = template -> {
|
|
||||||
var path = template.interpolate();
|
|
||||||
var relPath = path.startsWith("/") ? path.substring(1) : path;
|
|
||||||
return getApiBaseUrl().resolve(relPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.nimbusds.jose.JWEObject;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -68,7 +69,7 @@ public abstract class HubKeyLoadingModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static CompletableFuture<ReceivedKey> provideResult() {
|
static CompletableFuture<JWEObject> provideResult() {
|
||||||
return new CompletableFuture<>();
|
return new CompletableFuture<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,17 +114,10 @@ public abstract class HubKeyLoadingModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE)
|
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static Scene provideHubLegacyRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE);
|
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS)
|
|
||||||
@KeyLoadingScoped
|
|
||||||
static Scene provideHubLegacyRegisterSuccessScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -140,20 +134,6 @@ public abstract class HubKeyLoadingModule {
|
|||||||
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_FAILED);
|
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
|
|
||||||
@KeyLoadingScoped
|
|
||||||
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS)
|
|
||||||
@KeyLoadingScoped
|
|
||||||
static Scene provideHubRegisterDeviceAlreadyExistsScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE)
|
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE)
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
@@ -161,13 +141,6 @@ public abstract class HubKeyLoadingModule {
|
|||||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
|
||||||
@KeyLoadingScoped
|
|
||||||
static Scene provideRequireAccountInitScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
|
||||||
return fxmlLoaders.createScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(NoKeychainController.class)
|
@FxControllerKey(NoKeychainController.class)
|
||||||
@@ -193,16 +166,6 @@ public abstract class HubKeyLoadingModule {
|
|||||||
@FxControllerKey(RegisterDeviceController.class)
|
@FxControllerKey(RegisterDeviceController.class)
|
||||||
abstract FxController bindRegisterDeviceController(RegisterDeviceController controller);
|
abstract FxController bindRegisterDeviceController(RegisterDeviceController controller);
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(LegacyRegisterDeviceController.class)
|
|
||||||
abstract FxController bindLegacyRegisterDeviceController(LegacyRegisterDeviceController controller);
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(LegacyRegisterSuccessController.class)
|
|
||||||
abstract FxController bindLegacyRegisterSuccessController(LegacyRegisterSuccessController controller);
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(RegisterSuccessController.class)
|
@FxControllerKey(RegisterSuccessController.class)
|
||||||
@@ -217,9 +180,4 @@ public abstract class HubKeyLoadingModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(UnauthorizedDeviceController.class)
|
@FxControllerKey(UnauthorizedDeviceController.class)
|
||||||
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(RequireAccountInitController.class)
|
|
||||||
abstract FxController bindRequireAccountInitController(RequireAccountInitController controller);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.nimbusds.jose.JWEObject;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.keychain.KeychainManager;
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
|
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
|
||||||
@@ -35,15 +36,14 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
private final KeychainManager keychainManager;
|
private final KeychainManager keychainManager;
|
||||||
private final Lazy<Scene> authFlowScene;
|
private final Lazy<Scene> authFlowScene;
|
||||||
private final Lazy<Scene> noKeychainScene;
|
private final Lazy<Scene> noKeychainScene;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<JWEObject> result;
|
||||||
private final DeviceKey deviceKey;
|
private final DeviceKey deviceKey;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
|
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<JWEObject> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.keychainManager = keychainManager;
|
this.keychainManager = keychainManager;
|
||||||
window.setTitle(windowTitle);
|
window.setTitle(windowTitle);
|
||||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
|
||||||
this.authFlowScene = authFlowScene;
|
this.authFlowScene = authFlowScene;
|
||||||
this.noKeychainScene = noKeychainScene;
|
this.noKeychainScene = noKeychainScene;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
@@ -60,7 +60,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
var keypair = deviceKey.get();
|
var keypair = deviceKey.get();
|
||||||
showWindow(authFlowScene);
|
showWindow(authFlowScene);
|
||||||
var jwe = result.get();
|
var jwe = result.get();
|
||||||
return jwe.decryptMasterkey(keypair.getPrivate());
|
return JWEHelper.decrypt(jwe, keypair.getPrivate());
|
||||||
} catch (NoKeychainAccessProviderException e) {
|
} catch (NoKeychainAccessProviderException e) {
|
||||||
showWindow(noKeychainScene);
|
showWindow(noKeychainScene);
|
||||||
throw new UnlockCancelledException("Unlock canceled due to missing prerequisites", e);
|
throw new UnlockCancelledException("Unlock canceled due to missing prerequisites", e);
|
||||||
|
|||||||
@@ -1,141 +1,36 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.nimbusds.jose.EncryptionMethod;
|
import com.google.common.io.BaseEncoding;
|
||||||
import com.nimbusds.jose.JOSEException;
|
import com.nimbusds.jose.JOSEException;
|
||||||
import com.nimbusds.jose.JWEAlgorithm;
|
|
||||||
import com.nimbusds.jose.JWEHeader;
|
|
||||||
import com.nimbusds.jose.JWEObject;
|
import com.nimbusds.jose.JWEObject;
|
||||||
import com.nimbusds.jose.Payload;
|
|
||||||
import com.nimbusds.jose.crypto.ECDHDecrypter;
|
import com.nimbusds.jose.crypto.ECDHDecrypter;
|
||||||
import com.nimbusds.jose.crypto.ECDHEncrypter;
|
|
||||||
import com.nimbusds.jose.crypto.PasswordBasedDecrypter;
|
|
||||||
import com.nimbusds.jose.jwk.Curve;
|
|
||||||
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
|
|
||||||
import org.cryptomator.cryptolib.api.CryptoException;
|
|
||||||
import org.cryptomator.cryptolib.api.Masterkey;
|
import org.cryptomator.cryptolib.api.Masterkey;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.interfaces.ECPrivateKey;
|
import java.security.interfaces.ECPrivateKey;
|
||||||
import java.security.interfaces.ECPublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
class JWEHelper {
|
class JWEHelper {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JWEHelper.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JWEHelper.class);
|
||||||
private static final String JWE_PAYLOAD_KEY_FIELD = "key";
|
private static final String JWE_PAYLOAD_MASTERKEY_FIELD = "key";
|
||||||
private static final String EC_ALG = "EC";
|
|
||||||
|
|
||||||
private JWEHelper() {}
|
private JWEHelper(){}
|
||||||
|
|
||||||
public static JWEObject encryptUserKey(ECPrivateKey userKey, ECPublicKey deviceKey) {
|
public static Masterkey decrypt(JWEObject jwe, ECPrivateKey privateKey) throws MasterkeyLoadingFailedException {
|
||||||
return encryptKey(userKey, deviceKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, String setupCode) throws InvalidJweKeyException {
|
|
||||||
try {
|
|
||||||
jwe.decrypt(new PasswordBasedDecrypter(setupCode));
|
|
||||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
|
|
||||||
} catch (JOSEException e) {
|
|
||||||
throw new InvalidJweKeyException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, ECPrivateKey deviceKey) throws InvalidJweKeyException {
|
|
||||||
try {
|
|
||||||
jwe.decrypt(new ECDHDecrypter(deviceKey));
|
|
||||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
|
|
||||||
} catch (JOSEException e) {
|
|
||||||
throw new InvalidJweKeyException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to decode a DER-encoded EC private key.
|
|
||||||
*
|
|
||||||
* @param encoded DER-encoded EC private key
|
|
||||||
* @return the decoded key
|
|
||||||
* @throws KeyDecodeFailedException On malformed input
|
|
||||||
*/
|
|
||||||
public static ECPrivateKey decodeECPrivateKey(byte[] encoded) throws KeyDecodeFailedException {
|
|
||||||
try {
|
|
||||||
KeyFactory factory = KeyFactory.getInstance(EC_ALG);
|
|
||||||
var privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
|
|
||||||
if (privateKey instanceof ECPrivateKey ecPrivateKey) {
|
|
||||||
return ecPrivateKey;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException(EC_ALG + " key factory not generating ECPrivateKeys");
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new IllegalStateException(EC_ALG + " not supported");
|
|
||||||
} catch (InvalidKeySpecException e) {
|
|
||||||
throw new KeyDecodeFailedException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to decode a DER-encoded EC public key.
|
|
||||||
*
|
|
||||||
* @param encoded DER-encoded EC public key
|
|
||||||
* @return the decoded key
|
|
||||||
* @throws KeyDecodeFailedException On malformed input
|
|
||||||
*/
|
|
||||||
public static ECPublicKey decodeECPublicKey(byte[] encoded) throws KeyDecodeFailedException {
|
|
||||||
try {
|
|
||||||
KeyFactory factory = KeyFactory.getInstance(EC_ALG);
|
|
||||||
var publicKey = factory.generatePublic(new X509EncodedKeySpec(encoded));
|
|
||||||
if (publicKey instanceof ECPublicKey ecPublicKey) {
|
|
||||||
return ecPublicKey;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException(EC_ALG + " key factory not generating ECPublicKeys");
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new IllegalStateException(EC_ALG + " not supported");
|
|
||||||
} catch (InvalidKeySpecException e) {
|
|
||||||
throw new KeyDecodeFailedException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JWEObject encryptVaultKey(Masterkey vaultKey, ECPublicKey userKey) {
|
|
||||||
return encryptKey(vaultKey, userKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JWEObject encryptKey(Key key, ECPublicKey userKey) {
|
|
||||||
try {
|
|
||||||
var encodedVaultKey = Base64.getEncoder().encodeToString(key.getEncoded());
|
|
||||||
var keyGen = new ECKeyGenerator(Curve.P_384);
|
|
||||||
var ephemeralKeyPair = keyGen.generate();
|
|
||||||
var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
|
|
||||||
var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedVaultKey));
|
|
||||||
var jwe = new JWEObject(header, payload);
|
|
||||||
jwe.encrypt(new ECDHEncrypter(userKey));
|
|
||||||
return jwe;
|
|
||||||
} catch (JOSEException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Masterkey decryptVaultKey(JWEObject jwe, ECPrivateKey privateKey) throws InvalidJweKeyException {
|
|
||||||
try {
|
try {
|
||||||
jwe.decrypt(new ECDHDecrypter(privateKey));
|
jwe.decrypt(new ECDHDecrypter(privateKey));
|
||||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, Masterkey::new);
|
return readKey(jwe);
|
||||||
} catch (JOSEException e) {
|
} catch (JOSEException e) {
|
||||||
throw new InvalidJweKeyException(e);
|
LOG.warn("Failed to decrypt JWE: {}", jwe);
|
||||||
|
throw new MasterkeyLoadingFailedException("Failed to decrypt JWE", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T readKey(JWEObject jwe, String keyField, Function<byte[], T> rawKeyFactory) throws MasterkeyLoadingFailedException {
|
private static Masterkey readKey(JWEObject jwe) throws MasterkeyLoadingFailedException {
|
||||||
Preconditions.checkArgument(jwe.getState() == JWEObject.State.DECRYPTED);
|
Preconditions.checkArgument(jwe.getState() == JWEObject.State.DECRYPTED);
|
||||||
var fields = jwe.getPayload().toJSONObject();
|
var fields = jwe.getPayload().toJSONObject();
|
||||||
if (fields == null) {
|
if (fields == null) {
|
||||||
@@ -144,31 +39,17 @@ class JWEHelper {
|
|||||||
}
|
}
|
||||||
var keyBytes = new byte[0];
|
var keyBytes = new byte[0];
|
||||||
try {
|
try {
|
||||||
if (fields.get(keyField) instanceof String key) {
|
if (fields.get(JWE_PAYLOAD_MASTERKEY_FIELD) instanceof String key) {
|
||||||
keyBytes = Base64.getDecoder().decode(key);
|
keyBytes = BaseEncoding.base64().decode(key);
|
||||||
return rawKeyFactory.apply(keyBytes);
|
return new Masterkey(keyBytes);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("JWE payload doesn't contain field " + keyField);
|
throw new IllegalArgumentException("JWE payload doesn't contain field " + JWE_PAYLOAD_MASTERKEY_FIELD);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException | KeyDecodeFailedException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
|
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
|
||||||
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
|
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
|
||||||
} finally {
|
} finally {
|
||||||
Arrays.fill(keyBytes, (byte) 0x00);
|
Arrays.fill(keyBytes, (byte) 0x00);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InvalidJweKeyException extends MasterkeyLoadingFailedException {
|
|
||||||
|
|
||||||
public InvalidJweKeyException(Throwable cause) {
|
|
||||||
super("Invalid key", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class KeyDecodeFailedException extends CryptoException {
|
|
||||||
|
|
||||||
public KeyDecodeFailedException(Throwable cause) {
|
|
||||||
super("Malformed key", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
|
||||||
|
|
||||||
import com.auth0.jwt.JWT;
|
|
||||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
|
||||||
import com.fasterxml.jackson.core.JacksonException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import dagger.Lazy;
|
|
||||||
import org.cryptomator.common.settings.DeviceKey;
|
|
||||||
import org.cryptomator.cryptolib.common.P384KeyPair;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ContentDisplay;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import javafx.stage.WindowEvent;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
@KeyLoadingScoped
|
|
||||||
public class LegacyRegisterDeviceController implements FxController {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(LegacyRegisterDeviceController.class);
|
|
||||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
|
||||||
private static final List<Integer> EXPECTED_RESPONSE_CODES = List.of(201, 409);
|
|
||||||
|
|
||||||
private final Stage window;
|
|
||||||
private final HubConfig hubConfig;
|
|
||||||
private final String bearerToken;
|
|
||||||
private final Lazy<Scene> registerSuccessScene;
|
|
||||||
private final Lazy<Scene> registerFailedScene;
|
|
||||||
private final String deviceId;
|
|
||||||
private final P384KeyPair keyPair;
|
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
|
||||||
private final DecodedJWT jwt;
|
|
||||||
private final HttpClient httpClient;
|
|
||||||
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
|
|
||||||
|
|
||||||
public TextField deviceNameField;
|
|
||||||
public Button registerBtn;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public LegacyRegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
|
||||||
this.window = window;
|
|
||||||
this.hubConfig = hubConfig;
|
|
||||||
this.deviceId = deviceId;
|
|
||||||
this.keyPair = Objects.requireNonNull(deviceKey.get());
|
|
||||||
this.result = result;
|
|
||||||
this.bearerToken = Objects.requireNonNull(bearerToken.get());
|
|
||||||
this.registerSuccessScene = registerSuccessScene;
|
|
||||||
this.registerFailedScene = registerFailedScene;
|
|
||||||
this.jwt = JWT.decode(this.bearerToken);
|
|
||||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
|
||||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize() {
|
|
||||||
deviceNameField.setText(determineHostname());
|
|
||||||
deviceNameField.textProperty().addListener(observable -> deviceNameAlreadyExists.set(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String determineHostname() {
|
|
||||||
try {
|
|
||||||
var hostName = InetAddress.getLocalHost().getHostName();
|
|
||||||
return Objects.requireNonNullElse(hostName, "");
|
|
||||||
} catch (IOException e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void register() {
|
|
||||||
deviceNameAlreadyExists.set(false);
|
|
||||||
registerBtn.setContentDisplay(ContentDisplay.LEFT);
|
|
||||||
registerBtn.setDisable(true);
|
|
||||||
|
|
||||||
var deviceUri = URI.create(hubConfig.devicesResourceUrl + deviceId);
|
|
||||||
var deviceKey = keyPair.getPublic().getEncoded();
|
|
||||||
var dto = new CreateDeviceDto();
|
|
||||||
dto.id = deviceId;
|
|
||||||
dto.name = deviceNameField.getText();
|
|
||||||
dto.publicKey = Base64.getUrlEncoder().withoutPadding().encodeToString(deviceKey);
|
|
||||||
var json = toJson(dto);
|
|
||||||
var request = HttpRequest.newBuilder(deviceUri) //
|
|
||||||
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
|
||||||
.header("Content-Type", "application/json") //
|
|
||||||
.build();
|
|
||||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) //
|
|
||||||
.thenApply(response -> {
|
|
||||||
if (EXPECTED_RESPONSE_CODES.contains(response.statusCode())) {
|
|
||||||
return response;
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Server answered with unexpected status code " + response.statusCode());
|
|
||||||
}
|
|
||||||
}).handleAsync((response, throwable) -> {
|
|
||||||
if (response != null) {
|
|
||||||
this.handleResponse(response);
|
|
||||||
} else {
|
|
||||||
this.registrationFailed(throwable);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}, Platform::runLater);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toJson(CreateDeviceDto dto) {
|
|
||||||
try {
|
|
||||||
return JSON.writer().writeValueAsString(dto);
|
|
||||||
} catch (JacksonException e) {
|
|
||||||
throw new IllegalStateException("Failed to serialize DTO", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleResponse(HttpResponse<Void> voidHttpResponse) {
|
|
||||||
assert EXPECTED_RESPONSE_CODES.contains(voidHttpResponse.statusCode());
|
|
||||||
|
|
||||||
if (voidHttpResponse.statusCode() == 409) {
|
|
||||||
deviceNameAlreadyExists.set(true);
|
|
||||||
registerBtn.setContentDisplay(ContentDisplay.TEXT_ONLY);
|
|
||||||
registerBtn.setDisable(false);
|
|
||||||
} else {
|
|
||||||
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
|
|
||||||
window.setScene(registerSuccessScene.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registrationFailed(Throwable cause) {
|
|
||||||
LOG.warn("Device registration failed.", cause);
|
|
||||||
window.setScene(registerFailedScene.get());
|
|
||||||
result.completeExceptionally(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void close() {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void windowClosed(WindowEvent windowEvent) {
|
|
||||||
result.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter */
|
|
||||||
|
|
||||||
public String getUserName() {
|
|
||||||
return jwt.getClaim("email").asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--- Getters & Setters
|
|
||||||
|
|
||||||
public BooleanProperty deviceNameAlreadyExistsProperty() {
|
|
||||||
return deviceNameAlreadyExists;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getDeviceNameAlreadyExists() {
|
|
||||||
return deviceNameAlreadyExists.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CreateDeviceDto {
|
|
||||||
public String id;
|
|
||||||
public String name;
|
|
||||||
public final String type = "DESKTOP";
|
|
||||||
public String publicKey;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
|
||||||
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@KeyLoadingScoped
|
|
||||||
public class LegacyRegisterSuccessController implements FxController {
|
|
||||||
private final Stage window;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public LegacyRegisterSuccessController(@KeyLoading Stage window) {
|
|
||||||
this.window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void close() {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.nimbusds.jose.JWEObject;
|
import com.nimbusds.jose.JWEObject;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
@@ -12,8 +8,6 @@ import org.cryptomator.ui.common.FxmlFile;
|
|||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -23,15 +17,14 @@ import javafx.scene.Scene;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -40,36 +33,27 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
public class ReceiveKeyController implements FxController {
|
public class ReceiveKeyController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
|
|
||||||
private static final String SCHEME_PREFIX = "hub+";
|
private static final String SCHEME_PREFIX = "hub+";
|
||||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
|
||||||
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
|
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final HubConfig hubConfig;
|
|
||||||
private final String vaultId;
|
|
||||||
private final String deviceId;
|
private final String deviceId;
|
||||||
private final String bearerToken;
|
private final String bearerToken;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<JWEObject> result;
|
||||||
private final Lazy<Scene> registerDeviceScene;
|
private final Lazy<Scene> registerDeviceScene;
|
||||||
private final Lazy<Scene> legacyRegisterDeviceScene;
|
|
||||||
private final Lazy<Scene> unauthorizedScene;
|
private final Lazy<Scene> unauthorizedScene;
|
||||||
private final Lazy<Scene> accountInitializationScene;
|
private final URI vaultBaseUri;
|
||||||
private final Lazy<Scene> invalidLicenseScene;
|
private final Lazy<Scene> invalidLicenseScene;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<JWEObject> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.hubConfig = hubConfig;
|
|
||||||
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
|
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.registerDeviceScene = registerDeviceScene;
|
this.registerDeviceScene = registerDeviceScene;
|
||||||
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
|
|
||||||
this.unauthorizedScene = unauthorizedScene;
|
this.unauthorizedScene = unauthorizedScene;
|
||||||
this.accountInitializationScene = accountInitializationScene;
|
this.vaultBaseUri = getVaultBaseUri(vault);
|
||||||
this.invalidLicenseScene = invalidLicenseScene;
|
this.invalidLicenseScene = invalidLicenseScene;
|
||||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||||
@@ -77,158 +61,23 @@ public class ReceiveKeyController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
receiveKey();
|
var keyUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
|
||||||
}
|
var request = HttpRequest.newBuilder(keyUri) //
|
||||||
|
|
||||||
public void receiveKey() {
|
|
||||||
requestApiConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP 0 (Request): GET /api/config
|
|
||||||
*/
|
|
||||||
private void requestApiConfig() {
|
|
||||||
var configUri = hubConfig.URIs.API."config";
|
|
||||||
var request = HttpRequest.newBuilder(configUri) //
|
|
||||||
.GET() //
|
|
||||||
.timeout(REQ_TIMEOUT) //
|
|
||||||
.build();
|
|
||||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
|
|
||||||
.thenAcceptAsync(this::receivedApiConfig, Platform::runLater) //
|
|
||||||
.exceptionally(this::retrievalFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP 0 (Response): GET /api/config
|
|
||||||
*
|
|
||||||
* @param response Response
|
|
||||||
*/
|
|
||||||
private void receivedApiConfig(HttpResponse<String> response) {
|
|
||||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
|
||||||
Preconditions.checkState(response.statusCode() == 200, "Unexpected response " + response.statusCode());
|
|
||||||
try {
|
|
||||||
var config = JSON.reader().readValue(response.body(), ConfigDto.class);
|
|
||||||
if (config.apiLevel >= 1) {
|
|
||||||
requestDeviceData();
|
|
||||||
} else {
|
|
||||||
requestLegacyAccessToken();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP 1 (Request): GET user key for this device
|
|
||||||
*/
|
|
||||||
private void requestDeviceData() {
|
|
||||||
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
|
|
||||||
var request = HttpRequest.newBuilder(deviceUri) //
|
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
.GET() //
|
.GET() //
|
||||||
.timeout(REQ_TIMEOUT) //
|
|
||||||
.build();
|
.build();
|
||||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
|
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) //
|
||||||
.thenAcceptAsync(this::receivedDeviceData) //
|
.thenAcceptAsync(this::loadedExistingKey, Platform::runLater) //
|
||||||
.exceptionally(this::retrievalFailed);
|
.exceptionally(this::retrievalFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void loadedExistingKey(HttpResponse<InputStream> response) {
|
||||||
* STEP 1 (Response): GET user key for this device
|
|
||||||
*
|
|
||||||
* @param response Response
|
|
||||||
*/
|
|
||||||
private void receivedDeviceData(HttpResponse<String> response) {
|
|
||||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
|
||||||
try {
|
try {
|
||||||
switch (response.statusCode()) {
|
switch (response.statusCode()) {
|
||||||
case 200 -> {
|
case 200 -> retrievalSucceeded(response);
|
||||||
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
|
|
||||||
requestVaultMasterkey(device.userPrivateKey);
|
|
||||||
}
|
|
||||||
case 404 -> Platform.runLater(this::needsDeviceRegistration);
|
|
||||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void needsDeviceRegistration() {
|
|
||||||
window.setScene(registerDeviceScene.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP 2 (Request): GET vault key for this user
|
|
||||||
*/
|
|
||||||
private void requestVaultMasterkey(String encryptedUserKey) {
|
|
||||||
var vaultKeyUri = hubConfig.URIs.API."vaults/\{vaultId}/access-token";
|
|
||||||
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
|
||||||
.GET() //
|
|
||||||
.timeout(REQ_TIMEOUT) //
|
|
||||||
.build();
|
|
||||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
|
|
||||||
.thenAcceptAsync(response -> receivedVaultMasterkey(encryptedUserKey, response), Platform::runLater) //
|
|
||||||
.exceptionally(this::retrievalFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP 2 (Response): GET vault key for this user
|
|
||||||
*
|
|
||||||
* @param response Response
|
|
||||||
*/
|
|
||||||
private void receivedVaultMasterkey(String encryptedUserKey, HttpResponse<String> response) {
|
|
||||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
|
||||||
switch (response.statusCode()) {
|
|
||||||
case 200 -> receivedBothEncryptedKeys(response.body(), encryptedUserKey);
|
|
||||||
case 402 -> licenseExceeded();
|
|
||||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
|
|
||||||
case 449 -> accountInitializationRequired();
|
|
||||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) {
|
|
||||||
try {
|
|
||||||
var vaultKeyJwe = JWEObject.parse(encryptedVaultKey);
|
|
||||||
var userKeyJwe = JWEObject.parse(encryptedUserKey);
|
|
||||||
result.complete(ReceivedKey.vaultKeyAndUserKey(vaultKeyJwe, userKeyJwe));
|
|
||||||
window.close();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
retrievalFailed(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LEGACY FALLBACK (Request): GET the legacy access token from Hub 1.x
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private void requestLegacyAccessToken() {
|
|
||||||
var legacyAccessTokenUri = hubConfig.URIs.API."vaults/\{vaultId}/keys/\{deviceId}";
|
|
||||||
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
|
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
|
||||||
.GET() //
|
|
||||||
.timeout(REQ_TIMEOUT) //
|
|
||||||
.build();
|
|
||||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.US_ASCII)) //
|
|
||||||
.thenAcceptAsync(this::receivedLegacyAccessTokenResponse, Platform::runLater) //
|
|
||||||
.exceptionally(this::retrievalFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LEGACY FALLBACK (Response)
|
|
||||||
*
|
|
||||||
* @param response Response
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private void receivedLegacyAccessTokenResponse(HttpResponse<String> response) {
|
|
||||||
try {
|
|
||||||
switch (response.statusCode()) {
|
|
||||||
case 200 -> receivedLegacyAccessTokenSuccess(response.body());
|
|
||||||
case 402 -> licenseExceeded();
|
case 402 -> licenseExceeded();
|
||||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access
|
case 403 -> accessNotGranted();
|
||||||
case 404 -> needsLegacyDeviceRegistration();
|
case 404 -> needsDeviceRegistration();
|
||||||
default -> throw new IOException("Unexpected response " + response.statusCode());
|
default -> throw new IOException("Unexpected response " + response.statusCode());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -236,11 +85,10 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
private void retrievalSucceeded(HttpResponse<InputStream> response) throws IOException {
|
||||||
private void receivedLegacyAccessTokenSuccess(String rawToken) throws IOException {
|
|
||||||
try {
|
try {
|
||||||
var token = JWEObject.parse(rawToken);
|
var string = HttpHelper.readBody(response);
|
||||||
result.complete(ReceivedKey.legacyDeviceKey(token));
|
result.complete(JWEObject.parse(string));
|
||||||
window.close();
|
window.close();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
throw new IOException("Failed to parse JWE", e);
|
throw new IOException("Failed to parse JWE", e);
|
||||||
@@ -251,19 +99,14 @@ public class ReceiveKeyController implements FxController {
|
|||||||
window.setScene(invalidLicenseScene.get());
|
window.setScene(invalidLicenseScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
private void needsDeviceRegistration() {
|
||||||
private void needsLegacyDeviceRegistration() {
|
window.setScene(registerDeviceScene.get());
|
||||||
window.setScene(legacyRegisterDeviceScene.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void accessNotGranted() {
|
private void accessNotGranted() {
|
||||||
window.setScene(unauthorizedScene.get());
|
window.setScene(unauthorizedScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void accountInitializationRequired() {
|
|
||||||
window.setScene(accountInitializationScene.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Void retrievalFailed(Throwable cause) {
|
private Void retrievalFailed(Throwable cause) {
|
||||||
result.completeExceptionally(cause);
|
result.completeExceptionally(cause);
|
||||||
return null;
|
return null;
|
||||||
@@ -287,15 +130,16 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String extractVaultId(URI vaultKeyUri) {
|
private static URI getVaultBaseUri(Vault vault) {
|
||||||
assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX);
|
try {
|
||||||
var path = vaultKeyUri.getPath();
|
var kid = vault.getVaultConfigCache().get().getKeyId();
|
||||||
return path.substring(path.lastIndexOf('/') + 1);
|
assert kid.getScheme().startsWith(SCHEME_PREFIX);
|
||||||
|
var hubUriScheme = kid.getScheme().substring(SCHEME_PREFIX.length());
|
||||||
|
return new URI(hubUriScheme, kid.getSchemeSpecificPart(), kid.getFragment());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new IllegalStateException("URI constructed from params known to be valid", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user