mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-21 18:16:54 -04:00
Merge branch 'release/1.12.0'
This commit is contained in:
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -26,6 +26,7 @@ body:
|
||||
Examples:
|
||||
- Operating System: Windows 10
|
||||
- Cryptomator: 1.5.16
|
||||
- OneDrive: 23.226
|
||||
- LibreOffice: 7.1.4
|
||||
value: |
|
||||
- Operating System:
|
||||
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -6,7 +6,7 @@ updates:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "06:00"
|
||||
timezone: "UTC"
|
||||
timezone: "Etc/UTC"
|
||||
groups:
|
||||
java-test-dependencies:
|
||||
patterns:
|
||||
|
||||
19
.github/workflows/appimage.yml
vendored
19
.github/workflows/appimage.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
JAVA_VERSION: '21.0.2+13'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -29,16 +29,16 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
appimage-suffix: x86_64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
|
||||
openjfx-sha: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
|
||||
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/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: '871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -74,6 +74,7 @@ jobs:
|
||||
cp LICENSE.txt target
|
||||
cp target/cryptomator-*.jar target/mods
|
||||
- name: Run jlink
|
||||
#Remark: no compression is applied for improved build compression later (here appimage)
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jlink
|
||||
--verbose
|
||||
@@ -84,7 +85,7 @@ jobs:
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress=1
|
||||
--compress zip-0
|
||||
- name: Prepare additional launcher
|
||||
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
||||
env:
|
||||
@@ -102,7 +103,7 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||
@@ -163,7 +164,7 @@ jobs:
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appimage-${{ matrix.appimage-suffix }}
|
||||
path: |
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -19,13 +19,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
|
||||
4
.github/workflows/check-jdk-updates.yml
vendored
4
.github/workflows/check-jdk-updates.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
outputs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ env.JDK_VERSION }}
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
||||
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: ${{ env.JDK_VENDOR }}
|
||||
|
||||
16
.github/workflows/debian.yml
vendored
16
.github/workflows/debian.yml
vendored
@@ -17,13 +17,13 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
JAVA_VERSION: '21.0.2+13'
|
||||
COFFEELIBS_JDK: 21
|
||||
COFFEELIBS_JDK_VERSION: '21.0.1+12-0ppa1'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: 'f522ac2ae4bdd61f0219b7b8d2058ff72a22f36a44378453bcfdcd82f8f5e08c'
|
||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_linux-aarch64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AARCH64_HASH: 'c0d80ebbe0aab404ef9ad8b46c05bf533a1e40b39b2720eebd9238d81f6326ca'
|
||||
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:
|
||||
build:
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
run: |
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-deb-package
|
||||
path: |
|
||||
|
||||
17
.github/workflows/dependency-check.yml
vendored
Normal file
17
.github/workflows/dependency-check.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
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@main
|
||||
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 }}
|
||||
2
.github/workflows/dl-stats.yml
vendored
2
.github/workflows/dl-stats.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- name: Get download count of latest releases
|
||||
id: get-stats
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const query = `query($owner:String!, $name:String!) {
|
||||
|
||||
2
.github/workflows/error-db.yml
vendored
2
.github/workflows/error-db.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Query Discussion Data
|
||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
||||
id: query-data
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||
|
||||
2
.github/workflows/get-version.yml
vendored
2
.github/workflows/get-version.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
||||
19
.github/workflows/mac-dmg.yml
vendored
19
.github/workflows/mac-dmg.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
JAVA_VERSION: '21.0.2+13'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
@@ -37,19 +37,19 @@ jobs:
|
||||
output-suffix: x64
|
||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||
fuse-lib: macFUSE
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-x64_bin-jmods.zip'
|
||||
openjfx-sha: '55b8ff7453d59c89ae129f6c9c5ad7b09a5d359568811b376ac1766c14d6a17c'
|
||||
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]
|
||||
architecture: aarch64
|
||||
output-suffix: arm64
|
||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
||||
fuse-lib: FUSE-T
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'c60f5f19aa847e0e620e0b011e5de68f2c6755641c2141cec27a0b89f612beaf'
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: '7afaa1c57a6cc3c384d636e597b9a5364693e2db4aaec0a6e63d2fa964400b58'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -85,6 +85,7 @@ jobs:
|
||||
cp LICENSE.txt target
|
||||
cp target/cryptomator-*.jar target/mods
|
||||
- name: Run jlink
|
||||
#Remark: no compression is applied for improved build compression later (here dmg)
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jlink
|
||||
--verbose
|
||||
@@ -95,7 +96,7 @@ jobs:
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress=1
|
||||
--compress zip-0
|
||||
- name: Run jpackage
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jpackage
|
||||
@@ -108,7 +109,7 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
||||
@@ -249,7 +250,7 @@ jobs:
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dmg-${{ matrix.output-suffix }}
|
||||
path: Cryptomator-*.dmg
|
||||
|
||||
2
.github/workflows/no-response.yml
vendored
2
.github/workflows/no-response.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 0
|
||||
|
||||
2
.github/workflows/pullrequest.yml
vendored
2
.github/workflows/pullrequest.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
||||
29
.github/workflows/release-check.yml
vendored
29
.github/workflows/release-check.yml
vendored
@@ -10,12 +10,22 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: 21
|
||||
|
||||
jobs:
|
||||
release-check-precondition:
|
||||
check-preconditions:
|
||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- id: validate-pom-version
|
||||
name: Validate POM version
|
||||
run: |
|
||||
@@ -37,4 +47,19 @@ jobs:
|
||||
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"
|
||||
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 }}
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 365
|
||||
days-before-close: 90
|
||||
|
||||
31
.github/workflows/win-exe.yml
vendored
31
.github/workflows/win-exe.yml
vendored
@@ -15,11 +15,11 @@ on:
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'zulu'
|
||||
JAVA_VERSION: '21.0.1+12'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
|
||||
JAVA_VERSION: '21.0.2+13'
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/download/1.0.0-beta9/winfsp-uninstaller.exe'
|
||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/download/1.0.0/winfsp-uninstaller.exe'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -79,6 +79,7 @@ jobs:
|
||||
cp LICENSE.txt target
|
||||
cp target/cryptomator-*.jar target/mods
|
||||
- name: Run jlink
|
||||
#Remark: no compression is applied for improved build compression later (here msi)
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jlink
|
||||
--verbose
|
||||
@@ -89,7 +90,7 @@ jobs:
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress=1
|
||||
--compress zip-0
|
||||
- name: Change win-console flag if debug is active
|
||||
if: ${{ inputs.isDebug }}
|
||||
run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV
|
||||
@@ -105,7 +106,7 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
|
||||
@@ -213,7 +214,7 @@ jobs:
|
||||
--dest installer
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||
--win-menu
|
||||
--win-dir-chooser
|
||||
@@ -245,7 +246,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: msi
|
||||
path: |
|
||||
@@ -269,13 +270,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: msi
|
||||
path: dist/win/bundle/resources
|
||||
- name: Strip version info from msi file name
|
||||
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
@@ -308,7 +309,7 @@ jobs:
|
||||
-out dist/win/bundle/
|
||||
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
-dBundleVendor="Skymatic GmbH"
|
||||
-dBundleCopyright="(C) 2016 - 2023 Skymatic GmbH"
|
||||
-dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
|
||||
-dAboutUrl="https://cryptomator.org"
|
||||
-dHelpUrl="https://cryptomator.org/contact"
|
||||
-dUpdateUrl="https://cryptomator.org/downloads/"
|
||||
@@ -356,7 +357,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: exe
|
||||
path: |
|
||||
@@ -380,12 +381,12 @@ jobs:
|
||||
needs: [build-msi, build-exe]
|
||||
steps:
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: msi
|
||||
path: msi
|
||||
- name: Download .exe
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: exe
|
||||
path: exe
|
||||
|
||||
@@ -86,7 +86,7 @@ For more information on the security details visit [cryptomator.org](https://doc
|
||||
|
||||
### Dependencies
|
||||
|
||||
* JDK 19 (e.g. temurin)
|
||||
* JDK 21 (e.g. temurin, zulu)
|
||||
* Maven 3
|
||||
|
||||
### Run Maven
|
||||
|
||||
41
dist/linux/appimage/build.sh
vendored
41
dist/linux/appimage/build.sh
vendored
@@ -8,11 +8,14 @@ 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
|
||||
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 unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
|
||||
|
||||
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
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}
|
||||
|
||||
# compile
|
||||
@@ -20,17 +23,45 @@ mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
|
||||
cp ../../../LICENSE.txt ../../../target
|
||||
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
|
||||
${JAVA_HOME}/bin/jlink \
|
||||
--verbose \
|
||||
--output runtime \
|
||||
--module-path "${JAVA_HOME}/jmods" \
|
||||
--module-path "${JAVA_HOME}/jmods:openjfx-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 \
|
||||
--strip-native-commands \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
--compress=1
|
||||
--compress zip-0
|
||||
|
||||
# create app dir
|
||||
envsubst '${SEMVER_STR} ${REVISION_NUM}' < ../launcher-gtk2.properties > launcher-gtk2.properties
|
||||
@@ -46,7 +77,7 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--vendor "Skymatic GmbH" \
|
||||
--java-options "--enable-preview" \
|
||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--app-version "${VERSION}.${REVISION_NO}" \
|
||||
@@ -97,5 +128,5 @@ chmod +x /tmp/appimagetool.AppImage
|
||||
echo ""
|
||||
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
|
||||
echo ""
|
||||
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir jni runtime squashfs-root; rm launcher-gtk2.properties /tmp/appimagetool.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 ""
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<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"/>
|
||||
|
||||
4
dist/linux/debian/copyright
vendored
4
dist/linux/debian/copyright
vendored
@@ -4,11 +4,11 @@ Upstream-Contact: Cryptomator <info@cryptomator.org>
|
||||
Source: https://cryptomator.org
|
||||
|
||||
Files: *
|
||||
Copyright: 2016-2023 Skymatic GmbH
|
||||
Copyright: 2016-2024 Skymatic GmbH
|
||||
License: GPL-3+
|
||||
|
||||
Files: debian/org.cryptomator.Cryptomator.appdata.xml
|
||||
Copyright: 2016-2023 Skymatic GmbH
|
||||
Copyright: 2016-2024 Skymatic GmbH
|
||||
License: FSFAP
|
||||
|
||||
License: GPL-3+
|
||||
|
||||
5
dist/linux/debian/rules
vendored
5
dist/linux/debian/rules
vendored
@@ -24,6 +24,7 @@ override_dh_auto_clean:
|
||||
override_dh_auto_build:
|
||||
mkdir resources
|
||||
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 \
|
||||
--output runtime \
|
||||
--module-path "${JMODS_PATH}" \
|
||||
@@ -32,7 +33,7 @@ override_dh_auto_build:
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
--compress=2
|
||||
--compress zip-0
|
||||
$(JAVA_HOME)/bin/jpackage \
|
||||
--type app-image \
|
||||
--runtime-image runtime \
|
||||
@@ -44,7 +45,7 @@ override_dh_auto_build:
|
||||
--vendor "Skymatic GmbH" \
|
||||
--java-options "--enable-preview" \
|
||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||
--copyright "(C) 2016 - 2023 Skymatic GmbH" \
|
||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||
|
||||
6
dist/mac/dmg/build.sh
vendored
6
dist/mac/dmg/build.sh
vendored
@@ -21,7 +21,7 @@ rm -rf runtime dmg *.app *.dmg
|
||||
# set variables
|
||||
APP_NAME="Cryptomator"
|
||||
VENDOR="Skymatic GmbH"
|
||||
COPYRIGHT_YEARS="2016 - 2023"
|
||||
COPYRIGHT_YEARS="2016 - 2024"
|
||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||
@@ -35,7 +35,7 @@ if [ "$(machine)" = "arm64e" ]; then
|
||||
else
|
||||
ARCH="x64"
|
||||
fi
|
||||
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/20.0.2/openjfx-20.0.2_osx-${ARCH}_bin-jmods.zip"
|
||||
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-${ARCH}_bin-jmods.zip"
|
||||
|
||||
# check preconditions
|
||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||
@@ -76,7 +76,7 @@ ${JAVA_HOME}/bin/jlink \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
--compress=1
|
||||
--compress zip-0
|
||||
|
||||
# create app dir
|
||||
${JAVA_HOME}/bin/jpackage \
|
||||
|
||||
2
dist/mac/dmg/resources/licenseTemplate.ftl
vendored
2
dist/mac/dmg/resources/licenseTemplate.ftl
vendored
@@ -17,7 +17,7 @@
|
||||
\f1\b0 \
|
||||
\
|
||||
|
||||
\f0\b \'a9 2016 \'96 2023 Skymatic GmbH
|
||||
\f0\b \'a9 2016 \'96 2024 Skymatic GmbH
|
||||
\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.\
|
||||
|
||||
10
dist/win/build.ps1
vendored
10
dist/win/build.ps1
vendored
@@ -51,9 +51,9 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
||||
}
|
||||
|
||||
## download jfx jmods
|
||||
$jmodsVersion='20.0.2'
|
||||
$jmodsVersion='21.0.1'
|
||||
$jmodsUrl = "https://download2.gluonhq.com/openjfx/${jmodsVersion}/openjfx-${jmodsVersion}_windows-x64_bin-jmods.zip"
|
||||
$jfxJmodsChecksum = '18625bbc13c57dbf802486564247a8d8cab72ec558c240a401bf6440384ebd77'
|
||||
$jfxJmodsChecksum = 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
||||
$jfxJmodsZip = '.\resources\jfxJmods.zip'
|
||||
if( !(Test-Path -Path $jfxJmodsZip) ) {
|
||||
Write-Output "Downloading ${jmodsUrl}..."
|
||||
@@ -69,7 +69,7 @@ Expand-Archive -Path $jfxJmodsZip -Force -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" `
|
||||
--verbose `
|
||||
--output runtime `
|
||||
@@ -79,7 +79,7 @@ Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\ja
|
||||
--no-header-files `
|
||||
--no-man-pages `
|
||||
--strip-debug `
|
||||
--compress=1
|
||||
--compress "zip-0" #do not compress to have improved msi compression
|
||||
|
||||
$appPath = ".\$AppName"
|
||||
if ($clean -and (Test-Path -Path $appPath)) {
|
||||
@@ -181,7 +181,7 @@ Write-Output "Downloading ${winfspMsiUrl}..."
|
||||
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/download/1.0.0-beta9/winfsp-uninstaller.exe'
|
||||
$winfspUninstaller= 'https://github.com/cryptomator/winfsp-uninstaller/releases/download/1.0.0/winfsp-uninstaller.exe'
|
||||
Write-Output "Downloading ${winfspUninstaller}..."
|
||||
Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninstaller.exe" # redirects are followed by default
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
\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
|
||||
\par
|
||||
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
|
||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\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
|
||||
\par
|
||||
|
||||
2
dist/win/resources/licenseTemplate.ftl
vendored
2
dist/win/resources/licenseTemplate.ftl
vendored
@@ -10,7 +10,7 @@
|
||||
\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
|
||||
\par
|
||||
\b\'a9 2016 \'96 2023 Skymatic GmbH \b0\par
|
||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\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
|
||||
\par
|
||||
|
||||
42
pom.xml
42
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptomator</artifactId>
|
||||
<version>1.11.1</version>
|
||||
<version>1.12.0</version>
|
||||
<name>Cryptomator Desktop App</name>
|
||||
|
||||
<organization>
|
||||
@@ -35,42 +35,42 @@
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.6.8</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.2.4</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.4.0-beta2</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>4.0.0-beta5</cryptomator.fuse.version>
|
||||
<cryptomator.integrations.win.version>1.2.5</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.2.3</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.4.2</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>4.0.0</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>2.0.5</cryptomator.webdav.version>
|
||||
<cryptomator.webdav.version>2.0.6</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||
<dagger.version>2.48.1</dagger.version>
|
||||
<dagger.version>2.50</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
<jackson.version>2.16.0</jackson.version>
|
||||
<javafx.version>20.0.2</javafx.version>
|
||||
<guava.version>33.0.0-jre</guava.version>
|
||||
<jackson.version>2.16.1</jackson.version>
|
||||
<javafx.version>21.0.1</javafx.version>
|
||||
<jwt.version>4.4.0</jwt.version>
|
||||
<nimbus-jose.version>9.37.1</nimbus-jose.version>
|
||||
<logback.version>1.4.12</logback.version>
|
||||
<slf4j.version>2.0.9</slf4j.version>
|
||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
||||
<logback.version>1.4.14</logback.version>
|
||||
<slf4j.version>2.0.11</slf4j.version>
|
||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
||||
<zxcvbn.version>1.8.2</zxcvbn.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.10.1</junit.jupiter.version>
|
||||
<mockito.version>5.7.0</mockito.version>
|
||||
<junit.jupiter.version>5.10.2</junit.jupiter.version>
|
||||
<mockito.version>5.10.0</mockito.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>9.0.1</dependency-check.version>
|
||||
<dependency-check.version>9.0.9</dependency-check.version>
|
||||
<jacoco.version>0.8.11</jacoco.version>
|
||||
<license-generator.version>2.3.0</license-generator.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.11.0</mvn-compiler.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.2</mvn-surefire.version>
|
||||
<mvn-surefire.version>3.2.5</mvn-surefire.version>
|
||||
<mvn-jar.version>3.3.0</mvn-jar.version>
|
||||
</properties>
|
||||
|
||||
@@ -460,17 +460,19 @@
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
<configuration>
|
||||
<cveValidForHours>24</cveValidForHours>
|
||||
<nvdValidForHours>24</nvdValidForHours>
|
||||
<failBuildOnCVSS>0</failBuildOnCVSS>
|
||||
<skipTestScope>true</skipTestScope>
|
||||
<detail>true</detail>
|
||||
<suppressionFile>suppression.xml</suppressionFile>
|
||||
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.common;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.keychain.KeychainModule;
|
||||
import org.cryptomator.common.mount.MountModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
@@ -22,8 +20,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
@@ -136,13 +132,4 @@ public abstract class CommonsModule {
|
||||
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());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
public record ActualMountService(MountService service, boolean isDesired) {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -4,21 +4,18 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.integrations.mount.Mount;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Module
|
||||
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
|
||||
@Singleton
|
||||
static List<MountService> provideSupportedMountServices() {
|
||||
@@ -27,46 +24,18 @@ public class MountModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("FUPFMS")
|
||||
static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
|
||||
return new AtomicReference<>(null);
|
||||
static ObservableValue<MountService> provideDefaultMountService(List<MountService> mountProviders, Settings settings) {
|
||||
var fallbackProvider = mountProviders.stream().findFirst().get(); //there should always be a mount provider, at least webDAV
|
||||
return ObservableUtil.mapWithDefault(settings.mountService, //
|
||||
serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), //
|
||||
fallbackProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ObservableValue<ActualMountService> provideMountService(Settings settings, List<MountService> serviceImpls, @Named("FUPFMS") AtomicReference<MountService> fupfms) {
|
||||
var fallbackProvider = serviceImpls.stream().findFirst().orElse(null);
|
||||
|
||||
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;
|
||||
@Named("usedMountServices")
|
||||
static Set<MountService> provideSetOfUsedMountServices() {
|
||||
return ConcurrentHashMap.newKeySet();
|
||||
}
|
||||
|
||||
//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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,15 @@ import org.cryptomator.integrations.mount.MountFailedException;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
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_TO_EXISTING_DIR;
|
||||
@@ -24,24 +28,39 @@ import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
|
||||
@Singleton
|
||||
public class Mounter {
|
||||
|
||||
private final Settings settings;
|
||||
// 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 WindowsDriveLetters driveLetters;
|
||||
private final ObservableValue<ActualMountService> mountServiceObservable;
|
||||
private final List<MountService> mountProviders;
|
||||
private final Set<MountService> usedMountServices;
|
||||
private final ObservableValue<MountService> defaultMountService;
|
||||
|
||||
@Inject
|
||||
public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue<ActualMountService> mountServiceObservable) {
|
||||
this.settings = settings;
|
||||
public Mounter(Environment env, //
|
||||
Settings settings, //
|
||||
WindowsDriveLetters driveLetters, //
|
||||
List<MountService> mountProviders, //
|
||||
@Named("usedMountServices") Set<MountService> usedMountServices, //
|
||||
ObservableValue<MountService> defaultMountService) {
|
||||
this.env = env;
|
||||
this.settings = settings;
|
||||
this.driveLetters = driveLetters;
|
||||
this.mountServiceObservable = mountServiceObservable;
|
||||
this.mountProviders = mountProviders;
|
||||
this.usedMountServices = usedMountServices;
|
||||
this.defaultMountService = defaultMountService;
|
||||
}
|
||||
|
||||
private class SettledMounter {
|
||||
|
||||
private MountService service;
|
||||
private MountBuilder builder;
|
||||
private VaultSettings vaultSettings;
|
||||
private final MountService service;
|
||||
private final MountBuilder builder;
|
||||
private final VaultSettings vaultSettings;
|
||||
|
||||
public SettledMounter(MountService service, MountBuilder builder, VaultSettings vaultSettings) {
|
||||
this.service = service;
|
||||
@@ -53,8 +72,13 @@ public class Mounter {
|
||||
for (var capability : service.capabilities()) {
|
||||
switch (capability) {
|
||||
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
||||
case LOOPBACK_PORT ->
|
||||
builder.setLoopbackPort(settings.port.get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
|
||||
case LOOPBACK_PORT -> {
|
||||
if (vaultSettings.mountService.getValue() == null) {
|
||||
builder.setLoopbackPort(settings.port.get());
|
||||
} else {
|
||||
builder.setLoopbackPort(vaultSettings.port.get());
|
||||
}
|
||||
}
|
||||
case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
|
||||
case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode.get());
|
||||
case MOUNT_FLAGS -> {
|
||||
@@ -131,13 +155,26 @@ public class Mounter {
|
||||
}
|
||||
|
||||
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
||||
var mountService = this.mountServiceObservable.getValue().service();
|
||||
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
|
||||
|
||||
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 internal = new SettledMounter(mountService, builder, vaultSettings);
|
||||
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
||||
var cleanup = internal.prepare();
|
||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package org.cryptomator.common.settings;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
@@ -40,6 +39,7 @@ public class VaultSettings {
|
||||
static final WhenUnlocked DEFAULT_ACTION_AFTER_UNLOCK = WhenUnlocked.ASK;
|
||||
static final boolean DEFAULT_AUTOLOCK_WHEN_IDLE = false;
|
||||
static final int DEFAULT_AUTOLOCK_IDLE_SECONDS = 30 * 60;
|
||||
static final int DEFAULT_PORT = 42427;
|
||||
|
||||
private static final Random RNG = new Random();
|
||||
|
||||
@@ -56,6 +56,8 @@ public class VaultSettings {
|
||||
public final IntegerProperty autoLockIdleSeconds;
|
||||
public final ObjectProperty<Path> mountPoint;
|
||||
public final StringExpression mountName;
|
||||
public final StringProperty mountService;
|
||||
public final IntegerProperty port;
|
||||
|
||||
VaultSettings(VaultSettingsJson json) {
|
||||
this.id = json.id;
|
||||
@@ -70,6 +72,8 @@ public class VaultSettings {
|
||||
this.autoLockWhenIdle = new SimpleBooleanProperty(this, "autoLockWhenIdle", json.autoLockWhenIdle);
|
||||
this.autoLockIdleSeconds = new SimpleIntegerProperty(this, "autoLockIdleSeconds", json.autoLockIdleSeconds);
|
||||
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||
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
|
||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||
final String name;
|
||||
@@ -95,7 +99,7 @@ public class VaultSettings {
|
||||
}
|
||||
|
||||
Observable[] observables() {
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
|
||||
}
|
||||
|
||||
public static VaultSettings withRandomId() {
|
||||
@@ -124,6 +128,8 @@ public class VaultSettings {
|
||||
json.autoLockWhenIdle = autoLockWhenIdle.get();
|
||||
json.autoLockIdleSeconds = autoLockIdleSeconds.get();
|
||||
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||
json.mountService = mountService.get();
|
||||
json.port = port.get();
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ class VaultSettingsJson {
|
||||
@JsonProperty("autoLockIdleSeconds")
|
||||
int autoLockIdleSeconds = VaultSettings.DEFAULT_AUTOLOCK_IDLE_SECONDS;
|
||||
|
||||
@JsonProperty("mountService")
|
||||
String mountService;
|
||||
|
||||
@JsonProperty("port")
|
||||
int port = VaultSettings.DEFAULT_PORT;
|
||||
|
||||
@Deprecated(since = "1.7.0")
|
||||
@JsonProperty(value = "winDriveLetter", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||
String winDriveLetter;
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.cryptomator.common.vaults;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.mount.Mounter;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||
@@ -73,7 +72,13 @@ public class Vault {
|
||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
|
||||
Vault(VaultSettings vaultSettings, //
|
||||
VaultConfigCache configCache, //
|
||||
AtomicReference<CryptoFileSystem> cryptoFileSystem, //
|
||||
VaultState state, //
|
||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||
VaultStats stats, //
|
||||
Mounter mounter) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.configCache = configCache;
|
||||
this.cryptoFileSystem = cryptoFileSystem;
|
||||
|
||||
@@ -21,9 +21,11 @@ public enum FxmlFile {
|
||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.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_SETUP_DEVICE("/fxml/hub_setup_device.fxml"), //
|
||||
HUB_REGISTER_DEVICE("/fxml/hub_register_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"), //
|
||||
@@ -43,8 +45,10 @@ public enum FxmlFile {
|
||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||
SHARE_VAULT("/fxml/share_vault.fxml"), //
|
||||
UPDATE_REMINDER("/fxml/update_reminder.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_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
|
||||
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
|
||||
|
||||
@@ -47,6 +47,7 @@ public enum FontAwesome5Icon {
|
||||
QUESTION_CIRCLE("\uf059"), //
|
||||
REDO("\uF01E"), //
|
||||
SEARCH("\uF002"), //
|
||||
SHARE("\uF064"), //
|
||||
SPINNER("\uF110"), //
|
||||
STETHOSCOPE("\uF0f1"), //
|
||||
SYNC("\uF021"), //
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.sharevault.ShareVaultComponent;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
||||
@@ -22,7 +23,17 @@ import javafx.scene.image.Image;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@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})
|
||||
@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, //
|
||||
ShareVaultComponent.class})
|
||||
abstract class FxApplicationModule {
|
||||
|
||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.sharevault.ShareVaultComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockWorkflow;
|
||||
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
||||
@@ -51,6 +52,7 @@ public class FxApplicationWindows {
|
||||
private final ErrorComponent.Factory errorWindowFactory;
|
||||
private final ExecutorService executor;
|
||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||
private final FilteredList<Window> visibleWindows;
|
||||
|
||||
@Inject
|
||||
@@ -64,6 +66,7 @@ public class FxApplicationWindows {
|
||||
LockComponent.Factory lockWorkflowFactory, //
|
||||
ErrorComponent.Factory errorWindowFactory, //
|
||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||
ShareVaultComponent.Factory shareVaultWindow, //
|
||||
ExecutorService executor) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.trayIntegration = trayIntegration;
|
||||
@@ -76,6 +79,7 @@ public class FxApplicationWindows {
|
||||
this.errorWindowFactory = errorWindowFactory;
|
||||
this.executor = executor;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
this.shareVaultWindow = shareVaultWindow;
|
||||
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
||||
}
|
||||
|
||||
@@ -122,6 +126,10 @@ public class FxApplicationWindows {
|
||||
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) {
|
||||
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.URI;
|
||||
@@ -19,6 +19,19 @@ public class HubConfig {
|
||||
@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 "/":
|
||||
@@ -33,4 +46,17 @@ public class HubConfig {
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,12 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_LEGACY_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
|
||||
@FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS)
|
||||
@@ -135,10 +141,17 @@ public abstract class HubKeyLoadingModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_SETUP_DEVICE)
|
||||
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubRegisterDeviceScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_SETUP_DEVICE);
|
||||
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
|
||||
@@ -185,6 +198,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(LegacyRegisterDeviceController.class)
|
||||
abstract FxController bindLegacyRegisterDeviceController(LegacyRegisterDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(LegacyRegisterSuccessController.class)
|
||||
abstract FxController bindLegacyRegisterSuccessController(LegacyRegisterSuccessController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(RegisterSuccessController.class)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
|
||||
@@ -44,6 +43,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
||||
this.window = window;
|
||||
this.keychainManager = keychainManager;
|
||||
window.setTitle(windowTitle);
|
||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
||||
this.authFlowScene = authFlowScene;
|
||||
this.noKeychainScene = noKeychainScene;
|
||||
this.result = result;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.nimbusds.jose.EncryptionMethod;
|
||||
import com.nimbusds.jose.JOSEException;
|
||||
import com.nimbusds.jose.JWEAlgorithm;
|
||||
@@ -13,19 +12,20 @@ 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 com.nimbusds.jose.jwk.gen.JWKGenerator;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
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.Base64;
|
||||
import java.util.Map;
|
||||
@@ -37,26 +37,16 @@ class JWEHelper {
|
||||
private static final String JWE_PAYLOAD_KEY_FIELD = "key";
|
||||
private static final String EC_ALG = "EC";
|
||||
|
||||
private JWEHelper(){}
|
||||
private JWEHelper() {}
|
||||
|
||||
public static JWEObject encryptUserKey(ECPrivateKey userKey, ECPublicKey deviceKey) {
|
||||
try {
|
||||
var encodedUserKey = Base64.getEncoder().encodeToString(userKey.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, encodedUserKey));
|
||||
var jwe = new JWEObject(header, payload);
|
||||
jwe.encrypt(new ECDHEncrypter(deviceKey));
|
||||
return jwe;
|
||||
} catch (JOSEException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return encryptKey(userKey, deviceKey);
|
||||
}
|
||||
|
||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, String setupCode) throws InvalidJweKeyException {
|
||||
try {
|
||||
jwe.decrypt(new PasswordBasedDecrypter(setupCode));
|
||||
return decodeUserKey(jwe);
|
||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
|
||||
} catch (JOSEException e) {
|
||||
throw new InvalidJweKeyException(e);
|
||||
}
|
||||
@@ -65,17 +55,23 @@ class JWEHelper {
|
||||
public static ECPrivateKey decryptUserKey(JWEObject jwe, ECPrivateKey deviceKey) throws InvalidJweKeyException {
|
||||
try {
|
||||
jwe.decrypt(new ECDHDecrypter(deviceKey));
|
||||
return decodeUserKey(jwe);
|
||||
return readKey(jwe, JWE_PAYLOAD_KEY_FIELD, JWEHelper::decodeECPrivateKey);
|
||||
} catch (JOSEException e) {
|
||||
throw new InvalidJweKeyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ECPrivateKey decodeUserKey(JWEObject decryptedJwe) {
|
||||
/**
|
||||
* 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 {
|
||||
var keySpec = readKey(decryptedJwe, JWE_PAYLOAD_KEY_FIELD, PKCS8EncodedKeySpec::new);
|
||||
var factory = KeyFactory.getInstance(EC_ALG);
|
||||
var privateKey = factory.generatePrivate(keySpec);
|
||||
KeyFactory factory = KeyFactory.getInstance(EC_ALG);
|
||||
var privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
|
||||
if (privateKey instanceof ECPrivateKey ecPrivateKey) {
|
||||
return ecPrivateKey;
|
||||
} else {
|
||||
@@ -84,8 +80,49 @@ class JWEHelper {
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(EC_ALG + " not supported");
|
||||
} catch (InvalidKeySpecException e) {
|
||||
LOG.warn("Unexpected JWE payload: {}", decryptedJwe.getPayload());
|
||||
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,12 +145,12 @@ class JWEHelper {
|
||||
var keyBytes = new byte[0];
|
||||
try {
|
||||
if (fields.get(keyField) instanceof String key) {
|
||||
keyBytes = BaseEncoding.base64().decode(key);
|
||||
keyBytes = Base64.getDecoder().decode(key);
|
||||
return rawKeyFactory.apply(keyBytes);
|
||||
} else {
|
||||
throw new IllegalArgumentException("JWE payload doesn't contain field " + keyField);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalArgumentException | KeyDecodeFailedException e) {
|
||||
LOG.error("Unexpected JWE payload: {}", jwe.getPayload());
|
||||
throw new MasterkeyLoadingFailedException("Unexpected JWE payload", e);
|
||||
} finally {
|
||||
@@ -127,4 +164,11 @@ class JWEHelper {
|
||||
super("Invalid key", cause);
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeyDecodeFailedException extends CryptoException {
|
||||
|
||||
public KeyDecodeFailedException(Throwable cause) {
|
||||
super("Malformed key", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class LegacyRegisterDeviceController implements FxController {
|
||||
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_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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 dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -11,7 +12,6 @@ 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.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -32,7 +32,6 @@ import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -48,29 +47,29 @@ public class ReceiveKeyController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final HubConfig hubConfig;
|
||||
private final String vaultId;
|
||||
private final String deviceId;
|
||||
private final String bearerToken;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final Lazy<Scene> setupDeviceScene;
|
||||
private final Lazy<Scene> registerDeviceScene;
|
||||
private final Lazy<Scene> legacyRegisterDeviceScene;
|
||||
private final Lazy<Scene> unauthorizedScene;
|
||||
private final Lazy<Scene> accountInitializationScene;
|
||||
private final URI vaultBaseUri;
|
||||
private final Lazy<Scene> invalidLicenseScene;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@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_SETUP_DEVICE) Lazy<Scene> setupDeviceScene, @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, 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) {
|
||||
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.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||
this.result = result;
|
||||
this.setupDeviceScene = setupDeviceScene;
|
||||
this.registerDeviceScene = registerDeviceScene;
|
||||
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
|
||||
this.unauthorizedScene = unauthorizedScene;
|
||||
this.accountInitializationScene = accountInitializationScene;
|
||||
this.vaultBaseUri = getVaultBaseUri(vault);
|
||||
this.invalidLicenseScene = invalidLicenseScene;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||
@@ -78,70 +77,76 @@ public class ReceiveKeyController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
requestVaultMasterkey();
|
||||
receiveKey();
|
||||
}
|
||||
|
||||
public void receiveKey() {
|
||||
requestApiConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1 (Request): GET vault key for this user
|
||||
* STEP 0 (Request): GET /api/config
|
||||
*/
|
||||
private void requestVaultMasterkey() {
|
||||
var accessTokenUri = appendPath(vaultBaseUri, "/access-token");
|
||||
var request = HttpRequest.newBuilder(accessTokenUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
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::receivedVaultMasterkey, Platform::runLater) //
|
||||
.thenAcceptAsync(this::receivedApiConfig, Platform::runLater) //
|
||||
.exceptionally(this::retrievalFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1 (Response): GET vault key for this user
|
||||
* STEP 0 (Response): GET /api/config
|
||||
*
|
||||
* @param response Response
|
||||
*/
|
||||
private void receivedVaultMasterkey(HttpResponse<String> response) {
|
||||
private void receivedApiConfig(HttpResponse<String> response) {
|
||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
||||
switch (response.statusCode()) {
|
||||
case 200 -> requestUserKey(response.body());
|
||||
case 402 -> licenseExceeded();
|
||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
|
||||
case 449 -> accountInitializationRequired();
|
||||
case 404 -> requestLegacyAccessToken();
|
||||
default -> throw new IllegalStateException("Unexpected response " + 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 2 (Request): GET user key for this device
|
||||
* STEP 1 (Request): GET user key for this device
|
||||
*/
|
||||
private void requestUserKey(String encryptedVaultKey) {
|
||||
var deviceTokenUri = URI.create(hubConfig.getApiBaseUrl() + "/devices/" + deviceId);
|
||||
var request = HttpRequest.newBuilder(deviceTokenUri) //
|
||||
private void requestDeviceData() {
|
||||
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
|
||||
var request = HttpRequest.newBuilder(deviceUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.build();
|
||||
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
|
||||
.thenAcceptAsync(response -> receivedUserKey(encryptedVaultKey, response), Platform::runLater) //
|
||||
.thenAcceptAsync(this::receivedDeviceData) //
|
||||
.exceptionally(this::retrievalFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2 (Response): GET user key for this device
|
||||
* STEP 1 (Response): GET user key for this device
|
||||
*
|
||||
* @param response Response
|
||||
*/
|
||||
private void receivedUserKey(String encryptedVaultKey, HttpResponse<String> response) {
|
||||
private void receivedDeviceData(HttpResponse<String> response) {
|
||||
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
||||
try {
|
||||
switch (response.statusCode()) {
|
||||
case 200 -> {
|
||||
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
|
||||
receivedBothEncryptedKeys(encryptedVaultKey, device.userPrivateKey);
|
||||
requestVaultMasterkey(device.userPrivateKey);
|
||||
}
|
||||
case 404 -> needsDeviceSetup(); // TODO: using the setup code, we can theoretically immediately unlock
|
||||
case 404 -> Platform.runLater(this::needsDeviceRegistration);
|
||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@@ -149,18 +154,49 @@ public class ReceiveKeyController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void needsDeviceSetup() {
|
||||
window.setScene(setupDeviceScene.get());
|
||||
private void needsDeviceRegistration() {
|
||||
window.setScene(registerDeviceScene.get());
|
||||
}
|
||||
|
||||
private void receivedBothEncryptedKeys(String encryptedVaultKey, String encryptedUserKey) throws IOException {
|
||||
/**
|
||||
* 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) {
|
||||
throw new IOException("Failed to parse JWE", e);
|
||||
retrievalFailed(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +205,7 @@ public class ReceiveKeyController implements FxController {
|
||||
*/
|
||||
@Deprecated
|
||||
private void requestLegacyAccessToken() {
|
||||
var legacyAccessTokenUri = appendPath(vaultBaseUri, "/keys/" + deviceId);
|
||||
var legacyAccessTokenUri = hubConfig.URIs.API."vaults/\{vaultId}/keys/\{deviceId}";
|
||||
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.GET() //
|
||||
@@ -251,19 +287,15 @@ public class ReceiveKeyController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private static URI getVaultBaseUri(Vault vault) {
|
||||
try {
|
||||
var url = vault.getVaultConfigCache().get().getKeyId();
|
||||
assert url.getScheme().startsWith(SCHEME_PREFIX);
|
||||
var correctedScheme = url.getScheme().substring(SCHEME_PREFIX.length());
|
||||
return new URI(correctedScheme, url.getSchemeSpecificPart(), url.getFragment());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("URI constructed from params known to be valid", e);
|
||||
}
|
||||
private static String extractVaultId(URI vaultKeyUri) {
|
||||
assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX);
|
||||
var path = vaultKeyUri.getPath();
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,25 @@ import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@KeyLoadingScoped
|
||||
public class RegisterDeviceController implements FxController {
|
||||
@@ -57,12 +63,12 @@ public class RegisterDeviceController implements FxController {
|
||||
private final String bearerToken;
|
||||
private final Lazy<Scene> registerSuccessScene;
|
||||
private final Lazy<Scene> registerFailedScene;
|
||||
private final Lazy<Scene> deviceAlreadyExistsScene;
|
||||
private final String deviceId;
|
||||
private final P384KeyPair deviceKeyPair;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private final BooleanProperty deviceNameAlreadyExists = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty invalidSetupCode = new SimpleBooleanProperty(false);
|
||||
private final BooleanProperty workInProgress = new SimpleBooleanProperty(false);
|
||||
public TextField setupCodeField;
|
||||
@@ -70,7 +76,7 @@ public class RegisterDeviceController implements FxController {
|
||||
public Button registerBtn;
|
||||
|
||||
@Inject
|
||||
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene) {
|
||||
public RegisterDeviceController(@KeyLoading Stage window, ExecutorService executor, HubConfig hubConfig, @Named("deviceId") String deviceId, DeviceKey deviceKey, CompletableFuture<ReceivedKey> result, @Named("bearerToken") AtomicReference<String> bearerToken, @FxmlScene(FxmlFile.HUB_REGISTER_SUCCESS) Lazy<Scene> registerSuccessScene, @FxmlScene(FxmlFile.HUB_REGISTER_FAILED) Lazy<Scene> registerFailedScene, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE_ALREADY_EXISTS) Lazy<Scene> deviceAlreadyExistsScene) {
|
||||
this.window = window;
|
||||
this.hubConfig = hubConfig;
|
||||
this.deviceId = deviceId;
|
||||
@@ -79,13 +85,13 @@ public class RegisterDeviceController implements FxController {
|
||||
this.bearerToken = Objects.requireNonNull(bearerToken.get());
|
||||
this.registerSuccessScene = registerSuccessScene;
|
||||
this.registerFailedScene = registerFailedScene;
|
||||
this.deviceAlreadyExistsScene = deviceAlreadyExistsScene;
|
||||
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));
|
||||
deviceNameField.disableProperty().bind(workInProgress);
|
||||
setupCodeField.textProperty().addListener(observable -> invalidSetupCode.set(false));
|
||||
setupCodeField.disableProperty().bind(workInProgress);
|
||||
@@ -108,9 +114,8 @@ public class RegisterDeviceController implements FxController {
|
||||
public void register() {
|
||||
workInProgress.set(true);
|
||||
|
||||
var apiRootUrl = hubConfig.getApiBaseUrl();
|
||||
|
||||
var userReq = HttpRequest.newBuilder(apiRootUrl.resolve("users/me")) //
|
||||
var userReq = HttpRequest.newBuilder(hubConfig.URIs.API."users/me") //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
@@ -126,17 +131,19 @@ public class RegisterDeviceController implements FxController {
|
||||
}
|
||||
}).thenApply(user -> {
|
||||
try {
|
||||
assert user.privateKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
|
||||
assert user.privateKey != null && user.publicKey != null; // api/vaults/{v}/user-tokens/me would have returned 403, if user wasn't fully set up yet
|
||||
var userPublicKey = JWEHelper.decodeECPublicKey(Base64.getDecoder().decode(user.publicKey));
|
||||
migrateLegacyDevices(userPublicKey); // TODO: remove eventually, when most users have migrated to Hub 1.3.x or newer
|
||||
var userKey = JWEHelper.decryptUserKey(JWEObject.parse(user.privateKey), setupCodeField.getText());
|
||||
return JWEHelper.encryptUserKey(userKey, deviceKeyPair.getPublic());
|
||||
} catch (ParseException e) {
|
||||
} catch (ParseException | JWEHelper.KeyDecodeFailedException e) {
|
||||
throw new RuntimeException("Server answered with unparsable user key", e);
|
||||
}
|
||||
}).thenCompose(jwe -> {
|
||||
var now = Instant.now().toString();
|
||||
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
|
||||
var json = toJson(dto);
|
||||
var deviceUri = apiRootUrl.resolve("devices/" + deviceId);
|
||||
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
|
||||
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
|
||||
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
@@ -146,7 +153,7 @@ public class RegisterDeviceController implements FxController {
|
||||
return httpClient.sendAsync(putDeviceReq, HttpResponse.BodyHandlers.discarding());
|
||||
}).whenCompleteAsync((response, throwable) -> {
|
||||
if (response != null) {
|
||||
this.handleResponse(response);
|
||||
this.handleRegisterDeviceResponse(response);
|
||||
} else {
|
||||
this.setupFailed(throwable);
|
||||
}
|
||||
@@ -154,6 +161,46 @@ public class RegisterDeviceController implements FxController {
|
||||
}, Platform::runLater);
|
||||
}
|
||||
|
||||
private void migrateLegacyDevices(ECPublicKey userPublicKey) {
|
||||
try {
|
||||
// GET legacy access tokens
|
||||
var getUri = hubConfig.URIs.API."devices/\{deviceId}/legacy-access-tokens";
|
||||
var getReq = HttpRequest.newBuilder(getUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
||||
var getRes = httpClient.send(getReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (getRes.statusCode() != 200) {
|
||||
LOG.debug("GET {} resulted in status code {}. Skipping migration.", getUri, getRes.statusCode());
|
||||
return;
|
||||
}
|
||||
Map<String, String> legacyAccessTokens = JSON.readerForMapOf(String.class).readValue(getRes.body());
|
||||
if (legacyAccessTokens.isEmpty()) {
|
||||
return; // no migration required
|
||||
}
|
||||
|
||||
// POST new access tokens
|
||||
Map<String, String> newAccessTokens = legacyAccessTokens.entrySet().stream().<Map.Entry<String, String>>mapMulti((entry, consumer) -> {
|
||||
try (var vaultKey = JWEHelper.decryptVaultKey(JWEObject.parse(entry.getValue()), deviceKeyPair.getPrivate())) {
|
||||
var newAccessToken = JWEHelper.encryptVaultKey(vaultKey, userPublicKey).serialize();
|
||||
consumer.accept(Map.entry(entry.getKey(), newAccessToken));
|
||||
} catch (ParseException | JWEHelper.InvalidJweKeyException e) {
|
||||
LOG.warn("Failed to decrypt legacy access token for vault {}. Skipping migration.", entry.getKey());
|
||||
}
|
||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
var postUri = hubConfig.URIs.API."users/me/access-tokens";
|
||||
var postBody = JSON.writer().writeValueAsString(newAccessTokens);
|
||||
var postReq = HttpRequest.newBuilder(postUri).POST(HttpRequest.BodyPublishers.ofString(postBody)).timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
||||
var postRes = httpClient.send(postReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (postRes.statusCode() != 200) {
|
||||
throw new IOException(STR."Unexpected response from POST \{postUri}: \{postRes.statusCode()}");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// log and ignore: this is merely a best-effort attempt of migrating legacy devices. Failure is uncritical as this is merely a convenience feature.
|
||||
LOG.error("Legacy Device Migration failed.", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new UncheckedIOException(new InterruptedIOException("Legacy Device Migration interrupted"));
|
||||
}
|
||||
}
|
||||
|
||||
private UserDto fromJson(String json) {
|
||||
try {
|
||||
return JSON.reader().readValue(json, UserDto.class);
|
||||
@@ -170,12 +217,12 @@ public class RegisterDeviceController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResponse(HttpResponse<Void> response) {
|
||||
private void handleRegisterDeviceResponse(HttpResponse<Void> response) {
|
||||
if (response.statusCode() == 201) {
|
||||
LOG.debug("Device registration for hub instance {} successful.", hubConfig.authSuccessUrl);
|
||||
window.setScene(registerSuccessScene.get());
|
||||
} else if (response.statusCode() == 409) {
|
||||
deviceNameAlreadyExists.set(true);
|
||||
setupFailed(new DeviceAlreadyExistsException());
|
||||
} else {
|
||||
setupFailed(new IllegalStateException("Unexpected http status code " + response.statusCode()));
|
||||
}
|
||||
@@ -184,10 +231,13 @@ public class RegisterDeviceController implements FxController {
|
||||
private void setupFailed(Throwable cause) {
|
||||
switch (cause) {
|
||||
case CompletionException e when e.getCause() instanceof JWEHelper.InvalidJweKeyException -> invalidSetupCode.set(true);
|
||||
case DeviceAlreadyExistsException e -> {
|
||||
LOG.debug("Device already registered in hub instance {} for different user", hubConfig.authSuccessUrl);
|
||||
window.setScene(deviceAlreadyExistsScene.get());
|
||||
}
|
||||
default -> {
|
||||
LOG.warn("Device setup failed.", cause);
|
||||
window.setScene(registerFailedScene.get());
|
||||
result.completeExceptionally(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,15 +252,6 @@ public class RegisterDeviceController implements FxController {
|
||||
}
|
||||
|
||||
//--- Getters & Setters
|
||||
|
||||
public BooleanProperty deviceNameAlreadyExistsProperty() {
|
||||
return deviceNameAlreadyExists;
|
||||
}
|
||||
|
||||
public boolean getDeviceNameAlreadyExists() {
|
||||
return deviceNameAlreadyExists.get();
|
||||
}
|
||||
|
||||
public BooleanProperty invalidSetupCodeProperty() {
|
||||
return invalidSetupCode;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
|
||||
@@ -22,8 +21,8 @@ public class RegisterFailedController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
result.cancel(true);
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,43 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import dagger.Lazy;
|
||||
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 javax.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RegisterSuccessController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final Lazy<Scene> receiveKeyScene;
|
||||
private final ReceiveKeyController receiveKeyController;
|
||||
|
||||
@Inject
|
||||
public RegisterSuccessController(@KeyLoading Stage window) {
|
||||
public RegisterSuccessController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) Lazy<Scene> receiveKeyScene, ReceiveKeyController receiveKeyController) {
|
||||
this.window = window;
|
||||
this.result = result;
|
||||
this.receiveKeyScene = receiveKeyScene;
|
||||
this.receiveKeyController = receiveKeyController;
|
||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
public void complete() {
|
||||
window.setScene(receiveKeyScene.get());
|
||||
receiveKeyController.receiveKey();
|
||||
}
|
||||
|
||||
private void windowClosed(WindowEvent windowEvent) {
|
||||
result.cancel(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ public class VaultDetailLockedController implements FxController {
|
||||
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void share() {
|
||||
appWindows.showShareVaultWindow(vault.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showVaultOptions() {
|
||||
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
|
||||
|
||||
@@ -2,17 +2,14 @@ package org.cryptomator.ui.preferences;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.mount.MountModule;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.integrations.mount.MountCapability;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
@@ -21,24 +18,22 @@ import javafx.util.StringConverter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@PreferencesScoped
|
||||
public class VolumePreferencesController implements FxController {
|
||||
|
||||
private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
|
||||
private static final int MIN_PORT = 1024;
|
||||
private static final int MAX_PORT = 65535;
|
||||
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
|
||||
public static final int MIN_PORT = 1024;
|
||||
public static final int MAX_PORT = 65535;
|
||||
|
||||
private final Settings settings;
|
||||
private final ObservableValue<MountService> selectedMountService;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final BooleanExpression loopbackPortSupported;
|
||||
private final ObservableValue<Boolean> loopbackPortSupported;
|
||||
private final ObservableValue<Boolean> mountToDirSupported;
|
||||
private final ObservableValue<Boolean> mountToDriveLetterSupported;
|
||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||
private final ObservableValue<Boolean> readonlySupported;
|
||||
private final ObservableValue<Boolean> fuseRestartRequired;
|
||||
private final Lazy<Application> application;
|
||||
private final List<MountService> mountProviders;
|
||||
public ChoiceBox<MountService> volumeTypeChoiceBox;
|
||||
@@ -46,7 +41,10 @@ public class VolumePreferencesController implements FxController {
|
||||
public Button loopbackPortApplyButton;
|
||||
|
||||
@Inject
|
||||
VolumePreferencesController(Settings settings, Lazy<Application> application, List<MountService> mountProviders, @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) {
|
||||
VolumePreferencesController(Settings settings, //
|
||||
Lazy<Application> application, //
|
||||
List<MountService> mountProviders, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.settings = settings;
|
||||
this.application = application;
|
||||
this.mountProviders = mountProviders;
|
||||
@@ -54,17 +52,11 @@ public class VolumePreferencesController implements FxController {
|
||||
|
||||
var fallbackProvider = mountProviders.stream().findFirst().orElse(null);
|
||||
this.selectedMountService = ObservableUtil.mapWithDefault(settings.mountService, serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), fallbackProvider);
|
||||
this.loopbackPortSupported = BooleanExpression.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT)));
|
||||
this.loopbackPortSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT));
|
||||
this.mountToDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT) || s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR));
|
||||
this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
||||
this.fuseRestartRequired = selectedMountService.map(s -> {//
|
||||
return firstUsedProblematicFuseMountService.get() != null //
|
||||
&& MountModule.isProblematicFuseService(s) //
|
||||
&& !firstUsedProblematicFuseMountService.get().equals(s);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -101,12 +93,12 @@ public class VolumePreferencesController implements FxController {
|
||||
|
||||
/* Property Getters */
|
||||
|
||||
public BooleanExpression loopbackPortSupportedProperty() {
|
||||
public ObservableValue<Boolean> loopbackPortSupportedProperty() {
|
||||
return loopbackPortSupported;
|
||||
}
|
||||
|
||||
public boolean isLoopbackPortSupported() {
|
||||
return loopbackPortSupported.get();
|
||||
return loopbackPortSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> readonlySupportedProperty() {
|
||||
@@ -141,14 +133,6 @@ public class VolumePreferencesController implements FxController {
|
||||
return mountFlagsSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> fuseRestartRequiredProperty() {
|
||||
return fuseRestartRequired;
|
||||
}
|
||||
|
||||
public boolean getFuseRestartRequired() {
|
||||
return fuseRestartRequired.getValue();
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
|
||||
private class MountServiceConverter extends StringConverter<MountService> {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.cryptomator.ui.sharevault;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Lazy;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@ShareVaultScoped
|
||||
@Subcomponent(modules = {ShareVaultModule.class})
|
||||
public interface ShareVaultComponent {
|
||||
|
||||
@ShareVaultWindow
|
||||
Stage window();
|
||||
|
||||
@FxmlScene(FxmlFile.SHARE_VAULT)
|
||||
Lazy<Scene> scene();
|
||||
|
||||
default void showShareVaultWindow(){
|
||||
Stage stage = window();
|
||||
stage.setScene(scene().get());
|
||||
stage.show();
|
||||
}
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
ShareVaultComponent create(@BindsInstance @ShareVaultWindow Vault vault);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.cryptomator.ui.sharevault;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
@ShareVaultScoped
|
||||
public class ShareVaultController implements FxController {
|
||||
|
||||
private static final String SCHEME_PREFIX = "hub+";
|
||||
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
|
||||
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/en/latest/security/best-practices/#sharing-of-vaults";
|
||||
|
||||
private final Stage window;
|
||||
private final Lazy<Application> application;
|
||||
private final Vault vault;
|
||||
private final Boolean hubVault;
|
||||
|
||||
@Inject
|
||||
ShareVaultController(@ShareVaultWindow Stage window, //
|
||||
Lazy<Application> application, //
|
||||
@ShareVaultWindow Vault vault) {
|
||||
this.window = window;
|
||||
this.application = application;
|
||||
this.vault = vault;
|
||||
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
|
||||
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void visitHub() {
|
||||
application.get().getHostServices().showDocument(VISIT_HUB_URL);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void openHub() {
|
||||
application.get().getHostServices().showDocument(getHubUri(vault).toString());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void visitBestPractices() {
|
||||
application.get().getHostServices().showDocument(BEST_PRACTICES_URL);
|
||||
}
|
||||
|
||||
private static URI getHubUri(Vault vault) {
|
||||
try {
|
||||
var keyID = new URI(vault.getVaultConfigCache().get().getKeyId().toString());
|
||||
assert keyID.getScheme().startsWith(SCHEME_PREFIX);
|
||||
return new URI(keyID.getScheme().substring(SCHEME_PREFIX.length()) + "://" + keyID.getHost() + "/app/vaults");
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("URI constructed from params known to be valid", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHubVault() {
|
||||
return hubVault;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.cryptomator.ui.sharevault;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@Module
|
||||
abstract class ShareVaultModule {
|
||||
|
||||
@Provides
|
||||
@ShareVaultWindow
|
||||
@ShareVaultScoped
|
||||
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ShareVaultWindow
|
||||
@ShareVaultScoped
|
||||
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.APPLICATION_MODAL);
|
||||
stage.setTitle(resourceBundle.getString("shareVault.title"));
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.SHARE_VAULT)
|
||||
@ShareVaultScoped
|
||||
static Scene provideShareVaultScene(@ShareVaultWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.SHARE_VAULT);
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(ShareVaultController.class)
|
||||
abstract FxController bindShareVaultController(ShareVaultController controller);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.ui.sharevault;
|
||||
|
||||
import javax.inject.Scope;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ShareVaultScoped {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.cryptomator.ui.sharevault;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@interface ShareVaultWindow {
|
||||
|
||||
}
|
||||
@@ -19,16 +19,8 @@ import java.util.concurrent.Future;
|
||||
@Subcomponent(modules = {UnlockModule.class})
|
||||
public interface UnlockComponent {
|
||||
|
||||
ExecutorService defaultExecutorService();
|
||||
|
||||
UnlockWorkflow unlockWorkflow();
|
||||
|
||||
default Future<Boolean> startUnlockWorkflow() {
|
||||
UnlockWorkflow workflow = unlockWorkflow();
|
||||
defaultExecutorService().submit(workflow);
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
UnlockComponent create(@BindsInstance @UnlockWindow Vault vault, @BindsInstance @Named("unlockWindowOwner") @Nullable Stage owner);
|
||||
|
||||
@@ -81,6 +81,13 @@ abstract class UnlockModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART)
|
||||
@UnlockScoped
|
||||
static Scene provideRestartRequiredScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_REQUIRES_RESTART);
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
@Binds
|
||||
@@ -93,4 +100,9 @@ abstract class UnlockModule {
|
||||
@FxControllerKey(UnlockInvalidMountPointController.class)
|
||||
abstract FxController bindUnlockInvalidMountPointController(UnlockInvalidMountPointController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(UnlockRequiresRestartController.class)
|
||||
abstract FxController bindUnlockRequiresRestartController(UnlockRequiresRestartController controller);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.cryptomator.ui.unlock;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@UnlockScoped
|
||||
public class UnlockRequiresRestartController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Vault vault;
|
||||
|
||||
@Inject
|
||||
UnlockRequiresRestartController(@UnlockWindow Stage window, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationWindows appWindows, //
|
||||
@UnlockWindow Vault vault) {
|
||||
this.window = window;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.appWindows = appWindows;
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
window.setTitle(String.format(resourceBundle.getString("unlock.error.title"), vault.getDisplayName()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
window.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void closeAndOpenVaultOptions() {
|
||||
appWindows.showVaultOptionsWindow(vault, SelectedVaultOptionsTab.MOUNT);
|
||||
window.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.ui.unlock;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.mount.ConflictingMountServiceException;
|
||||
import org.cryptomator.common.mount.IllegalMountPointException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
@@ -29,7 +29,7 @@ import java.io.IOException;
|
||||
* This class runs the unlock process and controls when to display which UI.
|
||||
*/
|
||||
@UnlockScoped
|
||||
public class UnlockWorkflow extends Task<Boolean> {
|
||||
public class UnlockWorkflow extends Task<Void> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class);
|
||||
|
||||
@@ -38,42 +38,44 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
private final VaultService vaultService;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final Lazy<Scene> invalidMountPointScene;
|
||||
private final Lazy<Scene> restartRequiredScene;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final KeyLoadingStrategy keyLoadingStrategy;
|
||||
private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
|
||||
|
||||
@Inject
|
||||
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
|
||||
UnlockWorkflow(@UnlockWindow Stage window, //
|
||||
@UnlockWindow Vault vault, //
|
||||
VaultService vaultService, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, //
|
||||
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
|
||||
FxApplicationWindows appWindows, //
|
||||
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
|
||||
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.vaultService = vaultService;
|
||||
this.successScene = successScene;
|
||||
this.invalidMountPointScene = invalidMountPointScene;
|
||||
this.restartRequiredScene = restartRequiredScene;
|
||||
this.appWindows = appWindows;
|
||||
this.keyLoadingStrategy = keyLoadingStrategy;
|
||||
this.illegalMountPointException = illegalMountPointException;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean call() throws InterruptedException, IOException, CryptoException, MountFailedException {
|
||||
try {
|
||||
attemptUnlock();
|
||||
return true;
|
||||
} catch (UnlockCancelledException e) {
|
||||
cancel(false); // set Tasks state to cancelled
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptUnlock() throws IOException, CryptoException, MountFailedException {
|
||||
protected Void call() throws InterruptedException, IOException, CryptoException, MountFailedException {
|
||||
try {
|
||||
keyLoadingStrategy.use(vault::unlock);
|
||||
return null;
|
||||
} catch (UnlockCancelledException e) {
|
||||
cancel(false); // set Tasks state to cancelled
|
||||
return null;
|
||||
} catch (IOException | RuntimeException | MountFailedException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Throwables.propagateIfPossible(e, IOException.class);
|
||||
Throwables.propagateIfPossible(e, CryptoException.class);
|
||||
Throwables.propagateIfPossible(e, IllegalMountPointException.class);
|
||||
Throwables.propagateIfPossible(e, MountFailedException.class);
|
||||
throw new IllegalStateException("unexpected exception type", e);
|
||||
throw new IllegalStateException("Unexpected exception type", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +87,13 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
});
|
||||
}
|
||||
|
||||
private void handleConflictingMountServiceException() {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(restartRequiredScene.get());
|
||||
window.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleGenericError(Throwable e) {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
@@ -113,10 +122,10 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
protected void failed() {
|
||||
LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
|
||||
Throwable throwable = super.getException();
|
||||
if(throwable instanceof IllegalMountPointException impe) {
|
||||
handleIllegalMountPointError(impe);
|
||||
} else {
|
||||
handleGenericError(throwable);
|
||||
switch (throwable) {
|
||||
case IllegalMountPointException e -> handleIllegalMountPointError(e);
|
||||
case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
|
||||
default -> handleGenericError(throwable);
|
||||
}
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package org.cryptomator.ui.vaultoptions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.cryptomator.common.mount.ActualMountService;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.common.mount.Mounter;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.integrations.mount.MountCapability;
|
||||
import org.cryptomator.integrations.mount.MountService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.preferences.VolumePreferencesController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.RadioButton;
|
||||
@@ -26,6 +33,8 @@ import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -36,14 +45,21 @@ public class MountOptionsController implements FxController {
|
||||
private final VaultSettings vaultSettings;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Lazy<Application> application;
|
||||
|
||||
private final ObservableValue<String> defaultMountFlags;
|
||||
private final ObservableValue<Boolean> mountpointDirSupported;
|
||||
private final ObservableValue<Boolean> mountpointDriveLetterSupported;
|
||||
private final ObservableValue<Boolean> readOnlySupported;
|
||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||
private final ObservableValue<Boolean> defaultMountServiceSelected;
|
||||
private final ObservableValue<String> directoryPath;
|
||||
private final FxApplicationWindows applicationWindows;
|
||||
private final List<MountService> mountProviders;
|
||||
private final ObservableValue<MountService> defaultMountService;
|
||||
private final ObservableValue<MountService> selectedMountService;
|
||||
private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
|
||||
private final ObservableValue<Boolean> loopbackPortChangeable;
|
||||
|
||||
|
||||
//-- FXML objects --
|
||||
@@ -56,30 +72,58 @@ public class MountOptionsController implements FxController {
|
||||
public RadioButton mountPointDirBtn;
|
||||
public TextField directoryPathField;
|
||||
public ChoiceBox<Path> driveLetterSelection;
|
||||
public ChoiceBox<MountService> vaultVolumeTypeChoiceBox;
|
||||
public TextField vaultLoopbackPortField;
|
||||
public Button vaultLoopbackPortApplyButton;
|
||||
|
||||
|
||||
@Inject
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObservableValue<ActualMountService> mountService, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, FxApplicationWindows applicationWindows) {
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, //
|
||||
@VaultOptionsWindow Vault vault, //
|
||||
WindowsDriveLetters windowsDriveLetters, //
|
||||
ResourceBundle resourceBundle, //
|
||||
FxApplicationWindows applicationWindows, //
|
||||
Lazy<Application> application, //
|
||||
List<MountService> mountProviders, //
|
||||
Mounter mounter, //
|
||||
ObservableValue<MountService> defaultMountService) {
|
||||
this.window = window;
|
||||
this.vaultSettings = vault.getVaultSettings();
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.defaultMountFlags = mountService.map(as -> {
|
||||
if (as.service().hasCapability(MountCapability.MOUNT_FLAGS)) {
|
||||
return as.service().getDefaultMountFlags();
|
||||
this.applicationWindows = applicationWindows;
|
||||
this.directoryPath = vault.getVaultSettings().mountPoint.map(p -> isDriveLetter(p) ? null : p.toString());
|
||||
this.application = application;
|
||||
this.mountProviders = mountProviders;
|
||||
this.defaultMountService = defaultMountService;
|
||||
this.selectedMountService = Bindings.createObjectBinding(this::reselectMountService, defaultMountService, vaultSettings.mountService);
|
||||
this.selectedMountServiceRequiresRestart = selectedMountService.map(mounter::isConflictingMountService);
|
||||
|
||||
this.defaultMountFlags = selectedMountService.map(s -> {
|
||||
if (s.hasCapability(MountCapability.MOUNT_FLAGS)) {
|
||||
return s.getDefaultMountFlags();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
this.mountpointDirSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || as.service().hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountpointDriveLetterSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.mountFlagsSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.readOnlySupported = mountService.map(as -> as.service().hasCapability(MountCapability.READ_ONLY));
|
||||
this.directoryPath = vault.getVaultSettings().mountPoint.map(p -> isDriveLetter(p) ? null : p.toString());
|
||||
this.applicationWindows = applicationWindows;
|
||||
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true);
|
||||
this.readOnlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
||||
this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null);
|
||||
}
|
||||
|
||||
private MountService reselectMountService() {
|
||||
var desired = vaultSettings.mountService.getValue();
|
||||
var defaultMS = defaultMountService.getValue();
|
||||
return mountProviders.stream().filter(s -> s.getClass().getName().equals(desired)).findFirst().orElse(defaultMS);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
defaultMountService.addListener((_, _, _) -> vaultVolumeTypeChoiceBox.setConverter(new MountServiceConverter()));
|
||||
|
||||
// readonly:
|
||||
readOnlyCheckbox.selectedProperty().bindBidirectional(vaultSettings.usesReadOnlyMode);
|
||||
|
||||
@@ -106,6 +150,20 @@ public class MountOptionsController implements FxController {
|
||||
mountPointToggleGroup.selectToggle(mountPointDirBtn);
|
||||
}
|
||||
mountPointToggleGroup.selectedToggleProperty().addListener(this::selectedToggleChanged);
|
||||
|
||||
vaultVolumeTypeChoiceBox.getItems().add(null);
|
||||
vaultVolumeTypeChoiceBox.getItems().addAll(mountProviders);
|
||||
vaultVolumeTypeChoiceBox.setConverter(new MountServiceConverter());
|
||||
vaultVolumeTypeChoiceBox.getSelectionModel().select(isDefaultMountServiceSelected() ? null : selectedMountService.getValue());
|
||||
vaultVolumeTypeChoiceBox.valueProperty().addListener((_, _, newProvider) -> {
|
||||
var toSet = Optional.ofNullable(newProvider).map(nP -> nP.getClass().getName()).orElse(null);
|
||||
vaultSettings.mountService.set(toSet);
|
||||
});
|
||||
|
||||
vaultLoopbackPortField.setText(String.valueOf(vaultSettings.port.get()));
|
||||
vaultLoopbackPortApplyButton.visibleProperty().bind(vaultSettings.port.asString().isNotEqualTo(vaultLoopbackPortField.textProperty()));
|
||||
vaultLoopbackPortApplyButton.disableProperty().bind(Bindings.createBooleanBinding(this::validateLoopbackPort, vaultLoopbackPortField.textProperty()).not());
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -229,6 +287,26 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
}
|
||||
|
||||
public void openDocs() {
|
||||
application.get().getHostServices().showDocument(VolumePreferencesController.DOCS_MOUNTING_URL);
|
||||
}
|
||||
|
||||
private boolean validateLoopbackPort() {
|
||||
try {
|
||||
int port = Integer.parseInt(vaultLoopbackPortField.getText());
|
||||
return port == 0 // choose port automatically
|
||||
|| port >= VolumePreferencesController.MIN_PORT && port <= VolumePreferencesController.MAX_PORT; // port within range
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void doChangeLoopbackPort() {
|
||||
if (validateLoopbackPort()) {
|
||||
vaultSettings.port.set(Integer.parseInt(vaultLoopbackPortField.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
private static class NoDirSelectedException extends Exception {}
|
||||
//@formatter:on
|
||||
@@ -243,6 +321,14 @@ public class MountOptionsController implements FxController {
|
||||
return mountFlagsSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> defaultMountServiceSelectedProperty() {
|
||||
return defaultMountServiceSelected;
|
||||
}
|
||||
|
||||
public boolean isDefaultMountServiceSelected() {
|
||||
return defaultMountServiceSelected.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> mountpointDirSupportedProperty() {
|
||||
return mountpointDirSupported;
|
||||
}
|
||||
@@ -274,4 +360,37 @@ public class MountOptionsController implements FxController {
|
||||
public String getDirectoryPath() {
|
||||
return directoryPath.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> selectedMountServiceRequiresRestartProperty() {
|
||||
return selectedMountServiceRequiresRestart;
|
||||
}
|
||||
|
||||
public boolean getSelectedMountServiceRequiresRestart() {
|
||||
return selectedMountServiceRequiresRestart.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> loopbackPortChangeableProperty() {
|
||||
return loopbackPortChangeable;
|
||||
}
|
||||
|
||||
public boolean isLoopbackPortChangeable() {
|
||||
return loopbackPortChangeable.getValue();
|
||||
}
|
||||
|
||||
private class MountServiceConverter extends StringConverter<MountService> {
|
||||
|
||||
@Override
|
||||
public String toString(MountService provider) {
|
||||
if (provider == null) {
|
||||
return String.format(resourceBundle.getString("vaultOptions.mount.volumeType.default"), defaultMountService.getValue().displayName());
|
||||
} else {
|
||||
return provider.displayName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MountService fromString(String string) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.cryptomator.ui.vaultoptions;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
@@ -48,6 +49,10 @@ public class VaultOptionsController implements FxController {
|
||||
if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){
|
||||
tabPane.getTabs().remove(hubTab);
|
||||
}
|
||||
|
||||
vault.stateProperty().addListener(observable -> {
|
||||
tabPane.setDisable(vault.getState().equals(VaultState.Value.UNLOCKED));
|
||||
});
|
||||
}
|
||||
|
||||
private void selectChosenTab() {
|
||||
|
||||
@@ -936,3 +936,16 @@
|
||||
-fx-padding: 0.5px;
|
||||
-fx-background-color: CONTROL_BORDER_NORMAL;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Ad box *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.ad-box {
|
||||
-fx-padding: 12px;
|
||||
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
||||
-fx-background-insets: 0, 1px;
|
||||
-fx-background-radius: 4px;
|
||||
}
|
||||
@@ -935,3 +935,16 @@
|
||||
-fx-padding: 0.5px;
|
||||
-fx-background-color: CONTROL_BORDER_NORMAL;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Ad box *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.ad-box {
|
||||
-fx-padding: 12px;
|
||||
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
||||
-fx-background-insets: 0, 1px;
|
||||
-fx-background-radius: 4px;
|
||||
}
|
||||
@@ -41,7 +41,7 @@
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.register.description" wrapText="true"/>
|
||||
<Label text="%hub.register.legacy.description" wrapText="true"/>
|
||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets top="12"/>
|
||||
@@ -50,7 +50,7 @@
|
||||
<TextField fx:id="deviceNameField" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
<HBox alignment="TOP_RIGHT">
|
||||
<Label text="%hub.register.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
|
||||
<Label text="%hub.register.legacy.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
|
||||
<padding>
|
||||
<Insets top="6"/>
|
||||
</padding>
|
||||
|
||||
51
src/main/resources/fxml/hub_legacy_register_success.fxml
Normal file
51
src/main/resources/fxml/hub_legacy_register_success.fxml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.LegacyRegisterSuccessController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.registerSuccess.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.registerSuccess.legacy.description" wrapText="true"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
@@ -57,15 +57,6 @@
|
||||
<TextField fx:id="deviceNameField" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
<HBox alignment="TOP_RIGHT">
|
||||
<Label text="%hub.register.occupiedMsg" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.deviceNameAlreadyExists}" managed="${controller.deviceNameAlreadyExists}" graphicTextGap="6">
|
||||
<padding>
|
||||
<Insets top="6"/>
|
||||
</padding>
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red"/>
|
||||
</graphic>
|
||||
</Label>
|
||||
|
||||
<Label text="%hub.register.invalidAccountKeyLabel" textAlignment="RIGHT" alignment="CENTER_RIGHT" visible="${controller.invalidSetupCode}" managed="${controller.invalidSetupCode}" graphicTextGap="6">
|
||||
<padding>
|
||||
<Insets top="6"/>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.RegisterFailedController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.registerFailed.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.registerFailed.description.deviceAlreadyExists" wrapText="true"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
@@ -38,7 +38,7 @@
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.registerFailed.description" wrapText="true"/>
|
||||
<Label text="%hub.registerFailed.description.generic" wrapText="true"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
<Label text="%hub.registerSuccess.description" wrapText="true"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
|
||||
<Button text="%hub.registerSuccess.unlockBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#complete"/>
|
||||
<!-- TODO: add request access button -->
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</ImageView>
|
||||
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
||||
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
||||
<Label text="© 2016 – 2023 Skymatic GmbH"/>
|
||||
<Label text="© 2016 – 2024 Skymatic GmbH"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
</Hyperlink>
|
||||
</HBox>
|
||||
|
||||
<Label styleClass="label-red" text="%preferences.volume.fuseRestartRequired" visible="${controller.fuseRestartRequired}" managed="${controller.fuseRestartRequired}"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}">
|
||||
<Label text="%preferences.volume.tcp.port"/>
|
||||
<NumericTextField fx:id="loopbackPortField"/>
|
||||
|
||||
111
src/main/resources/fxml/share_vault.fxml
Normal file
111
src/main/resources/fxml/share_vault.fxml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.sharevault.ShareVaultController"
|
||||
prefWidth="540"
|
||||
spacing="12">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="INFO" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox>
|
||||
<VBox HBox.hgrow="ALWAYS" visible="${controller.hubVault}" managed="${controller.hubVault}">
|
||||
<Label text="%shareVault.hub.message" styleClass="label-large" wrapText="true">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%shareVault.hub.description" wrapText="true"/>
|
||||
<VBox>
|
||||
<padding>
|
||||
<Insets left="6"/>
|
||||
</padding>
|
||||
<Label text="%shareVault.hub.instruction.1" wrapText="true"/>
|
||||
<Label text="%shareVault.hub.instruction.2" wrapText="true"/>
|
||||
</VBox>
|
||||
</VBox>
|
||||
<VBox HBox.hgrow="ALWAYS" visible="${!controller.hubVault}" managed="${!controller.hubVault}">
|
||||
<Label text="%shareVault.message" styleClass="label-large" wrapText="true">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%shareVault.description" wrapText="true"/>
|
||||
<VBox>
|
||||
<padding>
|
||||
<Insets left="6"/>
|
||||
</padding>
|
||||
<Label text="%shareVault.instruction.1" wrapText="true"/>
|
||||
<Label text="%shareVault.instruction.2" wrapText="true"/>
|
||||
</VBox>
|
||||
<Region minHeight="6"/>
|
||||
<TextFlow styleClass="text-flow">
|
||||
<Text text="%shareVault.remarkBestPractices"/>
|
||||
<Text text=" "/>
|
||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#visitBestPractices">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="%shareVault.docsTooltip" showDelay="100ms"/>
|
||||
</tooltip>
|
||||
</Hyperlink>
|
||||
</TextFlow>
|
||||
<Region minHeight="12"/>
|
||||
<HBox alignment="CENTER_LEFT" spacing="6" styleClass="ad-box">
|
||||
<VBox spacing="6" alignment="CENTER_LEFT">
|
||||
<ImageView HBox.hgrow="ALWAYS" fitWidth="180" preserveRatio="true" cache="true">
|
||||
<Image url="@../img/hub_logo.png"/>
|
||||
</ImageView>
|
||||
<Label text="%shareVault.hubAd.description" style="-fx-font-weight: bold;" wrapText="true"/>
|
||||
<VBox spacing="6" alignment="CENTER_LEFT">
|
||||
<padding>
|
||||
<Insets left="6"/>
|
||||
</padding>
|
||||
<Label text="%shareVault.hubAd.keyManagement" wrapText="true"/>
|
||||
<Label text="%shareVault.hubAd.authentication" wrapText="true"/>
|
||||
<Label text="%shareVault.hubAd.encryption" wrapText="true"/>
|
||||
</VBox>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<ImageView HBox.hgrow="ALWAYS" fitWidth="180" preserveRatio="true" cache="true">
|
||||
<Image url="@../img/group-magic.png"/>
|
||||
</ImageView>
|
||||
</HBox>
|
||||
</VBox>
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#close"/>
|
||||
<Button text="%shareVault.hub.openHub" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#openHub" visible="${controller.hubVault}" managed="${controller.hubVault}"/>
|
||||
<Button text="%shareVault.visitHub" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#visitHub" visible="${!controller.hubVault}" managed="${!controller.hubVault}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</HBox>
|
||||
58
src/main/resources/fxml/unlock_requires_restart.fxml
Normal file
58
src/main/resources/fxml/unlock_requires_restart.fxml
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.unlock.UnlockRequiresRestartController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-red" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="TIMES" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%unlock.error.restartRequired.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
|
||||
<Label text="%unlock.error.restartRequired.description" wrapText="true" textAlignment="LEFT"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
|
||||
<buttons>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
|
||||
<Button text="%main.vaultlist.contextMenu.vaultoptions" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenVaultOptions">
|
||||
<tooltip>
|
||||
<Tooltip text="%main.vaultlist.contextMenu.vaultoptions"/>
|
||||
</tooltip>
|
||||
</Button>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
@@ -24,14 +24,17 @@
|
||||
<FontAwesome5IconView glyph="KEY" glyphSize="15"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Hyperlink text="%main.vaultDetail.passwordSavedInKeychain" visible="${controller.passwordSaved}" onAction="#showKeyVaultOptions">
|
||||
<Hyperlink text="%main.vaultDetail.passwordSavedInKeychain" visible="${controller.passwordSaved}" managed="${controller.passwordSaved}" onAction="#showKeyVaultOptions">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="LOCK"/>
|
||||
</graphic>
|
||||
</Hyperlink>
|
||||
|
||||
<Button text="%main.vaultDetail.share" minWidth="120" onAction="#share">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="SHARE" glyphSize="15"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<HBox alignment="BOTTOM_RIGHT">
|
||||
<Button text="%main.vaultDetail.optionsBtn" minWidth="120" onAction="#showVaultOptions">
|
||||
<graphic>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import org.cryptomator.ui.controls.NumericTextField?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
@@ -10,9 +11,9 @@
|
||||
<?import javafx.scene.control.RadioButton?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.ToggleGroup?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
||||
@@ -24,11 +25,35 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<TextFlow>
|
||||
<Label text="%vaultOptions.mount.info"/>
|
||||
<Label text=" "/>
|
||||
<Hyperlink styleClass="hyperlink-underline" text="%vaultOptions.mount.linkToPreferences" onAction="#openVolumePreferences" wrapText="true"/>
|
||||
</TextFlow>
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
<Label text="%vaultOptions.mount.volume.type"/>
|
||||
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
|
||||
</tooltip>
|
||||
</Hyperlink>
|
||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
|
||||
</tooltip>
|
||||
</Hyperlink>
|
||||
</HBox>
|
||||
|
||||
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
|
||||
<Label text="%vaultOptions.mount.volume.tcp.port"/>
|
||||
<NumericTextField fx:id="vaultLoopbackPortField"/>
|
||||
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
||||
</HBox>
|
||||
|
||||
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlySupported}" managed="${controller.readOnlySupported}"/>
|
||||
|
||||
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
||||
|
||||
@@ -48,6 +48,7 @@ addvaultwizard.new.nameInstruction=Choose a name for the vault
|
||||
addvaultwizard.new.namePrompt=Vault Name
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=Where should Cryptomator store the encrypted files of your vault?
|
||||
addvaultwizard.new.locationLoading=Checking local filesystem for default cloud storage directories…
|
||||
addvaultwizard.new.locationLabel=Storage location
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Custom location
|
||||
@@ -142,6 +143,9 @@ unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Your vault could not be mounted to the path "%s". Please try again or choose a different path.
|
||||
unlock.error.customPath.description.notEmptyDir=The custom mount path "%s" is not an empty folder. Please choose an empty folder and try again.
|
||||
unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %2$s
|
||||
unlock.error.restartRequired.message=Unable to unlock vault
|
||||
unlock.error.restartRequired.description=Change the volume type in vault options or restart Cryptomator.
|
||||
unlock.error.title=Unlock "%s" failed
|
||||
## Hub
|
||||
hub.noKeychain.message=Unable to access device key
|
||||
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
|
||||
@@ -155,17 +159,22 @@ hub.receive.message=Processing response…
|
||||
hub.receive.description=Cryptomator is receiving and processing the response from Hub. Please wait.
|
||||
### Register Device
|
||||
hub.register.message=New Device
|
||||
hub.register.description=This is the first Hub access from this device. Please authorize it using your Account Key.
|
||||
hub.register.description=This is the first Hub access from this device. Please register it using your Account Key.
|
||||
hub.register.nameLabel=Device Name
|
||||
hub.register.invalidAccountKeyLabel=Invalid Account Key
|
||||
hub.register.occupiedMsg=Name already in use
|
||||
hub.register.registerBtn=Confirm
|
||||
hub.register.registerBtn=Register
|
||||
### Register Device Legacy
|
||||
hub.register.legacy.occupiedMsg=Name already in use
|
||||
hub.register.legacy.description=This is the first Hub access from this device. Please register it.
|
||||
### Registration Success
|
||||
hub.registerSuccess.message=Device named
|
||||
hub.registerSuccess.description=To access the vault, your device needs to be authorized by the vault owner.
|
||||
hub.registerSuccess.message=Device registered
|
||||
hub.registerSuccess.description=Your device is successfully registered. You can now continue to unlock the vault.
|
||||
hub.registerSuccess.unlockBtn=Unlock
|
||||
hub.registerSuccess.legacy.description=To access the vault, your device needs to be additionally authorized by the vault owner.
|
||||
### Registration Failed
|
||||
hub.registerFailed.message=Device naming failed
|
||||
hub.registerFailed.description=An error was thrown in the naming process. For more details, look into the application log.
|
||||
hub.registerFailed.message=Device registration failed
|
||||
hub.registerFailed.description.generic=An error was thrown in the registration process. For more details, look into the application log.
|
||||
hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device.
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Access denied
|
||||
hub.unauthorized.description=Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.
|
||||
@@ -295,11 +304,11 @@ preferences.interface.showMinimizeButton=Show minimize button
|
||||
preferences.interface.showTrayIcon=Show tray icon (requires restart)
|
||||
## Volume
|
||||
preferences.volume=Virtual Drive
|
||||
preferences.volume.type=Volume Type
|
||||
preferences.volume.type=Default Volume Type
|
||||
preferences.volume.type.automatic=Automatic
|
||||
preferences.volume.docsTooltip=Open the documentation to learn more about the different volume types.
|
||||
preferences.volume.fuseRestartRequired=To apply the changes, Cryptomator needs to be restarted.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.tcp.port=Default TCP Port
|
||||
preferences.volume.supportedFeatures=The chosen volume type supports the following features:
|
||||
preferences.volume.feature.mountAuto=Automatic mount point selection
|
||||
preferences.volume.feature.mountToDir=Custom directory as mount point
|
||||
@@ -384,6 +393,7 @@ main.vaultDetail.unlockBtn=Unlock…
|
||||
main.vaultDetail.unlockNowBtn=Unlock Now
|
||||
main.vaultDetail.optionsBtn=Vault Options
|
||||
main.vaultDetail.passwordSavedInKeychain=Password saved
|
||||
main.vaultDetail.share=Share…
|
||||
### Unlocked
|
||||
main.vaultDetail.unlockedStatus=UNLOCKED
|
||||
main.vaultDetail.accessLocation=Your vault's contents are accessible here:
|
||||
@@ -438,8 +448,7 @@ vaultOptions.general.startHealthCheckBtn=Start Health Check
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Mounting
|
||||
vaultOptions.mount.info=Options depend on the selected volume type.
|
||||
vaultOptions.mount.linkToPreferences=Open virtual drive preferences
|
||||
vaultOptions.mount.info=Open virtual drive preferences to change default settings.
|
||||
vaultOptions.mount.readonly=Read-only
|
||||
vaultOptions.mount.customMountFlags=Custom mount flags
|
||||
vaultOptions.mount.winDriveLetterOccupied=occupied
|
||||
@@ -449,6 +458,10 @@ vaultOptions.mount.mountPoint.driveLetter=Use assigned drive letter
|
||||
vaultOptions.mount.mountPoint.custom=Use chosen directory
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Pick a directory
|
||||
vaultOptions.mount.volumeType.default=Default (%s)
|
||||
vaultOptions.mount.volumeType.restartRequired=To use this volume type, Cryptomator needs to be restarted.
|
||||
vaultOptions.mount.volume.tcp.port=TCP Port
|
||||
vaultOptions.mount.volume.type=Volume Type
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Password
|
||||
vaultOptions.masterkey.changePasswordBtn=Change Password
|
||||
@@ -518,4 +531,24 @@ updateReminder.message=Check for Updates?
|
||||
updateReminder.description=Stay updated with new features, bug fixes, and security improvements. We recommend to automatically check for updates.
|
||||
updateReminder.notNow=Not Now
|
||||
updateReminder.yesOnce=Yes, Once
|
||||
updateReminder.yesAutomatically=Yes, Automatically
|
||||
updateReminder.yesAutomatically=Yes, Automatically
|
||||
|
||||
# Share Vault
|
||||
shareVault.title=Share Vault
|
||||
shareVault.message=Would you like to share your vault with others?
|
||||
shareVault.description=Always be careful when sharing your vault with other people. In short, follow these steps:
|
||||
shareVault.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
|
||||
shareVault.instruction.2=2. Share the vault password in a secure way.
|
||||
shareVault.remarkBestPractices=For more information, check out the best practices suggestions in our docs.
|
||||
shareVault.docsTooltip=Open the documentation to learn more about sharing of vaults.
|
||||
shareVault.hubAd.description=The secure way to work in teams
|
||||
shareVault.hubAd.keyManagement=• Zero-knowledge key management
|
||||
shareVault.hubAd.authentication=• Strong authentication
|
||||
shareVault.hubAd.encryption=• End-to-end encryption
|
||||
shareVault.visitHub=Visit Cryptomator Hub
|
||||
|
||||
shareVault.hub.message=How to share a Hub vault
|
||||
shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps:
|
||||
shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
|
||||
shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub.
|
||||
shareVault.hub.openHub=Open Cryptomator Hub
|
||||
@@ -287,11 +287,9 @@ preferences.interface.showMinimizeButton=إظهار زر التصغير
|
||||
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
|
||||
## Volume
|
||||
preferences.volume=القرص الإفتراضي
|
||||
preferences.volume.type=نوع وحدة التخزين
|
||||
preferences.volume.type.automatic=تلقائي
|
||||
preferences.volume.docsTooltip=افتح الوثائق لمعرفة المزيد عن مختلف أنواع وحدة التخزين.
|
||||
preferences.volume.fuseRestartRequired=لتطبيق التغييرات، يحتاج Cryptomator إلى إعادة التشغيل.
|
||||
preferences.volume.tcp.port=منفذ TCP
|
||||
preferences.volume.supportedFeatures=يدعم نوع وحدة تخزين المختار الميزات التالية:
|
||||
## Updates
|
||||
preferences.updates=تحديثات
|
||||
|
||||
514
src/main/resources/i18n/strings_ba.properties
Normal file
514
src/main/resources/i18n/strings_ba.properties
Normal file
@@ -0,0 +1,514 @@
|
||||
# Locale Specific CSS files such as CJK, RTL,...
|
||||
|
||||
# Generics
|
||||
## Button
|
||||
generic.button.apply=Ҡуллан
|
||||
generic.button.back=Артҡа
|
||||
generic.button.cancel=Кире ал
|
||||
generic.button.change=Үҙгәрт
|
||||
generic.button.choose=Һайла…
|
||||
generic.button.close=Яп
|
||||
generic.button.copy=Күсермәһен ал
|
||||
generic.button.copied=Күсермә алынды!
|
||||
generic.button.done=Тамам
|
||||
generic.button.next=Киләһе
|
||||
generic.button.print=Баҫтыр
|
||||
|
||||
# Error
|
||||
error.message=Хата килеп сыҡты
|
||||
error.description=Cryptomator өсөн көтөлмәгән хәл. Был хатаның булған төҙәтеү варианттарын ҡарай алаһығыҙ. Әлегә тиклем белдерелмәгән булһа, был хаҡта хәбәр итә алаһығыҙ.
|
||||
error.hyperlink.lookup=Был хатаны тикшерегеҙ
|
||||
error.hyperlink.report=Был хатаны белдерегеҙ
|
||||
error.technicalDetails=Тулыраҡ мәғлүмәт:
|
||||
error.existingSolutionDescription=Cryptomator бының булырын көтмәгән. Әммә беҙ был хатаны хәл итер юл таптыҡ. Түбәндәге һылтанманы ҡарап сығығыҙ.
|
||||
error.hyperlink.solution=Хәл итеү варианттарын ҡарарға
|
||||
error.lookupPermissionMessage=Cryptomator был проблеманы онлайн хәл итә ала. Был ваҡытта IP-адресығыҙҙан беҙҙең проблемалар базаһына һоратыу ебәреләсәк.
|
||||
error.dismiss=Кире ҡаҡ
|
||||
error.lookUpSolution=Хәл итеү варианттарын ҡара
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Һаҡлағыс
|
||||
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=Күрһәт
|
||||
traymenu.showPreferencesWindow=Көйләүҙәр
|
||||
traymenu.lockAllVaults=Барыһын да биклә
|
||||
traymenu.quitApplication=Сыҡ
|
||||
traymenu.vault.unlock=Биген ас
|
||||
traymenu.vault.lock=Биклә
|
||||
traymenu.vault.reveal=Күрһәт
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Һаҡлағыс өҫтәү
|
||||
## New
|
||||
addvaultwizard.new.title=Яңы һаҡлағыс өҫтәү
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Һаҡлағыс өсөн исем һайлағыҙ
|
||||
addvaultwizard.new.namePrompt=Һаҡлағыс исеме
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=Cryptomator һаҡлағысығыҙҙың шифрланған файлдарын ҡайҙа һаҡларға тейеш?
|
||||
addvaultwizard.new.locationLabel=Һаҡлау урыны
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Башҡа урын
|
||||
addvaultwizard.new.directoryPickerButton=Һайла…
|
||||
addvaultwizard.new.directoryPickerTitle=Каталог һайлау
|
||||
addvaultwizard.new.fileAlreadyExists=Һаҡлағыс исеме менән бер файл йәки каталог бар
|
||||
addvaultwizard.new.locationDoesNotExist=Билдәләнгән юлда каталог юҡ йәки уға инеп булмай
|
||||
addvaultwizard.new.locationIsNotWritable=Билдәләнгән юлда яҙыу мөмкинлеге юҡ
|
||||
addvaultwizard.new.locationIsOk=Һаҡлағыс өсөн яраҡлы урын
|
||||
addvaultwizard.new.invalidName=Яраҡһыҙ һаҡлағыс исеме
|
||||
addvaultwizard.new.validName=Яраҡлы һаҡлағыс исеме
|
||||
addvaultwizard.new.validCharacters.message=Һаҡлағыс исемендә түбәндәге билдәләр була ала:
|
||||
addvaultwizard.new.validCharacters.chars=Хәрефтәр (мәҫ. a, ж or 수)
|
||||
addvaultwizard.new.validCharacters.numbers=Һандар
|
||||
addvaultwizard.new.validCharacters.dashes=Дефис (%s) йәки аҫҡы һыҙыҡ (%s)
|
||||
### Expert Settings
|
||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Эксперт көйләүҙәре ғәмәлдә
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=36 менән 220 араһында ҡиммәт яҙығыҙ (ғәҙәттә 220)
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Тулыраҡ мәғлүмәт алыр өсөн документтарҙы ҡарағыҙ.
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=Шифрланған файл исемдәренең максимум оҙонлоғо
|
||||
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Яраҡлы
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Һаҡлағыс яһа
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Серһүҙһеҙ мәғлүмәттәрегеҙгә инеп булмаясаҡ. Серһүҙ юғалтҡан осраҡ өсөн тергеҙеү асҡысы кәрәкме?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Эйе, аҙаҡтан терһәк тешләүҙән һаҡ булыу яҡшыраҡ
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Юҡ, рәхмәт, мин серһүҙемде юғалтмаясаҡмын
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=МӨҺИМ.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ ҺАҠЛАҒЫС ФАЙЛДАРЫ ⚠️
|
||||
addvault.new.readme.storageLocation.2=Был һаҡлағысығыҙҙы һаҡлау урыны.
|
||||
addvault.new.readme.storageLocation.3=ЯРАМАЙ
|
||||
addvault.new.readme.storageLocation.4=• был каталогтағы файлдарҙы үҙгәртеү
|
||||
addvault.new.readme.storageLocation.5=• шифрлау өсөн ниндәй ҙә булһа файлды ошо каталогка һалыу
|
||||
addvault.new.readme.storageLocation.6=Әгәр файлдарҙы шифрларға һәм һаҡлағыстың эстәлеген ҡарарға теләһәгеҙ, түбәндәгеләрҙе эшләгеҙ:
|
||||
addvault.new.readme.storageLocation.7=1. Был һаҡлағысты Cryptomator-ға өҫтәгеҙ.
|
||||
addvault.new.readme.storageLocation.8=2. Cryptomator-ҙа һаҡлағысты асығыҙ.
|
||||
addvault.new.readme.storageLocation.9=3. "Күрһәт" төймәһенә баҫып, инеү урынын асығыҙ.
|
||||
addvault.new.readme.storageLocation.10=Әгәр ярҙам кәрәк булһа, документтарҙы ҡарағыҙ: %s
|
||||
addvault.new.readme.accessLocation.fileName=РӘХИМ ИТЕГЕҘ.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ ШИФРЛАНҒАН ТОМ 🔐️
|
||||
addvault.new.readme.accessLocation.2=Был һаҡлағысығыҙға инеү урыны.
|
||||
addvault.new.readme.accessLocation.3=Был томға өҫтәлгән файлдар Cryptomator тарафынан шифрлана. Уның менән башҡа диск/каталог кеүек эшләй алаһығыҙ. Был уның эстәлегенең дешифрланған күренеше генә, һеҙҙең файлдар ҡаты дискығыҙҙа һәр ваҡыт шифрланған булып ҡала.
|
||||
addvault.new.readme.accessLocation.4=Был файлды юйырға була.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Булған һаҡлағыс өҫтәү
|
||||
addvaultwizard.existing.instruction=Һаҡлағысығыҙҙа "vault.cryptomator" файлын һайлағыҙ. Әгәр "masterkey.cryptomator" исемле файл ғына булһа, уны һайлағыҙ.
|
||||
addvaultwizard.existing.chooseBtn=Һайла…
|
||||
addvaultwizard.existing.filePickerTitle=Һаҡлағыс файлын һайлау
|
||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Һаҡлағысы
|
||||
## Success
|
||||
addvaultwizard.success.nextStepsInstructions=\ "%s" һаҡлағысы өҫтәлде.\nЭстәлеккә инеү йәки өҫтәү өсөн был һаҡлағысты асыр кәрәк. Уны аҙаҡтан да асырға була.
|
||||
addvaultwizard.success.unlockNow=Хәҙер бикте ас
|
||||
|
||||
# Remove Vault
|
||||
removeVault.title="%s" һаҡлағысын алып ташла
|
||||
removeVault.message=Һаҡлағысты алып ташларғамы?
|
||||
removeVault.description=Cryptomator был һаҡлағысты ғына онотасаҡ. Һеҙ уны яңынан өҫтәй алаһығыҙ. Ҡаты дисктан шифрланған файлдар юйылмаясаҡ.
|
||||
removeVault.confirmBtn=Һаҡлағысты алып ташла
|
||||
|
||||
# Change Password
|
||||
changepassword.title=Серһүҙҙе үҙгәртеү
|
||||
changepassword.enterOldPassword="%s" өсөн ағымдағы серһүҙҙе яҙығыҙ
|
||||
changepassword.finalConfirmation=Серһүҙемде онотһам, мәғлүмәттәремә инә алмаясағымды аңлайым
|
||||
|
||||
# Forget Password
|
||||
forgetPassword.title=Серһүҙҙе онот
|
||||
forgetPassword.message=Һаҡланған серһүҙҙе оноторғамы?
|
||||
forgetPassword.description=Был һаҡлағыстың һеҙҙең система асҡыс сылбырында һаҡланған серһүҙен юясаҡ.
|
||||
forgetPassword.confirmBtn=Серһүҙҙе онот
|
||||
|
||||
# Unlock
|
||||
unlock.title="%s" биген асыу
|
||||
unlock.passwordPrompt="%s" серһүҙен яҙығыҙ:
|
||||
unlock.savePassword=Серһүҙҙе һаҡла
|
||||
unlock.unlockBtn=Биген ас
|
||||
## Select
|
||||
unlock.chooseMasterkey.message=Masterkey файлы табылманы
|
||||
unlock.chooseMasterkey.description=Cryptomator "%s" һаҡлағысының masterkey файлын таба алманы. Асҡыс файлын ҡулдан һайлағыҙ.
|
||||
unlock.chooseMasterkey.filePickerTitle=Masterkey файлын һайлағыҙ
|
||||
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
||||
## Success
|
||||
unlock.success.message=Бикте сисеү уңышлы тамамланды
|
||||
unlock.success.description="%s" һаҡлағысының эстәлеге хәҙер уның тоташтырыу нөктәһе аша асыҡ.
|
||||
unlock.success.rememberChoice=Һайлауымды иҫтә тот, ҡабат һорама
|
||||
unlock.success.revealBtn=Дискты күрһәт
|
||||
## Failure
|
||||
unlock.error.customPath.message=Һаҡлағысты күрһәтелгән юлға тоташтырып булманы
|
||||
unlock.error.customPath.description.notSupported=Махсус юлды артабан да ҡулланырға теләһәгеҙ, көйләүҙәргә барып уны терәкләгән том төрөн һайлағыҙ. Юғиһә, һаҡлағыс көйләүҙәренә күсерегеҙ һәм терәкләнгән тоташтырыу урыны һайлағыҙ.
|
||||
unlock.error.customPath.description.notExists=Махсус тоташтырыу юлы юҡ. Уны урындағы файл системаһында яһағыҙ йәки һаҡлағыс параметрҙарында үҙгәртегеҙ.
|
||||
unlock.error.customPath.description.inUse="%s" диск хәрефе йәки махсус тоташтырыу юлы ҡулланыла инде.
|
||||
unlock.error.customPath.description.hideawayNotDir=Асыу өсөн ҡулланылған ваҡытлыса, йәшерен "%3$s" файлын алып ташлау мөмкин түгел. Файлды тикшереп, һуңынан ҡул менән юйығыҙ.
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Һаҡлағысығыҙ "%s" юлына тоташтырылманы. Тағы бер тапҡыр ҡабатлап ҡарағыҙ йәки икенсе юл һайлағыҙ.
|
||||
unlock.error.customPath.description.notEmptyDir="%s" махсус тоташтырыу урыны буш каталог түгел. Зинһар, буш каталог һайлағыҙ һәм яңынан ҡабатлағыҙ.
|
||||
unlock.error.customPath.description.generic=Һеҙ был һаҡлағыс өсөн махсус тоташтырыу урыны һайланығыҙ, ләкин уны ҡулланыу киләһе хәбәр менән уңышһыҙ тамамланды: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Һаҡлағыс биген сисеп булманы
|
||||
unlock.error.fuseRestartRequired.description=Һаҡлағыс параметрҙарында күләм төрөн үҙгәртеү йәки Cryptomator-ҙы яңынан башлатыу.
|
||||
unlock.error.title="%s" биге асылманы
|
||||
## Hub
|
||||
hub.noKeychain.message=Йыһаз асҡысына инеү рөхсәте юҡ
|
||||
hub.noKeychain.description=Хаб һаҡлағыстарын асыу өсөн йыһаз асҡысы кәрәк, ул иһә асҡыс сылбырында һаҡлана. Артабан “%s” мөмкинлеге бирегеҙ һәм көйләүҙәрҙә асҡыс сылбырын һайлағыҙ.
|
||||
hub.noKeychain.openBtn=Көйләүҙәрҙе ас
|
||||
### Waiting
|
||||
hub.auth.message=Аутентиклау көтөлә…
|
||||
hub.auth.description=Автоматик рәүештә инеү битенә йүнәлтелергә тейешһегеҙ.
|
||||
hub.auth.loginLink=Йүнәлтелмәнегеҙме? Асыу өсөн ошонда баҫығыҙ.
|
||||
### Receive Key
|
||||
hub.receive.message=Яуапты эшкәртеү…
|
||||
hub.receive.description=Cryptomator хабтан килгән яуапты ҡабул итә һәм эшкәртә. Зинһар, көтөгөҙ.
|
||||
### Register Device
|
||||
hub.register.message=Яңы йыһаз
|
||||
hub.register.description=Был йыһаздан хабҡа тәү тапҡыр инеү. Зинһар, иҫәп яҙмаһы асҡысы менән рөхсәт бирегеҙ.
|
||||
hub.register.nameLabel=Йыһаз исеме
|
||||
hub.register.invalidAccountKeyLabel=Хаталы иҫәп яҙмаһы асҡысы
|
||||
hub.register.occupiedMsg=Исем ҡулланыла инде
|
||||
hub.register.registerBtn=Раҫла
|
||||
### Registration Success
|
||||
hub.registerSuccess.message=Йыһаз исемләнде
|
||||
hub.registerSuccess.description=Һаҡлағысҡа инеү өсөн һаҡлағыс хужаһы йыһазығыҙға инеү рөхсәте бирергә тейеш.
|
||||
### Registration Failed
|
||||
hub.registerFailed.message=Йыһазға исем биреп булманы
|
||||
hub.registerFailed.description=Исемләү процесында хата килеп сыҡты. Тулыраҡ мәғлүмәт өсөн ҡушымта журналын ҡарағыҙ.
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Инеү кире ҡағылды
|
||||
hub.unauthorized.description=Һеҙҙең йыһаз әлегә был һаҡлағысҡа инеү хоҡуғына эйә түгел. Һаҡлағыс хужаһынан рөхсәт һорағыҙ.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
|
||||
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
|
||||
hub.requireAccountInit.description.1=Хаб ҡулланыусы профиле
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
hub.invalidLicense.message=Хаб рөхсәтнамәһе яраҡһыҙ
|
||||
hub.invalidLicense.description=Һеҙҙең Cryptomator хабығыҙ ғәмәлдән тыш рөхсәтнамәгә эйә. Рөхсәтнамәне яңыртыу йәки оҙайтыу өсөн Хаб администраторына хәбәр итегеҙ.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.message=Бикләп булманы
|
||||
lock.forced.description=Көтөп торған ғәмәлдәр йәки асыҡ файлдар "%s" бикләүҙе тотҡарланы. Был һаҡлағысты көсләп бикләп була, ләкин инеү-сығыуҙы өҙөү һаҡланмаған мәғлүмәттәрҙе юғалтыуға килтереүе ихтимал.
|
||||
lock.forced.retryBtn=Ҡабатла
|
||||
lock.forced.forceBtn=Көсләп биклә
|
||||
## Failure
|
||||
lock.fail.message=Һаҡлағысты бикләп булманы
|
||||
lock.fail.description="%s" һаҡлағысын бикләп булмай. Һаҡланмаған эштең һаҡланыуын һәм мөһим уҡыу/яҙыу ғәмәлдәренең тамамланыуын тикшерегеҙ. һаҡлағысты ябыр өсөн Cryptomator процесын туҡтатығыҙ.
|
||||
|
||||
# Migration
|
||||
migration.title=Һаҡлағысты яңыртыу
|
||||
## Start
|
||||
migration.start.header=Һаҡлағысты яңыртыу
|
||||
migration.start.text="%s" һаҡлағысын Cryptomator-ҙың яңы версияһында асыу өсөн һаҡлағысты яңы форматҡа күсерергә кәрәк. Быны эшләр алдынан түбәндәгеләрҙе белер кәрәк:
|
||||
migration.start.remarkUndone=Был яңыртыуҙы кире алып булмай.
|
||||
migration.start.remarkVersions=Cryptomator-ҙың иҫке версиялары яңыртылған һаҡлағысты аса алмай.
|
||||
migration.start.remarkCanRun=Һаҡлағысты ҡулланған һәр йыһаздың Cryptomator-ҙың был версияһы менән эшләй аласағын тикшереү кәрәк.
|
||||
migration.start.remarkSynced=Яңыртыуҙан алда һаҡлағыстың был һәм башҡа йыһыздарҙа тулыһынса синхронлаштырылыуына тикшерер кәрәк.
|
||||
migration.start.confirm=Юғарыла килтерелгән мәғлүмәтте уҡыным һәм аңланым
|
||||
## Run
|
||||
migration.run.enterPassword="%s" серһүҙен яҙығыҙ
|
||||
migration.run.startMigrationBtn=Һаҡлағысты күсер
|
||||
migration.run.progressHint=Был бер ни тиклем ваҡыт ала…
|
||||
## Success
|
||||
migration.success.nextStepsInstructions="%s" уңышлы күсерелде.\nХәҙер һаҡлағысығыҙҙы аса алаһығыҙ.
|
||||
migration.success.unlockNow=Хәҙер бикте ас
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=Терәкләнмәгән файл системаһы
|
||||
migration.error.missingFileSystemCapabilities.description=Күсеү башланманы, сөнки һаҡлағысығыҙ яраҡһыҙ файл системаһында урынлашҡан.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Файл системаһы оҙон файл исемдәрен терәкләмәй.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Файл системаһы оҙон юлдарҙы терәкләмәй.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Файл системаһы уҡылыуға рөхсәт бирмәй.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Файл системаһы яҙыуға рөхсәт бирмәй.
|
||||
## Impossible
|
||||
migration.impossible.heading=Һаҡлағысты күсереп булманы
|
||||
migration.impossible.reason=Һаҡлағыс автоматик рәүештә күсерелә алмай, сөнки уның һаҡлау урыны йәки инеү урыны яраҡһыҙ.
|
||||
migration.impossible.moreInfo=Һаҡлағысты элекке версия менән асырға мөмкин. Ҡулдан күсереү буйынса инструкциялар өсөн, сайтҡа инегеҙ:
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
health.title="%s" хәлен тикшереү
|
||||
health.intro.header=Хәлде тикшереү
|
||||
health.intro.text=Хәлде тикшереү — көмбәҙҙең эске төҙөлөшөндәге проблемаларҙы асыҡлау һәм хәл итеү өсөн тикшереүҙәр тупланмаһы. Зинһар, иҫтә тотоғоҙ:
|
||||
health.intro.remarkSync=Бөтә йыһаздарҙың тулыһынса синхронлаштырылғанын ҡарағыҙ, был күпселек проблемаларҙы хәл итә.
|
||||
health.intro.remarkFix=Бөтә проблемаларҙы ла хәл итеү мөмкин түгел.
|
||||
health.intro.remarkBackup=Мәғлүмәттәр боҙолған осраҡта һаҡлыҡ файлдары ғына ярҙам итә ала.
|
||||
health.intro.affirmation=Юғарыла килтерелгән мәғлүмәтте уҡыным һәм аңланым
|
||||
## Start Failure
|
||||
health.fail.header=Һаҡлағыс конфигурацияһын тейәүҙәге хата
|
||||
health.fail.ioError=Конфигурация файлын асыу һәм уҡыу ваҡытында хата килеп сыҡты.
|
||||
health.fail.parseError=Һаҡлағыс конфигурацияһын тағатҡанда хата барлыҡҡа килде.
|
||||
health.fail.moreInfo=Тулыраҡ мәғлүмәт
|
||||
## Check Selection
|
||||
health.checkList.description=Һул яҡтағы исемлектән һайлағыҙ йәки аҫтағы төймәләрҙе ҡулланығыҙ.
|
||||
health.checkList.selectAllButton=Бөтәһен дә һайла
|
||||
health.checkList.deselectAllButton=Береһен дә һайлама
|
||||
health.check.runBatchBtn=Һайланған тикшереүҙәрҙе башлат
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=Һөҙөмтәләр өсөн һул яҡтағы исемлектә тамамланған хәл тикшереүен һайлағыҙ.
|
||||
health.check.detail.checkScheduled=Тикшереү планлаштырылған.
|
||||
health.check.detail.checkRunning=Әлеге ваҡытта тикшереү бара…
|
||||
health.check.detail.checkSkipped=Тикшереү үткәреү өсөн һайланмаған.
|
||||
health.check.detail.checkFinished=Тикшереү уңышлы тамамланды.
|
||||
health.check.detail.checkFinishedAndFound=Тикшереү тамамланды. Һөҙөмтәләрҙе ҡарап сығығыҙ.
|
||||
health.check.detail.checkFailed=Тикшереү хата арҡаһында тамамланманы.
|
||||
health.check.detail.checkCancelled=Тикшереү өҙөлдө.
|
||||
health.check.detail.listFilters.label=Фильтр
|
||||
health.check.detail.fixAllSpecificBtn=Бөтә төрҙәрҙе төҙәтеү
|
||||
health.check.exportBtn=Һөҙөмтәләрҙе экспортла
|
||||
## Result view
|
||||
health.result.severityFilter.all=Ауырлыҡ кимәле — Бөтәһе лә
|
||||
health.result.severityFilter.good=Яҡшы
|
||||
health.result.severityFilter.info=Мәғлүмәт
|
||||
health.result.severityFilter.warn=Иҫкәртеү
|
||||
health.result.severityFilter.crit=Критик
|
||||
health.result.severityTip.good=Ауырлыҡ кимәле: Яҡшы\nһаҡлағыстың ғәҙәти структураһы.
|
||||
health.result.severityTip.info=Ауырлыҡ кимәле: Мәғлүмәт\nһаҡлағыс структураһы боҙолмаған, төҙәтеү тәҡдим ителә.
|
||||
health.result.severityTip.warn=Ауырлыҡ кимәле: Иҫкәртеү\nһаҡлағыс структураһы боҙолған, төҙәтеү бик ныҡ кәңәш ителә.
|
||||
health.result.severityTip.crit=Ауырлыҡ кимәле: Критик\nһаҡлағыс структураһы боҙоҡ, мәғлүмәт юғалтыуҙары бар.
|
||||
health.result.fixStateFilter.all=Төҙәтеү торошо — Барыһы ла
|
||||
health.result.fixStateFilter.fixable=Төҙәтеп булған
|
||||
health.result.fixStateFilter.notFixable=Төҙәтелерлек түгел
|
||||
health.result.fixStateFilter.fixing=Төҙәтеү…
|
||||
health.result.fixStateFilter.fixed=Төҙәтелде
|
||||
health.result.fixStateFilter.fixFailed=Төҙәтеп булманы
|
||||
## Fix Application
|
||||
health.fix.fixBtn=Төҙәт
|
||||
health.fix.successTip=Төҙәтелде
|
||||
health.fix.failTip=Төҙәтеп булманы, тулыраҡ мәғлүмәт өсөн журналды ҡарағыҙ
|
||||
|
||||
# Preferences
|
||||
preferences.title=Көйләүҙәр
|
||||
## General
|
||||
preferences.general=Дөйөм
|
||||
preferences.general.startHidden=Cryptomator башланған саҡта тәҙрәне йәшерергә
|
||||
preferences.general.autoCloseVaults=Ҡушымтанан сыҡҡан ваҡытта һаҡлағыстарҙы автоматик рәүештә бикләргә
|
||||
preferences.general.debugLogging=Төҙөкләндереү журналын асырға
|
||||
preferences.general.debugDirectory=Журнал файлдарын күрһәт
|
||||
preferences.general.autoStart=Система стартында Cryptomator-ҙы эшләтеп ебәрергә
|
||||
preferences.general.keychainBackend=Серһүҙ һаҡлау өсөн
|
||||
## Interface
|
||||
preferences.interface=Интерфейс
|
||||
preferences.interface.theme=Күренеш
|
||||
preferences.interface.theme.automatic=Автоматик
|
||||
preferences.interface.theme.dark=Ҡараңғы
|
||||
preferences.interface.theme.light=Яҡты
|
||||
preferences.interface.unlockThemes=Ҡараңғы режимды ас
|
||||
preferences.interface.language=Тел (яңынан башлатыу кәрәк)
|
||||
preferences.interface.language.auto=Ғәҙәттәге система көйләүҙәре
|
||||
preferences.interface.interfaceOrientation=Интерфейс йүнәлеше
|
||||
preferences.interface.interfaceOrientation.ltr=Һулдан уңға
|
||||
preferences.interface.interfaceOrientation.rtl=Уңдан һулға
|
||||
preferences.interface.showMinimizeButton=Бәләкәйләтеү төймәһен күрһәтергә
|
||||
preferences.interface.showTrayIcon=Панелдә билдәне күрһәтергә (яңынан башлатыу кәрәк)
|
||||
## Volume
|
||||
preferences.volume=Виртуаль диск
|
||||
preferences.volume.type=Ғәҙәттәге том төрө
|
||||
preferences.volume.type.automatic=Автоматик
|
||||
preferences.volume.docsTooltip=Төрлө том төрҙәре тураһында күберәк белер өсөн, документтарҙы ҡарағыҙ.
|
||||
preferences.volume.fuseRestartRequired=Үҙгәрештәрҙе ҡулланыу өсөн Cryptomator-ҙы яңынан асыу кәрәк.
|
||||
preferences.volume.tcp.port=Ғәҙәттәге TCP порты
|
||||
preferences.volume.supportedFeatures=Һайланған том төрө түбәндәге үҙенсәлектәрҙе терәкләй:
|
||||
preferences.volume.feature.mountAuto=Тоташтырыу нөктәһен автоматик рәүештә һайлау
|
||||
preferences.volume.feature.mountToDir=Тоташтырыу нөктәһе өсөн махсус каталог һайлау
|
||||
preferences.volume.feature.mountToDriveLetter=Тоташтырыу нөктәһе ите диск хәрефен ҡулланыу
|
||||
preferences.volume.feature.mountFlags=Махсус тоташтырыу варианттары
|
||||
preferences.volume.feature.readOnly=Уҡыу ғына режимында тоташтырыу
|
||||
## Updates
|
||||
preferences.updates=Яңыртыуҙар
|
||||
preferences.updates.currentVersion=Ағымдағы версия: %s
|
||||
preferences.updates.autoUpdateCheck=Яңыртыуҙарҙы автоматик рәүештә тикшереү
|
||||
preferences.updates.checkNowBtn=Хәҙер тикшер
|
||||
preferences.updates.updateAvailable=%s версияһына тиклем яңыртыу бар.
|
||||
## Contribution
|
||||
preferences.contribute=Ярҙам
|
||||
preferences.contribute.registeredFor=%s өсөн теркәлгән ярҙамсы сертификаты
|
||||
preferences.contribute.noCertificate=Cryptomator-ҡа ярҙам итегеҙ һәм ярҙамсы сертификаты алығыҙ. Был рөхсәтнамә асҡысы кеүек, әммә ирекле программа ҡулланған бик шәп кешеләр өсөн. ;-)
|
||||
preferences.contribute.getCertificate=Юҡ мы әллә? Уны нисек алырға кәрәклеген ҡарағыҙ.
|
||||
preferences.contribute.promptText=Ярҙамсы сертификатын бында йәбештерегеҙ
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
preferences.about=Тураһында
|
||||
|
||||
# Vault Statistics
|
||||
stats.title=%s статистикаһы
|
||||
stats.cacheHitRate=Кэшҡа тип килеү йышлығы
|
||||
## Read
|
||||
stats.read.throughput.idle=Уҡыу: буш
|
||||
stats.read.throughput.kibs=Уҡыу: %.2f КиБ/с
|
||||
stats.read.throughput.mibs=Уҡыу: %.2f МиБ/с
|
||||
stats.read.total.data.none=Уҡылған: -
|
||||
stats.read.total.data.kib=Уҡылған: %.1f КиБ
|
||||
stats.read.total.data.mib=Уҡылған: %.1f МиБ
|
||||
stats.read.total.data.gib=Уҡылған: %.1f ГиБ
|
||||
stats.decr.total.data.none=Шифры сиселгән: -
|
||||
stats.decr.total.data.kib=Шифры сиселгән: %.1f КиБ
|
||||
stats.decr.total.data.mib=Шифры сиселгән: %.1f МиБ
|
||||
stats.decr.total.data.gib=Шифры сиселгән: %.1f ГиБ
|
||||
stats.read.accessCount=Барлыҡ уҡыу: %d
|
||||
## Write
|
||||
stats.write.throughput.idle=Яҙыу: буш
|
||||
stats.write.throughput.kibs=Яҙыу: %.2f КиБ/с
|
||||
stats.write.throughput.mibs=Яҙыу: %.2f МиБ/с
|
||||
stats.write.total.data.none=Яҙылған: -
|
||||
stats.write.total.data.kib=Яҙылған: %.1f КиБ
|
||||
stats.write.total.data.mib=Яҙылған: %.1f МиБ
|
||||
stats.write.total.data.gib=Яҙылған: %.1f ГиБ
|
||||
stats.encr.total.data.none=Шифрланған: -
|
||||
stats.encr.total.data.kib=Шифрланған: %.1f КиБ
|
||||
stats.encr.total.data.mib=Шифрланған: %.1f МиБ
|
||||
stats.encr.total.data.gib=Шифрланған: %.1f ГиБ
|
||||
stats.write.accessCount=Барлыҡ яҙыу: %d
|
||||
|
||||
## Accesses
|
||||
stats.access.current=Инеү: %d
|
||||
stats.access.total=Барлыҡ инеү: %d
|
||||
|
||||
|
||||
# Main Window
|
||||
main.closeBtn.tooltip=Яп
|
||||
main.minimizeBtn.tooltip=Бәләкәйләт
|
||||
main.preferencesBtn.tooltip=Көйләүҙәр
|
||||
main.debugModeEnabled.tooltip=Төҙөкләндереү режимы асыҡ
|
||||
main.supporterCertificateMissing.tooltip=Зинһар, иғәнә биреү тураһында уйлағыҙ
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Һаҡлағыс өҫтәү өсөн бында баҫығыҙ
|
||||
main.vaultlist.contextMenu.remove=Алып ташлау…
|
||||
main.vaultlist.contextMenu.lock=Биклә
|
||||
main.vaultlist.contextMenu.unlock=Бикте асыу…
|
||||
main.vaultlist.contextMenu.unlockNow=Хәҙер бикте ас
|
||||
main.vaultlist.contextMenu.vaultoptions=Һаҡлағыс варианттарын күрһәт
|
||||
main.vaultlist.contextMenu.reveal=Дискты күрһәт
|
||||
main.vaultlist.addVaultBtn=Өҫтәү
|
||||
main.vaultlist.addVaultBtn.menuItemNew=Яңы Һаҡлағыс...
|
||||
main.vaultlist.addVaultBtn.menuItemExisting=Булған һаҡлағыс...
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Файлдарығыҙҙы һаҡлау өсөн Cryptomator-ҙы һайлағанығыҙ өсөн рәхмәт. Әгәр һеҙгә ярҙам кәрәк булһа, башлау буйынса белешмәләребеҙҙе ҡарағыҙ:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=БИКЛЕ
|
||||
main.vaultDetail.unlockBtn=Биген ас…
|
||||
main.vaultDetail.unlockNowBtn=Хәҙер бикте ас
|
||||
main.vaultDetail.optionsBtn=Һаҡлағыс варианттары
|
||||
main.vaultDetail.passwordSavedInKeychain=Серһүҙ һаҡланды
|
||||
### Unlocked
|
||||
main.vaultDetail.unlockedStatus=АСЫҠ
|
||||
main.vaultDetail.accessLocation=Һаҡлағыс эстәлеген бында ҡарарға була:
|
||||
main.vaultDetail.revealBtn=Дискты күрһәт
|
||||
main.vaultDetail.copyUri=URI күсермәһен ал
|
||||
main.vaultDetail.lockBtn=Биклә
|
||||
main.vaultDetail.bytesPerSecondRead=Уҡыу:
|
||||
main.vaultDetail.bytesPerSecondWritten=Яҙыу:
|
||||
main.vaultDetail.throughput.idle=буш
|
||||
main.vaultDetail.throughput.kbps=%.1f КиБ/с
|
||||
main.vaultDetail.throughput.mbps=%.1f МиБ/с
|
||||
main.vaultDetail.stats=Һаҡлағыс статистикаһы
|
||||
main.vaultDetail.locateEncryptedFileBtn=Шифрланған файлды тап
|
||||
main.vaultDetail.locateEncryptedFileBtn.tooltip=Шифрланған аналогын табыр өсөн һаҡлағыстан файл һайлағыҙ
|
||||
main.vaultDetail.encryptedPathsCopied=Юлдарҙын Clipboard-ҡа күсермәһе алынды!
|
||||
main.vaultDetail.filePickerTitle=Һаҡлағыс эсендә файл һайлау
|
||||
### Missing
|
||||
main.vaultDetail.missing.info=Cryptomator был юлдан һаҡлағыс таба алманы.
|
||||
main.vaultDetail.missing.recheck=Яңынан тикшер
|
||||
main.vaultDetail.missing.remove=Һаҡлағыс исемлегенән алып ташла…
|
||||
main.vaultDetail.missing.changeLocation=Һаҡлағыс урынын үҙгәрт…
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=Һаҡлағысты яңыртыу
|
||||
main.vaultDetail.migratePrompt=Һаҡлағысҡа инер алдынан уны яңы форматҡа тиклем яңыртыу кәрәк
|
||||
### Error
|
||||
main.vaultDetail.error.info=Һаҡлағысты дисктан тейәүҙә хата килеп сыҡты.
|
||||
main.vaultDetail.error.reload=Яңынан тейә
|
||||
main.vaultDetail.error.windowTitle=Һаҡлағыс тейәүҙә хата
|
||||
|
||||
# Wrong File Alert
|
||||
wrongFileAlert.title=Файлдарҙы нисек шифрларға
|
||||
wrongFileAlert.message=Был файлдарҙы шифрларға тырыштығыҙмы?
|
||||
wrongFileAlert.description=Бының өсөн Cryptomator һеҙҙең система файл менеджерында том яһай.
|
||||
wrongFileAlert.instruction.0=Файлдарҙы шифрлау өсөн ошо аҙымдарҙы үтәгеҙ:
|
||||
wrongFileAlert.instruction.1=1. Һаҡлағысығыҙҙы асығыҙ.
|
||||
wrongFileAlert.instruction.2=2. Файл менеджерығыҙҙа томды асыу өсөн «Күрһәт» төймәһенә баҫығыҙ.
|
||||
wrongFileAlert.instruction.3=3. Файлдарығыҙҙы ошо томға өҫтәгеҙ.
|
||||
wrongFileAlert.link=Өҫтәмә ярҙам өсөн, ҡарағыҙ:
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general=Дөйөм
|
||||
vaultOptions.general.vaultName=Һаҡлағыс исеме
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=Бикләү өсөн буш тороу ваҡыты
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=минут
|
||||
vaultOptions.general.unlockAfterStartup=Cryptomator асылғанда һаҡлағыстың биген асырға
|
||||
vaultOptions.general.actionAfterUnlock=Бик сиселгәндән һуң
|
||||
vaultOptions.general.actionAfterUnlock.ignore=Бер ни ҙә эшләмәҫкә
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Дискты күрһәт
|
||||
vaultOptions.general.actionAfterUnlock.ask=Һорарға
|
||||
vaultOptions.general.startHealthCheckBtn=Хәл тикшереүен башлатырға
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Тоташтырыу
|
||||
vaultOptions.mount.info=Ғәҙәттәге көйләүҙәрҙе үҙгәртеү өсөн виртуаль диск көйләүҙәрен асығыҙ.
|
||||
vaultOptions.mount.readonly=Уҡыу ғына
|
||||
vaultOptions.mount.customMountFlags=Махсус тоташтырыу флагтары
|
||||
vaultOptions.mount.winDriveLetterOccupied=биләнгән
|
||||
vaultOptions.mount.mountPoint=Тоташтырыу нөктәһе
|
||||
vaultOptions.mount.mountPoint.auto=Автоматик рәүештә уңай урын һайларға
|
||||
vaultOptions.mount.mountPoint.driveLetter=Тәғәйенләнгән диск хәрефен ҡулланырға
|
||||
vaultOptions.mount.mountPoint.custom=Һайланған каталог ҡулланырға
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Һайла…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Каталог һайлау
|
||||
vaultOptions.mount.volumeType.default=Ғәҙәттәгеләр (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Был том төрөн ҡулланыу өсөн Cryptomator-ҙы яңынан асыу кәрәк.
|
||||
vaultOptions.mount.volume.tcp.port=TCP порты
|
||||
vaultOptions.mount.volume.type=Том төрө
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Серһүҙ
|
||||
vaultOptions.masterkey.changePasswordBtn=Серһүҙҙе үҙгәртеү
|
||||
vaultOptions.masterkey.forgetSavedPasswordBtn=Һаҡланған серһүҙҙе онот
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=Серһүҙ юғалғанда һаҡлағысҡа инеү мөмкинлеген ҡайтарыу асҡысы менән генә тергеҙергә мөмкин.
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Ҡайтарыу асҡысын күрһәт
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Серһүҙҙе яңырт
|
||||
## Hub
|
||||
vaultOptions.hub=Ҡайтарыу
|
||||
vaultOptions.hub.convertInfo=Ашығыс осраҡта, был хаб һаҡлағысын серһүҙ нигеҙле һаҡлағысҡа әйләндереү өсөн тергеҙеү асҡысын ҡуллана алаһығыҙ.
|
||||
vaultOptions.hub.convertBtn=Серһүҙ нигеҙле һаҡлағысҡа күс
|
||||
|
||||
# Recovery Key
|
||||
## Display Recovery Key
|
||||
recoveryKey.display.title=Ҡайтарыу асҡысын ҡарау
|
||||
recoveryKey.create.message=Серһүҙ кәрәкле
|
||||
recoveryKey.create.description=Ҡайтарыу асҡысын күрһәтеү өсөн "%s" серһүҙен индерегеҙ.
|
||||
recoveryKey.display.description="%s" һаҡлағысына инеү мөмкинлеген тергеҙеү өсөн түбәндәге ҡайтарыу асҡысын ҡулланырға була:
|
||||
recoveryKey.display.StorageHints=Быны ышаныслы урында һаҡлағыҙ, мәҫәлән:\n • Серһүҙ менеджеры ярҙамында һаҡлағыҙ\n • USB флеш-дискҡа һаҡлағыҙ\n • Ҡағыҙҙа баҫтырығыҙ
|
||||
## Reset Password
|
||||
### Enter Recovery Key
|
||||
recoveryKey.recover.title=Серһүҙҙе яңырт
|
||||
recoveryKey.recover.prompt="%s" өсөн ҡайтарыу асҡысын яҙығыҙ:
|
||||
recoveryKey.recover.correctKey=Был ҡайтарыу асҡысы дөрөҫ
|
||||
recoveryKey.recover.wrongKey=Был ҡайтарыу асҡысы башҡа һаҡлағысҡа ҡарай
|
||||
recoveryKey.recover.invalidKey=Был ҡайтарыу асҡысы дөрөҫ түгел
|
||||
recoveryKey.printout.heading=Cryptomator ҡайтарыу асҡысы\n"%s"\n
|
||||
### Reset Password
|
||||
recoveryKey.recover.resetBtn=Яңынан башла
|
||||
### Recovery Key Password Reset Success
|
||||
recoveryKey.recover.resetSuccess.message=Серһүҙ яңыртыу уңышлы тамамланды
|
||||
recoveryKey.recover.resetSuccess.description=Яңы серһүҙ менән һаҡлағысты аса алаһығыҙ.
|
||||
|
||||
# Convert Vault
|
||||
convertVault.title=Һаҡлағысты үҙгәртеү
|
||||
convertVault.convert.convertBtn.before=Үҙгәрт
|
||||
convertVault.convert.convertBtn.processing=Үҙгәртеү…
|
||||
convertVault.success.message=Үҙгәртеү уңышлы тамамланды
|
||||
convertVault.hubToPassword.success.description=Хәҙер һеҙ һайланған серһүҙ менән, хабҡа инмәйенсә, һаҡлағысты аса алаһығыҙ.
|
||||
|
||||
# New Password
|
||||
newPassword.promptText=Яңы серһүҙ яҙығыҙ
|
||||
newPassword.reenterPassword=Яңы серһүҙҙе раҫлағыҙ
|
||||
newPassword.passwordsMatch=Серһүҙҙәр тап килә!
|
||||
|
||||
# Quit
|
||||
quit.title=Ҡушымтанан сығыу
|
||||
quit.description=Сығырға теләүегеҙҙе раҫлағыҙ. Cryptomator мәғлүмәтте юғалтыуҙан һаҡлар өсөн бөтә һаҡлағыстарҙы бикләп ҡуя.
|
||||
quit.lockAndQuitBtn=Биклә һәм сыҡ
|
||||
|
||||
# Forced Quit
|
||||
quit.forced.description=Көтөп торған ғәмәлдәр йәки асыҡ файлдар һаҡлағыстарҙы бикләүҙе тотҡарланы. Был һаҡлағыстарҙы көсләп бикләп була, ләкин инеү-сығыуҙы өҙөү һаҡланмаған мәғлүмәттәрҙе юғалтыуға килтереүе ихтимал.
|
||||
|
||||
# Update Reminder
|
||||
updateReminder.description=Яңы үҙенсәлектәр, хаталарҙы төҙәтеү, хәүефһеҙлекте яҡшыртыу менән яңыртылып торорға. Яңыртыуҙарҙы автоматик рәүештә тикшереүҙе кәңәш итәбеҙ.
|
||||
updateReminder.notNow=Хәҙер түгел
|
||||
updateReminder.yesOnce=Эйе, бер тапҡыр
|
||||
updateReminder.yesAutomatically=Эйе, автоматик рәүештә
|
||||
@@ -284,11 +284,9 @@ preferences.interface.showMinimizeButton=Паказаць кнопку згор
|
||||
preferences.interface.showTrayIcon=Паказваць іконку на інфармацыйнай панэлі (спатрэбіцца перазапуск)
|
||||
## Volume
|
||||
preferences.volume=Віртуальны дыск
|
||||
preferences.volume.type=Тып тому
|
||||
preferences.volume.type.automatic=Аўтаматычна
|
||||
preferences.volume.docsTooltip=Адчыні дакумэнтацыю, каб даведацца больш пра розныя тыпы тому.
|
||||
preferences.volume.fuseRestartRequired=Каб дастасаваць зьмены, Cryptomator патрэбна перазапусьціць.
|
||||
preferences.volume.tcp.port=Порт TCP
|
||||
preferences.volume.supportedFeatures=Абраны тып тому падтрымлівае наступныя функцыі:
|
||||
preferences.volume.feature.mountAuto=Аўтаматычны выбар пункту мантажавання
|
||||
preferences.volume.feature.mountToDir=Карыстальніцкая тэчка як пункт мантажавання
|
||||
@@ -427,8 +425,6 @@ vaultOptions.general.startHealthCheckBtn=Пачаць тэст на цэласн
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Мантажаванне
|
||||
vaultOptions.mount.info=Опцыі залежаць ад абранага тыпу тому.
|
||||
vaultOptions.mount.linkToPreferences=Адчыніць налады віртуальнага дыску
|
||||
vaultOptions.mount.readonly=Толькі для чытання
|
||||
vaultOptions.mount.customMountFlags=Карыстальніцкія опцыі мантажавання
|
||||
vaultOptions.mount.winDriveLetterOccupied=занята
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Временният, скр
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
|
||||
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
|
||||
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Хранилището не може да бъде отключено
|
||||
unlock.error.fuseRestartRequired.description=Променете вида на тома в настройките на хранилището или рестартирайте Криптоматор.
|
||||
unlock.error.title=Неуспешно отключване на „%s“
|
||||
## Hub
|
||||
hub.noKeychain.message=Няма достъп до ключа на устройството
|
||||
hub.noKeychain.description=За да отключите хранилищата в Hub е необходим ключ за устройството, който се защитава с помощта на ключодържател. За да продължите, разрешете „%s“ и изберете ключодържателя в настройките.
|
||||
@@ -169,8 +172,8 @@ hub.registerFailed.description=В процеса на именуване е до
|
||||
hub.unauthorized.message=Отказан достъп
|
||||
hub.unauthorized.description=Устройството не е упълномощено за достъп до това хранилище. Поискайте достъп от собственика.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Необходимо действие
|
||||
hub.requireAccountInit.description.0=За да продължите завършене необходимите стъпки в
|
||||
hub.requireAccountInit.message=Необходимо е действие
|
||||
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
|
||||
hub.requireAccountInit.description.1=профила в Hub
|
||||
hub.requireAccountInit.description.2=.
|
||||
### License Exceeded
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Бутон за скриване
|
||||
preferences.interface.showTrayIcon=Икона в областта за известия (изисква рестарт)
|
||||
## Volume
|
||||
preferences.volume=Виртуален диск
|
||||
preferences.volume.type=Вид на тома
|
||||
preferences.volume.type=Подразбиран вид на тома
|
||||
preferences.volume.type.automatic=Автоматично
|
||||
preferences.volume.docsTooltip=Вижте документацията относно видовете томове.
|
||||
preferences.volume.fuseRestartRequired=За да бъдат приложени промените, Криптоматор трябва да бъде рестартиран.
|
||||
preferences.volume.tcp.port=Порт на TCP
|
||||
preferences.volume.tcp.port=Подразбиран порт на TCP
|
||||
preferences.volume.supportedFeatures=Избрания вид на тома има следните възможности:
|
||||
preferences.volume.feature.mountAuto=Автоматичен избор на точка за монтиране
|
||||
preferences.volume.feature.mountToDir=Папка по избор като точка за монтиране
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Проверка на състояни
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Монтиране
|
||||
vaultOptions.mount.info=Възможностите зависят от вида на избрания том.
|
||||
vaultOptions.mount.linkToPreferences=Настройки на виртуалния диск
|
||||
vaultOptions.mount.info=Отворете настройките на виртуалния диск, за да промените стандартните настройки.
|
||||
vaultOptions.mount.readonly=Само за четене
|
||||
vaultOptions.mount.customMountFlags=Флагове на монтиране
|
||||
vaultOptions.mount.winDriveLetterOccupied=използвана
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Използване на опреде
|
||||
vaultOptions.mount.mountPoint.custom=Използване на избрана папка
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Избиране…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Избиране на папка
|
||||
vaultOptions.mount.volumeType.default=По подразбиране (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=За да използвате този вид томове, трябва да рестартирате Криптоматор.
|
||||
vaultOptions.mount.volume.tcp.port=Порт на TCP
|
||||
vaultOptions.mount.volume.type=Вид на тома
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Парола
|
||||
vaultOptions.masterkey.changePasswordBtn=Промяна на парола
|
||||
|
||||
@@ -288,11 +288,9 @@ preferences.interface.showMinimizeButton=Mostra el botó 'minimitzar'
|
||||
preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
|
||||
## Volume
|
||||
preferences.volume=Unitat virtual
|
||||
preferences.volume.type=Tipus de volum
|
||||
preferences.volume.type.automatic=Automàtic
|
||||
preferences.volume.docsTooltip=Obre la documentació per aprendre més sobre els diferents tipus de volums.
|
||||
preferences.volume.fuseRestartRequired=Per aplicar els canvis Cryptomator necessita reiniciar-se.
|
||||
preferences.volume.tcp.port=Port TCP
|
||||
preferences.volume.supportedFeatures=El tipus de volum escollit suporta les següents característiques:
|
||||
preferences.volume.feature.mountAuto=Selecció automàtica del punt de muntatge
|
||||
preferences.volume.feature.mountToDir=Directori personalitzat com a punt de muntatge
|
||||
@@ -431,8 +429,6 @@ vaultOptions.general.startHealthCheckBtn=Inicia la comprovació
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Muntatge
|
||||
vaultOptions.mount.info=Les opcions depenen del tipus de volum escollit.
|
||||
vaultOptions.mount.linkToPreferences=Obre les preferències del disc virtual
|
||||
vaultOptions.mount.readonly=Només lectura
|
||||
vaultOptions.mount.customMountFlags=Senyaladors de muntatge personalitzats
|
||||
vaultOptions.mount.winDriveLetterOccupied=ocupat
|
||||
|
||||
@@ -269,7 +269,6 @@ preferences.interface.showTrayIcon=Zobrazit ikonu v liště (vyžaduje restart)
|
||||
## Volume
|
||||
preferences.volume=Virtuální jednotky
|
||||
preferences.volume.type.automatic=Automatické
|
||||
preferences.volume.tcp.port=TCP port
|
||||
preferences.volume.feature.mountFlags=Vlastní možnosti připojení disku
|
||||
preferences.volume.feature.readOnly=Připojení disku pouze pro čtení
|
||||
## Updates
|
||||
|
||||
@@ -288,11 +288,9 @@ preferences.interface.showMinimizeButton=Vis knap til minimering
|
||||
preferences.interface.showTrayIcon=Vis ikon i system-bakken (kræver genstart)
|
||||
## Volume
|
||||
preferences.volume=Virtuelt drev
|
||||
preferences.volume.type=Drev Type
|
||||
preferences.volume.type.automatic=Automatisk
|
||||
preferences.volume.docsTooltip=Åbn dokumentationen for at lære mere om de forskellige typer drev.
|
||||
preferences.volume.fuseRestartRequired=For at anvende ændringerne skal Cryptomator genstartes.
|
||||
preferences.volume.tcp.port=TCP port
|
||||
preferences.volume.supportedFeatures=Den valgte type drev understøtter følgende funktioner:
|
||||
preferences.volume.feature.mountAuto=Automatisk valg af monteringspunkt
|
||||
preferences.volume.feature.mountToDir=Brugerdefineret mappe som monteringspunkt
|
||||
@@ -431,8 +429,6 @@ vaultOptions.general.startHealthCheckBtn=Start sunhedstjek
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montering
|
||||
vaultOptions.mount.info=Valgmulighederne afhænger af den valgte type drev.
|
||||
vaultOptions.mount.linkToPreferences=Åbn virtuelle drev præferencer
|
||||
vaultOptions.mount.readonly=Skrivebeskyttet
|
||||
vaultOptions.mount.customMountFlags=Brugerdefinerede monterings-flag
|
||||
vaultOptions.mount.winDriveLetterOccupied=optaget
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Die temporäre, versteckte Da
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Dein Tresor konnte nicht in den Pfad „%s“ eingehängt werden. Bitte versuche es erneut oder wähle einen anderen Pfad aus.
|
||||
unlock.error.customPath.description.notEmptyDir=Der benutzerdefinierte Einhängepunkt „%s“ ist kein leerer Ordner. Bitte wähle einen leeren Ordner und versuche es erneut.
|
||||
unlock.error.customPath.description.generic=Du hast für diesen Tresor einen benutzerdefinierten Einhängepunkt ausgewählt, aber dessen Verwendung ist mit folgender Meldung fehlgeschlagen: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Tresor konnte nicht entsperrt werden
|
||||
unlock.error.fuseRestartRequired.description=Ändere den Laufwerkstyp in den Tresoroptionen oder starte Cryptomator neu.
|
||||
unlock.error.title=„%s“ konnte nicht entsperrt werden
|
||||
## Hub
|
||||
hub.noKeychain.message=Zugriff auf Geräteschlüssel nicht möglich
|
||||
hub.noKeychain.description=Zum Entsperren von Hub-Tresoren wird ein Geräteschlüssel benötigt, der in einem Schlüsselbund gesichert ist. Um fortzufahren, aktiviere „%s“ und wähle in den Einstellungen einen Schlüsselbund.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Schaltfläche zum Minimieren anzeigen
|
||||
preferences.interface.showTrayIcon=Symbol im Infobereich anzeigen (Neustart erforderlich)
|
||||
## Volume
|
||||
preferences.volume=Virtuelles Laufwerk
|
||||
preferences.volume.type=Laufwerkstyp
|
||||
preferences.volume.type=Standard-Laufwerkstyp
|
||||
preferences.volume.type.automatic=Automatisch
|
||||
preferences.volume.docsTooltip=Öffne die Dokumentation, um mehr über die verschiedenen Laufwerkstypen zu erfahren.
|
||||
preferences.volume.fuseRestartRequired=Um die Änderungen anzuwenden, muss Cryptomator neu gestartet werden.
|
||||
preferences.volume.tcp.port=TCP-Port
|
||||
preferences.volume.tcp.port=Standard-TCP-Port
|
||||
preferences.volume.supportedFeatures=Der gewählte Laufwerkstyp unterstützt folgende Funktionen:
|
||||
preferences.volume.feature.mountAuto=Automatische Auswahl des Einhängepunkts
|
||||
preferences.volume.feature.mountToDir=Benutzerdefiniertes Verzeichnis als Einhängepunkt
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Integritätsprüfung starten
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Laufwerk
|
||||
vaultOptions.mount.info=Die Optionen hängen vom gewählten Laufwerkstyp ab.
|
||||
vaultOptions.mount.linkToPreferences=Einstellungen für virtuelles Laufwerk öffnen
|
||||
vaultOptions.mount.info=Öffne die Einstellungen des virtuellen Laufwerks, um die Standardeinstellungen zu ändern.
|
||||
vaultOptions.mount.readonly=Schreibgeschützt
|
||||
vaultOptions.mount.customMountFlags=Benutzerdefinierte Einhänge-Optionen
|
||||
vaultOptions.mount.winDriveLetterOccupied=belegt
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Laufwerksbuchstaben zuweisen
|
||||
vaultOptions.mount.mountPoint.custom=Gewähltes Verzeichnis verwenden
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Durchsuchen …
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Wähle ein Verzeichnis
|
||||
vaultOptions.mount.volumeType.default=Standard (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Um diesen Laufwerkstyp verwenden zu können, muss Cryptomator neu gestartet werden.
|
||||
vaultOptions.mount.volume.tcp.port=TCP-Port
|
||||
vaultOptions.mount.volume.type=Laufwerkstyp
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Passwort
|
||||
vaultOptions.masterkey.changePasswordBtn=Passwort ändern
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Το προσωρινό, κρ
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Η κρύπτη σας δεν μπορεί να τοποθετηθεί στη διαδρομή "%s". Παρακαλώ δοκιμάστε ξανά ή επιλέξτε διαφορετική διαδρομή.
|
||||
unlock.error.customPath.description.notEmptyDir=Η προσαρμοσμένη διαδρομή προσάρτησης "%s" δεν είναι ένας άδειος φάκελος. Παρακαλώ επιλέξτε έναν άδειο φάκελο και προσπαθήστε ξανά.
|
||||
unlock.error.customPath.description.generic=Έχετε επιλέξει μια προσαρμοσμένη διαδρομή προσάρτησης για αυτή την κρύπτη, αλλά η χρήση της απέτυχε με το μήνυμα: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Αδυναμία ξεκλειδώματος κρύπτης
|
||||
unlock.error.fuseRestartRequired.description=Αλλάξτε τον τύπο τόμου στις επιλογές κρύπτης ή επανεκκινήστε το Cryptomator.
|
||||
unlock.error.title=Ξεκλείδωμα "%s" απέτυχε
|
||||
## Hub
|
||||
hub.noKeychain.message=Δεν είναι δυνατή η πρόσβαση στο κλειδί της συσκευής
|
||||
hub.noKeychain.description=Για να ξεκλειδώσετε τις κρύπτες Hub, απαιτείται ένα κλειδί συσκευής, το οποίο ασφαλίζεται με χρήση μπρελόκ. Για να συνεχίσετε, ενεργοποιήστε το "%s" και επιλέξτε ένα keychain στις προτιμήσεις.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Εμφάνιση κουμπιού ελ
|
||||
preferences.interface.showTrayIcon=Εμφάνιση εικονιδίου tray (απαιτεί επανεκκίνηση)
|
||||
## Volume
|
||||
preferences.volume=Εικονικός δίσκος
|
||||
preferences.volume.type=Τύπος Τόμου
|
||||
preferences.volume.type=Προεπιλεγμένος Τύπος Τόμου
|
||||
preferences.volume.type.automatic=Αυτόματα
|
||||
preferences.volume.docsTooltip=Ανοίξτε τις οδηγίες για να μάθετε περισσότερα σχετικά με τους διαφορετικούς τύπους τόμων.
|
||||
preferences.volume.fuseRestartRequired=Για να εφαρμοστούν οι αλλαγές, πρέπει να γίνει επανεκκίνηση του Cryptomator.
|
||||
preferences.volume.tcp.port=Θύρα TCP
|
||||
preferences.volume.tcp.port=Προεπιλεγμένη Θύρα TCP
|
||||
preferences.volume.supportedFeatures=Ο επιλεγμένος τύπος τόμου υποστηρίζει τις ακόλουθες δυνατότητες:
|
||||
preferences.volume.feature.mountAuto=Αυτόματη επιλογή σημείου προσάρτησης
|
||||
preferences.volume.feature.mountToDir=Προσαρμοσμένος κατάλογος ως σημείο προσάρτησης
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Έναρξη ελέγχου Υγεία
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Προσάρτηση
|
||||
vaultOptions.mount.info=Οι επιλογές εξαρτώνται από τον επιλεγμένο τύπο τόμου.
|
||||
vaultOptions.mount.linkToPreferences=Άνοιγμα προτιμήσεων εικονικής μονάδας δίσκου
|
||||
vaultOptions.mount.info=Ανοίξτε τις προτιμήσεις εικονικής μονάδας δίσκου για να αλλάξετε τις προεπιλεγμένες ρυθμίσεις.
|
||||
vaultOptions.mount.readonly=Μόνο για ανάγνωση
|
||||
vaultOptions.mount.customMountFlags=Προσαρμοσμένες ετικέτες προσάρτησης
|
||||
vaultOptions.mount.winDriveLetterOccupied=κατειλημμένο
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Χρήση επιλεγμένου γρ
|
||||
vaultOptions.mount.mountPoint.custom=Χρήση επιλεγμένου καταλόγου
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Επιλογή…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Επιλέξτε έναν κατάλογο
|
||||
vaultOptions.mount.volumeType.default=Προεπιλογή (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Για να χρησιμοποιήσετε αυτόν τον τύπο τόμου, πρέπει να γίνει επανεκκίνηση του Cryptomator.
|
||||
vaultOptions.mount.volume.tcp.port=Θύρα TCP
|
||||
vaultOptions.mount.volume.type=Τύπος Τόμου
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Κωδικός πρόσβασης
|
||||
vaultOptions.masterkey.changePasswordBtn=Αλλαγή κωδικού πρόσβασης
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=El archivo oculto temporal "%
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Su bóveda no se pudo montar en la ruta "%s". Intente de nuevo o elija una ruta diferente.
|
||||
unlock.error.customPath.description.notEmptyDir=La ruta de montaje personalizada "%s" no es una carpeta vacía. Elija una carpeta vacía y vuelva a intentarlo.
|
||||
unlock.error.customPath.description.generic=Seleccionó una ruta de montaje personalizada para esta bóveda, pero falló al usarla con el mensaje: %2$s
|
||||
unlock.error.fuseRestartRequired.message=No se puede desbloquear la bóveda
|
||||
unlock.error.fuseRestartRequired.description=Cambie el tipo de volumen en las opciones de la bóveda o reinicie Cryptomator.
|
||||
unlock.error.title=Error al desbloquear "%s"
|
||||
## Hub
|
||||
hub.noKeychain.message=No se puede acceder a la clave del dispositivo
|
||||
hub.noKeychain.description=Para desbloquear las bóvedas de Hub, se requiere una clave de dispositivo que se asegura con un llavero. Para continuar, habilite "%s" y seleccione un llavero en las preferencias.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Mostrar botón minimizar
|
||||
preferences.interface.showTrayIcon=Mostrar ícono de bandeja (requiere reiniciar)
|
||||
## Volume
|
||||
preferences.volume=Unidad virtual
|
||||
preferences.volume.type=Tipo de volumen
|
||||
preferences.volume.type=Tipo de volumen predeterminado
|
||||
preferences.volume.type.automatic=Automático
|
||||
preferences.volume.docsTooltip=Abra la documentación para saber más sobre los diferentes tipos de volumen.
|
||||
preferences.volume.fuseRestartRequired=Para aplicar los cambios, Cryptomator necesita ser reiniciado.
|
||||
preferences.volume.tcp.port=Puerto TCP
|
||||
preferences.volume.tcp.port=Puerto TCP predeterminado
|
||||
preferences.volume.supportedFeatures=El tipo de volumen elegido admite las siguientes funciones:
|
||||
preferences.volume.feature.mountAuto=Selección automática del punto de montaje
|
||||
preferences.volume.feature.mountToDir=Directorio personalizado como punto de montaje
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Iniciar comprobación de estado
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montaje
|
||||
vaultOptions.mount.info=Las opciones dependen del tipo de volumen seleccionado.
|
||||
vaultOptions.mount.linkToPreferences=Abrir preferencias de unidad virtual
|
||||
vaultOptions.mount.info=Abra las preferencias de unidad virtual para cambiar la configuración predeterminada.
|
||||
vaultOptions.mount.readonly=Sólo lectura
|
||||
vaultOptions.mount.customMountFlags=Opciones de montaje personalizadas
|
||||
vaultOptions.mount.winDriveLetterOccupied=ocupado
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Usar letra de unidad asignada
|
||||
vaultOptions.mount.mountPoint.custom=Usar directorio seleccionado
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Elegir…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Seleccionar un directorio
|
||||
vaultOptions.mount.volumeType.default=Predeterminado (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Para utilizar este tipo de volumen, debe reiniciarse Criptomator.
|
||||
vaultOptions.mount.volume.tcp.port=Puerto TCP
|
||||
vaultOptions.mount.volume.type=Tipo de volumen
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Contraseña
|
||||
vaultOptions.masterkey.changePasswordBtn=Cambiar contraseña
|
||||
|
||||
@@ -294,11 +294,9 @@ preferences.interface.showMinimizeButton=Ipakita ang pindutan ng minimize
|
||||
preferences.interface.showTrayIcon=Ipakita ang icon ng tray (kailangan i-restart)
|
||||
## Volume
|
||||
preferences.volume=Virtual Drive
|
||||
preferences.volume.type=Uri ng Dami
|
||||
preferences.volume.type.automatic=Awtomatiko
|
||||
preferences.volume.docsTooltip=Buksan ang dokumentasyon para matuto pa tungkol sa iba't ibang uri ng volume.
|
||||
preferences.volume.fuseRestartRequired=Upang mailapat ang mga pagbabago, kailangang i-restart ang Cryptomator.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.supportedFeatures=Sinusuportahan ng napiling uri ng volume ang mga sumusunod na tampok:
|
||||
preferences.volume.feature.mountAuto=Awtomatikong pagpili ng mount point
|
||||
preferences.volume.feature.mountToDir=Custom na direktoryo bilang mount point
|
||||
@@ -437,8 +435,6 @@ vaultOptions.general.startHealthCheckBtn=Simulan ang Health Check
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Pag-mount
|
||||
vaultOptions.mount.info=Ang mga opsyon ay depende sa napiling uri ng volume.
|
||||
vaultOptions.mount.linkToPreferences=Buksan ang mga kagustuhan sa virtual drive
|
||||
vaultOptions.mount.readonly=Basahin lamang
|
||||
vaultOptions.mount.customMountFlags=Mga custom na naka-mount na flag
|
||||
vaultOptions.mount.winDriveLetterOccupied=inookupahan
|
||||
|
||||
@@ -32,7 +32,7 @@ defaults.vault.vaultName=Coffre
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=Montrer
|
||||
traymenu.showPreferencesWindow=Préférences
|
||||
traymenu.lockAllVaults=Tout Verrouiller
|
||||
traymenu.lockAllVaults=Tout verrouiller
|
||||
traymenu.quitApplication=Quitter
|
||||
traymenu.vault.unlock=Déverrouiller
|
||||
traymenu.vault.lock=Verrouiller
|
||||
@@ -78,7 +78,7 @@ addvault.new.readme.storageLocation.fileName=IMPORTANT.rtf
|
||||
addvault.new.readme.storageLocation.1=Fichiers de coffre-fort
|
||||
addvault.new.readme.storageLocation.2=Ceci est l'emplacement de stockage de votre coffre.
|
||||
addvault.new.readme.storageLocation.3=NE PAS
|
||||
addvault.new.readme.storageLocation.4=• Modifier n'importe quel fichier dans ce répertoire ou
|
||||
addvault.new.readme.storageLocation.4=• modifier les fichiers dans ce répertoire ni
|
||||
addvault.new.readme.storageLocation.5=• coller de fichier à chiffrer dans ce répertoire.
|
||||
addvault.new.readme.storageLocation.6=Si vous voulez chiffrer les fichiers et afficher le contenu du coffre, faites ce qui suit :
|
||||
addvault.new.readme.storageLocation.7=1. Ajouter ce coffre à Cryptomator.
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Le fichier temporaire et cach
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Votre coffre n'a pas pu être monté au point "%s". Veuillez réessayer ou choisissez un autre point.
|
||||
unlock.error.customPath.description.notEmptyDir=Le chemin de montage personnalisé "%s" n'est pas un dossier vide. Veuillez choisir un dossier vide et réessayez.
|
||||
unlock.error.customPath.description.generic=Vous avez sélectionné un point de montage personnalisé pour ce coffre, mais son utilisation a échoué avec le message : %2$s
|
||||
unlock.error.fuseRestartRequired.message=Impossible de déverrouiller le coffre
|
||||
unlock.error.fuseRestartRequired.description=Changez le type de volume dans les options du coffre ou redémarrez Cryptomator.
|
||||
unlock.error.title=Échec du déverrouillage de "%s"
|
||||
## Hub
|
||||
hub.noKeychain.message=Impossible d'accéder à la clé du périphérique
|
||||
hub.noKeychain.description=Le déverrouillage des coffres Hub nécessite une clé de périphérique sécurisée à l'aide d'un trousseau. Pour continuer, activez « %s » et sélectionnez un trousseau dans les préférences.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Afficher le bouton Réduire
|
||||
preferences.interface.showTrayIcon=Montrer l'icône de service (redémarrage nécessaire)
|
||||
## Volume
|
||||
preferences.volume=Disque virtuel
|
||||
preferences.volume.type=Type de volume
|
||||
preferences.volume.type=Type de volume par défaut
|
||||
preferences.volume.type.automatic=Automatique
|
||||
preferences.volume.docsTooltip=Consultez la documentation pour en savoir plus sur les différents types de volumes.
|
||||
preferences.volume.fuseRestartRequired=Pour appliquer les modifications, Cryptomator doit être redémarré.
|
||||
preferences.volume.tcp.port=Port TCP
|
||||
preferences.volume.tcp.port=Port TCP par défaut
|
||||
preferences.volume.supportedFeatures=Le type de volume choisi prend en charge les fonctionnalités suivantes :
|
||||
preferences.volume.feature.mountAuto=Sélection automatique du point de montage
|
||||
preferences.volume.feature.mountToDir=Répertoire personnalisé comme point de montage
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Commencer la vérification de l'état
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montage
|
||||
vaultOptions.mount.info=Les options dépendent du type de volume sélectionné.
|
||||
vaultOptions.mount.linkToPreferences=Ouvrir les préférences du lecteur virtuel
|
||||
vaultOptions.mount.info=Ouvrez les préférences du lecteur virtuel pour changer les paramètres par défaut.
|
||||
vaultOptions.mount.readonly=Lecture seule
|
||||
vaultOptions.mount.customMountFlags=Paramètres personnalisés de montage
|
||||
vaultOptions.mount.winDriveLetterOccupied=occupé
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Utiliser la lettre du lecteur assigné
|
||||
vaultOptions.mount.mountPoint.custom=Utiliser le répertoire choisi
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choisir...
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Choisissez un répertoire
|
||||
vaultOptions.mount.volumeType.default=Par défaut (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Pour utiliser ce type de volume, Cryptomator doit être redémarré.
|
||||
vaultOptions.mount.volume.tcp.port=Port TCP
|
||||
vaultOptions.mount.volume.type=Type de volume
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Mot de passe
|
||||
vaultOptions.masterkey.changePasswordBtn=Modifier le mot de passe
|
||||
|
||||
@@ -286,11 +286,9 @@ preferences.interface.showMinimizeButton=הצג כפתור מזעור
|
||||
preferences.interface.showTrayIcon=הצג צלמית בשורה מטה (דורש הפעלה מחדש)
|
||||
## Volume
|
||||
preferences.volume=כונן וירטואלי
|
||||
preferences.volume.type=סוג נפח
|
||||
preferences.volume.type.automatic=אוטומטי
|
||||
preferences.volume.docsTooltip=בכדי ללמוד עוד על סוגי volume ניתן לקרוא את הדוקומנטציה.
|
||||
preferences.volume.fuseRestartRequired=בכדי להחיל את השינויים נדרשת הפעלה מחדש.
|
||||
preferences.volume.tcp.port=פורט TCP
|
||||
preferences.volume.supportedFeatures=סוג ה- volume שבחרת תומך ביכולות הבאות:
|
||||
preferences.volume.feature.mountAuto=נבחרה בחירת נקודת קישור אוטומטית
|
||||
preferences.volume.feature.mountToDir=הגדרת תיקיה כנקודת קישור
|
||||
@@ -428,8 +426,6 @@ vaultOptions.general.startHealthCheckBtn=התחל בדיקת בריאות
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=קישור
|
||||
vaultOptions.mount.info=האפשרויות תלויות בסוג ה- volume הנבחר.
|
||||
vaultOptions.mount.linkToPreferences=פתח את העדפות הכונן הוירטואלי
|
||||
vaultOptions.mount.readonly=קריאה בלבד
|
||||
vaultOptions.mount.customMountFlags=דגלים ידנים לקישור
|
||||
vaultOptions.mount.winDriveLetterOccupied=בשימוש
|
||||
|
||||
@@ -292,11 +292,9 @@ preferences.interface.showMinimizeButton=Kicsinyítés ikon megjelenítése
|
||||
preferences.interface.showTrayIcon=Tálca ikon megjelenítése (újraindítás szükséges)
|
||||
## Volume
|
||||
preferences.volume=Virtuális meghajtó
|
||||
preferences.volume.type=Kötet Típusa
|
||||
preferences.volume.type.automatic=Automatikus
|
||||
preferences.volume.docsTooltip=További információért a kötet típusokról kattintson ide, hogy megnyissa a dokumentációt.
|
||||
preferences.volume.fuseRestartRequired=A változtatások alkalmazásához újra kell indítania a Cryptomatort.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.supportedFeatures=A kiválaszott kötet típus az alábbi funkciókat támogatja:
|
||||
preferences.volume.feature.mountAuto=Automatikus csatlakozási pont választás
|
||||
preferences.volume.feature.mountToDir=Egyéni mappa csatlakozási pontként
|
||||
@@ -435,8 +433,6 @@ vaultOptions.general.startHealthCheckBtn=Épség-ellenőrzés indítása
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Felcsatolás
|
||||
vaultOptions.mount.info=Az opciók a kiválasztott kötet típustól függőek.
|
||||
vaultOptions.mount.linkToPreferences=Virtuális meghajtó opciók megnyitása
|
||||
vaultOptions.mount.readonly=Csak-olvasható
|
||||
vaultOptions.mount.customMountFlags=Egyedi csatolási paraméterek
|
||||
vaultOptions.mount.winDriveLetterOccupied=foglalt
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Impossibile rimuovere il file
|
||||
unlock.error.customPath.description.couldNotBeCleaned=La tua cassaforte non può essere montata sul percorso "%s". Riprova o scegli un percorso diverso.
|
||||
unlock.error.customPath.description.notEmptyDir=Il percorso di montaggio selezionato "%s" non è una cartella vuota. Scegli una cartella vuota e riprova.
|
||||
unlock.error.customPath.description.generic=Hai selezionato un percorso di montaggio personalizzato per questa cassaforte, ma il suo utilizzo non è riuscito con il messaggio: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Impossibile sbloccare la cassaforte
|
||||
unlock.error.fuseRestartRequired.description=Cambia il tipo di unità nelle opzioni della cassaforte o riavvia Cryptomator.
|
||||
unlock.error.title=Sblocco "%s" non riuscito
|
||||
## Hub
|
||||
hub.noKeychain.message=Impossibile accedere alla chiave del dispositivo
|
||||
hub.noKeychain.description=Per sbloccare le casseforti Hub, è necessaria una chiave del dispositivo, che è protetta tramite un portachiavi. Per procedere, abilita "%s" e seleziona un portachiavi nelle preferenze.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Mostra il pulsante minimizza
|
||||
preferences.interface.showTrayIcon=Mostra l'icona della barra d'applicazioni (richiede il riavvio)
|
||||
## Volume
|
||||
preferences.volume=Unità Virtuale
|
||||
preferences.volume.type=Tipo di Volume
|
||||
preferences.volume.type=Tipo di Unità Predefinita
|
||||
preferences.volume.type.automatic=Automatico
|
||||
preferences.volume.docsTooltip=Aprire la documentazione per saperne di più sui diversi tipi di volume.
|
||||
preferences.volume.fuseRestartRequired=Per applicare le modifiche, Cryptomator deve essere riavviato.
|
||||
preferences.volume.tcp.port=Porta TCP
|
||||
preferences.volume.tcp.port=Porta TCP Predefinita
|
||||
preferences.volume.supportedFeatures=Il tipo di volume scelto supporta le seguenti caratteristiche:
|
||||
preferences.volume.feature.mountAuto=Selezione automatica del punto di montaggio
|
||||
preferences.volume.feature.mountToDir=Directory personalizzata come punto di montaggio
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Avvia il Controllo della Salute
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montaggio
|
||||
vaultOptions.mount.info=Le opzioni dipendono dal tipo di volume selezionato.
|
||||
vaultOptions.mount.linkToPreferences=Apri le preferenze dell'unità virtuale
|
||||
vaultOptions.mount.info=Apri le preferenze dell'unità virtuale per modificare le impostazioni predefinite.
|
||||
vaultOptions.mount.readonly=Sola Lettura
|
||||
vaultOptions.mount.customMountFlags=Flag di Montaggio Personalizzati
|
||||
vaultOptions.mount.winDriveLetterOccupied=occupato
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Usa la lettera del drive assegnata
|
||||
vaultOptions.mount.mountPoint.custom=Usa la directory scelta
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Scegli…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Scegli una directory
|
||||
vaultOptions.mount.volumeType.default=Predefinito (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Per utilizzare questo tipo di unità, Cryptomator deve essere riavviato.
|
||||
vaultOptions.mount.volume.tcp.port=Porta TCP
|
||||
vaultOptions.mount.volume.type=Tipo di Unità
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Password
|
||||
vaultOptions.masterkey.changePasswordBtn=Modifica password
|
||||
|
||||
@@ -292,11 +292,9 @@ preferences.interface.showMinimizeButton=最小化ボタンを表示
|
||||
preferences.interface.showTrayIcon=トレイアイコンを表示 (再起動が必要)
|
||||
## Volume
|
||||
preferences.volume=仮想ドライブ
|
||||
preferences.volume.type=ボリュームの種類
|
||||
preferences.volume.type.automatic=自動
|
||||
preferences.volume.docsTooltip=異なるボリュームタイプの詳細については、ドキュメントを確認してください。
|
||||
preferences.volume.fuseRestartRequired=変更を適用するには、Cryptomator を再起動する必要があります。
|
||||
preferences.volume.tcp.port=TCP ポート
|
||||
preferences.volume.supportedFeatures=選択したボリューム形式は、次の機能をサポートしています:
|
||||
preferences.volume.feature.mountAuto=マウント先の自動選択
|
||||
preferences.volume.feature.mountToDir=カスタム ディレクトリをマウント先に指定
|
||||
@@ -435,8 +433,6 @@ vaultOptions.general.startHealthCheckBtn=正常性チェックを開始
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=マウント
|
||||
vaultOptions.mount.info=オプションは、選択されたボリュームタイプによって異なります。
|
||||
vaultOptions.mount.linkToPreferences=仮想ドライブの環境設定を開く
|
||||
vaultOptions.mount.readonly=読み取り専用
|
||||
vaultOptions.mount.customMountFlags=カスタム マウント フラグ
|
||||
vaultOptions.mount.winDriveLetterOccupied=使用中
|
||||
|
||||
@@ -241,9 +241,7 @@ preferences.interface.showMinimizeButton=최소화 버튼 표시
|
||||
preferences.interface.showTrayIcon=트레이 아이콘 보기 (재시작 필요)
|
||||
## Volume
|
||||
preferences.volume=가상 드라이브
|
||||
preferences.volume.type=볼륨 유형
|
||||
preferences.volume.type.automatic=자동
|
||||
preferences.volume.tcp.port=TCP 포트
|
||||
preferences.volume.supportedFeatures=현재 선택한 볼륨 타입은 다음과 같은 기능들을 지원합니다:
|
||||
preferences.volume.feature.mountToDir=마운트할 폴더 지정
|
||||
preferences.volume.feature.mountToDriveLetter=마운트할 드라이브 문자
|
||||
|
||||
@@ -20,6 +20,7 @@ error.description=Cryptomator negaidīja, ka tas notiks. Varat meklēt esošos
|
||||
error.hyperlink.lookup=Meklējiet šo kļūdu
|
||||
error.hyperlink.report=Ziņojiet par šo kļūdu
|
||||
error.technicalDetails=Detaļas:
|
||||
error.existingSolutionDescription=Cryptomator negaidīja, ka tas notiks. Bet mēs atradām esošu risinājumu šai kļūdai. Lūdzu, apskatiet tālāk norādīto saiti.
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Glabātava
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Den midlertidige, skjulte fil
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Hvelvet ditt kan ikke monteres i banen "%s". Prøv igjen eller velg en annen sti.
|
||||
unlock.error.customPath.description.notEmptyDir=Tilpasset monterings sti "%s" er ikke en tom mappe. Velg en tom mappe og prøv igjen.
|
||||
unlock.error.customPath.description.generic=Du har valgt en egendefinert monterings sti for dette hvelvet, men bruk av den mislyktes med meldingen: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Kan ikke låse opp hvelvet
|
||||
unlock.error.fuseRestartRequired.description=Endre volumtype i innstillinger for hvelvet eller start Cryptomator på nytt.
|
||||
unlock.error.title=Lås opp "%s" mislyktes
|
||||
## Hub
|
||||
hub.noKeychain.message=Får ikke tilgang til enhetsnøkkel
|
||||
hub.noKeychain.description=For å låse opp Hub-hvelv er det nødvendig med en enhetsnøkkel som er sikret med en nøkkelring. For å fortsette, aktiver "%s" og velg en nøkkelring i innstillingene.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Vis minimer-knapp
|
||||
preferences.interface.showTrayIcon=Vis verktøykasseikon (krever omstart)
|
||||
## Volume
|
||||
preferences.volume=Virtuell enhet
|
||||
preferences.volume.type=Volumtype
|
||||
preferences.volume.type=Standard volumtype
|
||||
preferences.volume.type.automatic=Automatisk
|
||||
preferences.volume.docsTooltip=Åpne dokumentasjonen for å lære mer om de forskjellige volumtypene.
|
||||
preferences.volume.fuseRestartRequired=For å iverksette endringene, må Cryptomator startes på nytt.
|
||||
preferences.volume.tcp.port=TCP Port
|
||||
preferences.volume.tcp.port=Standard TCP port
|
||||
preferences.volume.supportedFeatures=Valgt volumtype støtter følgende funksjoner:
|
||||
preferences.volume.feature.mountAuto=Valg av automatisk monteringspunkt
|
||||
preferences.volume.feature.mountToDir=Egendefinert mappe som monteringspunkt
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Start helsesjekk
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montering
|
||||
vaultOptions.mount.info=Alternativer avhenger av den valgte volumtypen.
|
||||
vaultOptions.mount.linkToPreferences=Åpne virtuelle drev-innstillinger
|
||||
vaultOptions.mount.info=Åpne innstillinger for virtuell enhet for å endre standardinnstillinger.
|
||||
vaultOptions.mount.readonly=Skrivebeskyttet
|
||||
vaultOptions.mount.customMountFlags=Tilpassede moteringsparametre
|
||||
vaultOptions.mount.winDriveLetterOccupied=opptatt
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Bruk tildelt drevbokstav
|
||||
vaultOptions.mount.mountPoint.custom=Bruk valgt mappe
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Velg…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Velg en mappe
|
||||
vaultOptions.mount.volumeType.default=Standard (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=For å bruke denne volumtypen må Cryptomator startes på nytt.
|
||||
vaultOptions.mount.volume.tcp.port=TCP Port
|
||||
vaultOptions.mount.volume.type=Volumtype
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Passord
|
||||
vaultOptions.masterkey.changePasswordBtn=Endre passord
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Het tijdelijk verborgen besta
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Uw kluis kon niet worden gekoppeld aan het pad "%s". Probeer het opnieuw of kies een ander pad.
|
||||
unlock.error.customPath.description.notEmptyDir=Het aangepaste pad "%s" is geen lege map. Kies een lege map en probeer het opnieuw.
|
||||
unlock.error.customPath.description.generic=Je hebt een aangepast koppelpad voor deze kluis geselecteerd, maar gebruik ervan is mislukt met het bericht: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Kan kluis niet ontgrendelen
|
||||
unlock.error.fuseRestartRequired.description=Wijzig het volumetype in kluisopties of start Cryptomator opnieuw.
|
||||
unlock.error.title=Ontgrendelen "%s" mislukt
|
||||
## Hub
|
||||
hub.noKeychain.message=Geen toegang tot de apparaatsleutel
|
||||
hub.noKeychain.description=Om Hub kluizen te ontgrendelen is een apparaatsleutel vereist, die met een sleutelhanger wordt beveiligd. Om verder te gaan, schakel "%s" in en selecteer een sleutelhanger in de voorkeursinstellingen.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Knop minimaliseren weergeven
|
||||
preferences.interface.showTrayIcon=Pictogram in systeemvak weergeven (herstart vereist)
|
||||
## Volume
|
||||
preferences.volume=Virtuele schijf
|
||||
preferences.volume.type=Type volume
|
||||
preferences.volume.type=Standaard volumetype
|
||||
preferences.volume.type.automatic=Automatisch
|
||||
preferences.volume.docsTooltip=Open de documentatie om meer te weten te komen over de verschillende volume types.
|
||||
preferences.volume.fuseRestartRequired=Om de wijzigingen toe te passen, moet Cryptomator opnieuw worden gestart.
|
||||
preferences.volume.tcp.port=TCP-poort
|
||||
preferences.volume.tcp.port=Standaard TCP-poort
|
||||
preferences.volume.supportedFeatures=Het gekozen volume type ondersteunt de volgende functies:
|
||||
preferences.volume.feature.mountAuto=Automatische koppelpunt selectie
|
||||
preferences.volume.feature.mountToDir=Aangepaste map als koppelpunt
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Start controle
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Aankoppelen
|
||||
vaultOptions.mount.info=Opties hangen af van het geselecteerde volume type.
|
||||
vaultOptions.mount.linkToPreferences=Open voorkeuren virtuele schijf
|
||||
vaultOptions.mount.info=Open 'Virtueel station' in systeemvoorkeuren om de standaardinstellingen te wijzigen.
|
||||
vaultOptions.mount.readonly=Alleen-Lezen
|
||||
vaultOptions.mount.customMountFlags=Aangepaste Aankoppel Parameters
|
||||
vaultOptions.mount.winDriveLetterOccupied=in gebruik
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Gebruik de toegewezen schijfletter
|
||||
vaultOptions.mount.mountPoint.custom=Gebruik gekozen map
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Kies…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Selecteer een map
|
||||
vaultOptions.mount.volumeType.default=Standaard (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Om dit volumetype te kunnen gebruiken, moet Cryptomator opnieuw worden opgestart.
|
||||
vaultOptions.mount.volume.tcp.port=TCP-poort
|
||||
vaultOptions.mount.volume.type=Volumetype
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Wachtwoord
|
||||
vaultOptions.masterkey.changePasswordBtn=Wijzig wachtwoord
|
||||
|
||||
@@ -95,6 +95,9 @@ unlock.chooseMasterkey.filePickerTitle=ਮਾਸਟਰ-ਕੁੰਜੀ ਫਾਇ
|
||||
unlock.success.rememberChoice=ਚੋਣਾਂ ਯਾਦ ਰੱਖੋ, ਇਹ ਮੁੜ ਕੇ ਨਾ ਵੇਖਾਓ
|
||||
unlock.success.revealBtn=ਡਰਾਇਵ ਦਿਖਾਓ
|
||||
## Failure
|
||||
unlock.error.fuseRestartRequired.message=ਵਾਲੇਟ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਅਸਮਰੱਥ
|
||||
unlock.error.fuseRestartRequired.description=ਵਾਲਟ ਚੋਣਾਂ ਵਿੱਚ ਵਾਲੀਅਮ ਦੀ ਕਿਸਮ ਨੂੰ ਬਦਲੋ ਜਾਂ Cryptomator ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰੋ।
|
||||
unlock.error.title="%s" ਨੂੰ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ
|
||||
## Hub
|
||||
### Waiting
|
||||
### Receive Key
|
||||
@@ -158,6 +161,8 @@ preferences.interface.theme.dark=ਗੂੜ੍ਹਾ
|
||||
preferences.interface.theme.light=ਹਲਕਾ
|
||||
## Volume
|
||||
preferences.volume=ਵਰਚੁਅਲ ਡਰਾਇਵ
|
||||
preferences.volume.type=ਮੂਲ ਵਾਲੀਅਮ ਕਿਸਮ
|
||||
preferences.volume.tcp.port=ਮੂਲ TCP ਪੋਰਟ
|
||||
## Updates
|
||||
preferences.updates=ਅੱਪਡੇਟ
|
||||
preferences.updates.currentVersion=ਮੌਜੂਦਾ ਵਰਜ਼ਨ: %s
|
||||
@@ -260,6 +265,7 @@ vaultOptions.general.actionAfterUnlock.ask=ਪੁੱਛੋ
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=ਮਾਊਂਟ ਕਰਨਾ
|
||||
vaultOptions.mount.info=ਮੂਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲਣ ਲਈ ਵਰਚੁਅਲ ਡਰਾਇਵ ਪਸੰਦਾਂ ਨੂੰ ਖੋਲ੍ਹੋ।
|
||||
vaultOptions.mount.readonly=ਕੇਵਲ ਪੜ੍ਹਨ ਲਈ
|
||||
vaultOptions.mount.customMountFlags=ਪਸੰਦੀਦਾ ਮਾਊਂਟ ਚਿੰਨ੍ਹ
|
||||
vaultOptions.mount.winDriveLetterOccupied=ਮੱਲਿਆ
|
||||
@@ -267,6 +273,10 @@ vaultOptions.mount.mountPoint=ਮਾਊਂਟ ਪੁਆਇੰਟ
|
||||
vaultOptions.mount.mountPoint.auto=ਆਪਣੇ-ਆਪ ਢੁੱਕਵਾਂ ਟਿਕਾਣਾ ਚੁਣ ਲਵੋ
|
||||
vaultOptions.mount.mountPoint.driveLetter=ਜਾਰੀ ਕੀਤਾ ਡਰਾਇਵ ਅੱਖਰ ਵਰਤੋਂ
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=…ਚੁਣੋ
|
||||
vaultOptions.mount.volumeType.default=ਮੂਲ (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=ਇਸ ਵਾਲੀਅਮ ਕਿਸਮ ਨੂੰ ਵਰਤਣ ਲਈ Cryptomator ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।
|
||||
vaultOptions.mount.volume.tcp.port=TCP ਪੋਰਟ
|
||||
vaultOptions.mount.volume.type=ਵਾਲੀਅਮ ਦੀ ਕਿਸਮ
|
||||
## Master Key
|
||||
vaultOptions.masterkey=ਪਾਸਵਰਡ
|
||||
vaultOptions.masterkey.changePasswordBtn=ਪਾਸਵਰਡ ਬਦਲੋ
|
||||
|
||||
@@ -141,6 +141,9 @@ unlock.error.customPath.description.hideawayNotDir=Nie można usunąć ukrytego
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Twój sejf nie mógł być zamontowany do ścieżki "%s". Spróbuj ponownie lub wybierz inną ścieżkę.
|
||||
unlock.error.customPath.description.notEmptyDir=Wybrana ścieżka montowania "%s" nie jest pustym katalogiem. Wybierz pusty katalog i spróbuj ponownie.
|
||||
unlock.error.customPath.description.generic=Wybrałeś własną ścieżkę montowania dla tego sejfu, ale użycie jej nie powiodło się. Powód: %2$s
|
||||
unlock.error.fuseRestartRequired.message=Nie można odblokować sejfu
|
||||
unlock.error.fuseRestartRequired.description=Zmień typ udziału w opcjach sejfu lub zrestartuj Cryptomator.
|
||||
unlock.error.title=Błąd odblokowywania "%s"
|
||||
## Hub
|
||||
hub.noKeychain.message=Brak dostępu do klucza urządzenia
|
||||
hub.noKeychain.description=Aby odblokować sejfy na Hubie, wymagany jest klucz urządzenia zabezpieczony za pomocą pęku kluczy. Aby kontynuować, włącz "%s" i wybierz Pęk kluczy w ustawieniach.
|
||||
@@ -294,11 +297,11 @@ preferences.interface.showMinimizeButton=Pokaż przycisk minimalizacji
|
||||
preferences.interface.showTrayIcon=Pokaż ikonę zasobnika (wymaga restartu)
|
||||
## Volume
|
||||
preferences.volume=Dysk wirtualny
|
||||
preferences.volume.type=Typ woluminu
|
||||
preferences.volume.type=Domyślny typ udziału
|
||||
preferences.volume.type.automatic=Automatyczny
|
||||
preferences.volume.docsTooltip=Sprawdź dokumentację, aby dowiedzieć się więcej o różnych typach udziałów.
|
||||
preferences.volume.fuseRestartRequired=Aby zastosować zmiany, konieczne jest ponowne uruchomienie Cryptomatora.
|
||||
preferences.volume.tcp.port=Port TCP
|
||||
preferences.volume.tcp.port=Domyślny port TCP
|
||||
preferences.volume.supportedFeatures=Wybrany typ udziału obsługuje następujące funkcje:
|
||||
preferences.volume.feature.mountAuto=Automatyczny wybór punktu montowania
|
||||
preferences.volume.feature.mountToDir=Niestandardowy katalog jako punkt montowania
|
||||
@@ -437,8 +440,7 @@ vaultOptions.general.startHealthCheckBtn=Rozpocznij Test Spójności
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount=Montowanie
|
||||
vaultOptions.mount.info=Opcje zależą od wybranego typu udziału.
|
||||
vaultOptions.mount.linkToPreferences=Otwórz ustawienia dysku wirtualnego
|
||||
vaultOptions.mount.info=Otwórz ustawienia dysku wirtualnego, aby zmienić wartości domyślne.
|
||||
vaultOptions.mount.readonly=Tylko do odczytu
|
||||
vaultOptions.mount.customMountFlags=Własne flagi montowania udziału
|
||||
vaultOptions.mount.winDriveLetterOccupied=zajęty
|
||||
@@ -448,6 +450,10 @@ vaultOptions.mount.mountPoint.driveLetter=Użyj przypisanej litery dysku
|
||||
vaultOptions.mount.mountPoint.custom=Użyj wybranego katalogu
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Wybierz…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Wybierz katalog
|
||||
vaultOptions.mount.volumeType.default=Domyślny (%s)
|
||||
vaultOptions.mount.volumeType.fuseRestartRequired=Aby użyć tego typu udziału, należy zrestartować Cryptomator.
|
||||
vaultOptions.mount.volume.tcp.port=Port TCP
|
||||
vaultOptions.mount.volume.type=Typ udziału
|
||||
## Master Key
|
||||
vaultOptions.masterkey=Hasło
|
||||
vaultOptions.masterkey.changePasswordBtn=Zmiana Hasła
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user