Merge branch 'develop' into feature/decrypt-name-single-dialog-per-vault

This commit is contained in:
Jan-Peter Klein
2026-03-30 12:47:55 +02:00
97 changed files with 1677 additions and 1257 deletions

View File

@@ -14,6 +14,9 @@ updates:
versions: ["2.0.1.MR"]
- dependency-name: "org.openjfx:*"
update-types: ["version-update:semver-major"]
# due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
versions: [ "3.5.4", "3.5.5" ]
groups:
java-test-dependencies:
patterns:

View File

@@ -3,6 +3,8 @@ name: Build AppImage
on:
release:
types: [published]
schedule:
- cron: '0 23 20 * *'
workflow_dispatch:
inputs:
version:
@@ -47,10 +49,12 @@ jobs:
arch: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
- os: ubuntu-24.04-arm
arch: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
@@ -142,6 +146,7 @@ jobs:
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${REV_NUM}\""
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
@@ -165,7 +170,8 @@ jobs:
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit
run: |
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
echo "${{ matrix.appimagetool-sha }} appimagetool.AppImage" | shasum -a256 --check
chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -185,7 +191,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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: appimage-${{ matrix.arch }}
path: |

View File

@@ -51,7 +51,7 @@ jobs:
- name: Download file
run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}"
- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ steps.extractName.outputs.fileName }}
path: ${{ steps.extractName.outputs.fileName }}
@@ -63,7 +63,7 @@ jobs:
if: inputs.kaspersky
steps:
- name: Download artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
@@ -83,7 +83,7 @@ jobs:
if: inputs.avast
steps:
- name: Download artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload

View File

@@ -67,7 +67,7 @@ jobs:
### Other Changes 📎
END REPLACE-->
Feel free to also read our [CHANGELOG.md](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
---

View File

@@ -1,6 +1,8 @@
name: Build Debian Package
on:
schedule:
- cron: '0 22 20 * *'
workflow_dispatch:
inputs:
semver:
@@ -143,7 +145,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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: linux-deb-package
path: |

View File

@@ -1,85 +0,0 @@
name: Create PR for flathub
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag'
required: true
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.tag }}
tarball:
name: Determines tarball url and compute checksum
runs-on: ubuntu-latest
needs: [get-version]
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
outputs:
url: ${{ steps.url.outputs.url}}
sha512: ${{ steps.sha512.outputs.sha512}}
steps:
- name: Determine tarball url
id: url
run: |
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz"
echo "url=${URL}" >> "$GITHUB_OUTPUT"
env:
TAG: ${{ inputs.tag || github.event.release.tag_name}}
- name: Download source tarball and compute checksum
id: sha512
run: |
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
echo "sha512=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
flathub:
name: Create PR for flathub
runs-on: ubuntu-latest
needs: [tarball, get-version]
env:
FLATHUB_PR_URL: tbd
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'flathub/org.cryptomator.Cryptomator'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Checkout release branch
run: |
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
- name: Update build file
run: |
sed -i -e 's/VERSION: [0-9]\+\.[0-9]\+\.[0-9]\+.*/VERSION: ${{ needs.get-version.outputs.semVerStr }}/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's/sha512: [0-9A-Za-z_\+-]\{128\} #CRYPTOMATOR/sha512: ${{ needs.tarball.outputs.sha512 }} #CRYPTOMATOR/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's;url: https://github.com/cryptomator/cryptomator/archive/refs/tags/[^[:blank:]]\+;url: ${{ needs.tarball.outputs.url }};g' org.cryptomator.Cryptomator.yaml
- name: Commit and push
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
git config push.autoSetupRemote true
git stage .
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
git push
- name: Create pull request
run: |
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update maven dependencies\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV"
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
if: github.event_name == 'release'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
SLACK_MESSAGE: "See <${{ env.FLATHUB_PR_URL }}|PR> on how to proceed.>."
SLACK_FOOTER: false
MSG_MINIMAL: true

264
.github/workflows/linux-flatpak.yml vendored Normal file
View File

@@ -0,0 +1,264 @@
name: Build flatpak
on:
release:
types: [published]
workflow_dispatch:
inputs:
src-tag:
description: 'Source or Release tag'
required: false
create-pr:
description: 'Create Flathub PR'
required: false
type: boolean
default: false
push:
branches-ignore:
- 'dependabot/**'
paths:
- '.github/workflows/get-version.yml'
- '.github/workflows/linux-flatpak.yml'
- 'dist/linux/flatpak/**'
- 'dist/linux/common/**'
- 'dist/linux/resources/**'
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.src-tag }}
build-flatpak:
name: "Build flatpak"
needs: [get-version]
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
options: --privileged
strategy:
fail-fast: false
matrix:
variant:
- arch: x86_64
runner: ubuntu-24.04
- arch: aarch64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.variant.runner }}
permissions:
contents: read
env:
SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: flathub/org.cryptomator.Cryptomator
submodules: true
- name: Checkout build script
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: build-scripts
- name: Checkout app source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: cryptomator
ref: ${{ env.SRC_GIT_SHA }}
fetch-depth: 0
- name: Prepare build files
# using envsubst instead of yq to keep linebreaks
run: |
cp -r -f build-scripts/dist/linux/flatpak/* .
envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml
env:
FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }}
FLATPAK_REVISION: 1
CRYPTOMATOR_SOURCE: |-
type: git
path: cryptomator
commit: ${{ env.SRC_GIT_SHA }}
- name: Copy build script for upload
run: cp org.cryptomator.Cryptomator.yaml org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
archive: false
if-no-files-found: error
path: |
org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
- uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # SNAPSHOT due to using keep-build-dirs
with:
bundle: cryptomator.flatpak
manifest-path: org.cryptomator.Cryptomator.yaml
cache-key: flatpak-builder-${{ env.SRC_GIT_SHA }}
arch: ${{ matrix.variant.arch }}
keep-build-dirs: true
- name: Collect maven dependencies
working-directory: .flatpak-builder/build/cryptomator-1/.m2/repository/
run: |
find * -type f \( -iname '*.jar' -o -iname '*.pom' \) | sort -V > /tmp/maven-dependency-files.txt
grep -v '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-common.txt
grep '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-javafx.txt
- name: Update arch independent maven dependencies
run: |
(
cd .flatpak-builder/build/cryptomator-1/.m2/repository/
while IFS= read -r dependencyPath; do
dependencyName=$(dirname "$dependencyPath")
dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64)
cat <<EOF
- type: file
dest: .m2/repository/${dependencyName}
url: https://repo.maven.apache.org/maven2/${dependencyPath}
sha256: ${dependencySha}
EOF
done < maven-dependency-files-common.txt
) > maven-dependencies.yaml
- name: Update arch specific maven dependencies
run: |
(
cd .flatpak-builder/build/cryptomator-1/.m2/repository/
while IFS= read -r dependencyPath; do
dependencyName=$(dirname "$dependencyPath")
dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64)
cat <<EOF
- type: file
dest: .m2/repository/${dependencyName}
url: https://repo.maven.apache.org/maven2/${dependencyPath}
sha256: ${dependencySha}
only-arches: [${{ matrix.variant.arch }}]
EOF
done < maven-dependency-files-javafx.txt
) > javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: maven-sources-${{ matrix.variant.arch }}
if-no-files-found: error
path: |
maven-dependencies.yaml
javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
verify-maven-sources:
name: Verify maven sources
runs-on: ubuntu-latest
needs: [build-flatpak]
permissions:
contents: none
steps:
- name: Download updated maven aarch64 dependencies
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: maven-sources-aarch64
path: mvn-src-aarch64
- name: Download updated maven x86_64 dependencies
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: maven-sources-x86_64
path: mvn-src-x64
- name: Verify arch independent maven dependencies
run: cmp --silent mvn-src-aarch64/maven-dependencies.yaml mvn-src-x64/maven-dependencies.yaml
create-pr:
name: Create PR for flathub
runs-on: ubuntu-latest
needs: [get-version, verify-maven-sources]
if: (github.event_name == 'workflow_dispatch' && inputs.create-pr ) || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable')
permissions:
contents: write
env:
TARBALL_URL: 'https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name || inputs.src-tag }}.tar.gz'
steps:
- name: Check that input "src-tag" is actually a tag
if: github.event_name == 'workflow_dispatch'
run: |
if [ -z "$SRC_TAG" ]; then
echo '::error::Input "src-tag" must be set to create a Flathub PR'
exit 1
fi
env:
SRC_TAG: ${{ inputs.src-tag }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: flathub/org.cryptomator.Cryptomator
submodules: true #TODO: Update submodule!
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Checkout release branch
run: |
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: cryptomator
- name: Download source tarball and compute checksum
id: sha512
run: |
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${TARBALL_URL} --output cryptomator.tar.gz
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
echo "value=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
- name: Download updated maven aarch64 dependencies
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: maven-sources-aarch64
path: mvn-src-aarch64
- name: Download updated maven x86_64 dependencies
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: maven-sources-x86_64
path: mvn-src-x64
- name: Determine revision
id: revision
run: |
CURRENT_VERSION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.VERSION)' org.cryptomator.Cryptomator.yaml)"
CURRENT_REVISION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.REVISION_NO)' org.cryptomator.Cryptomator.yaml)"
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REVISION" =~ ^[0-9]+$ ]]; then
NEXT_REVISION=$((CURRENT_REVISION + 1))
else
NEXT_REVISION=1
fi
echo "value=${NEXT_REVISION}" >> "$GITHUB_OUTPUT"
env:
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
- name: Update build files
run: |
cp -r -f cryptomator/dist/linux/flatpak/* .
cp -r -f mvn-src-x64/* .
cp -r -f mvn-src-aarch64/* .
envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml
yq -i 'del(.modules[] | select(.name == "cryptomator") | .build-options.build-args)' org.cryptomator.Cryptomator.yaml
yq -i '(.modules[] | select(.name == "cryptomator") | .sources) += ["maven-dependencies.yaml", "javafx-maven-dependencies-x86_64.yaml", "javafx-maven-dependencies-aarch64.yaml"]' org.cryptomator.Cryptomator.yaml
env:
FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }}
FLATPAK_REVISION: ${{ steps.revision.outputs.value}}
CRYPTOMATOR_SOURCE: |-
type: archive
sha512: ${{steps.sha512.outputs.value}}
url: ${{ env.TARBALL_URL }}
- name: Commit and push
run: |
git config user.name "cryptobot"
git config user.email "cryptobot@users.noreply.github.com"
git config push.autoSetupRemote true
git stage org.cryptomator.Cryptomator.yaml maven-dependencies.yaml javafx-maven-dependencies-aarch64.yaml javafx-maven-dependencies-x86_64.yaml
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
git push
- name: Create pull request
id: create-pr
run: |
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
if: github.event_name == 'release'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.FLATHUB_PR_URL }}|PR> on how to proceed."
SLACK_FOOTER: false
MSG_MINIMAL: true

View File

@@ -3,6 +3,8 @@ name: Build Arch package
on:
release:
types: [published]
schedule:
- cron: '0 21 20 * *'
workflow_dispatch:
inputs:
version:
@@ -67,13 +69,13 @@ jobs:
sudo -u builder
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
makepkg --syncdeps --cleanbuild --noconfirm --log
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: arch-package
if-no-files-found: error
path: |
${{ env.PKGDEST }}/*.pkg.tar.zst
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pkgbuild-file
if-no-files-found: error
@@ -145,7 +147,7 @@ jobs:
env:
VERSION: ${{ needs.get-version.outputs.semVerStr }}
- name: Download PKGBUILD template
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: pkgbuild-file
- name: Prepare PKGBUILD

View File

@@ -11,6 +11,8 @@ name: Build macOS .dmg for x64
on:
release:
types: [published]
schedule:
- cron: '0 20 20 * *'
workflow_dispatch:
inputs:
version:
@@ -138,12 +140,14 @@ jobs:
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
run: |
mv appdir/Cryptomator.app Cryptomator.app
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
@@ -151,20 +155,6 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Build and install DockTilePlugin
env:
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
run: |
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-destination "platform=macOS,arch=x86_64" \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p Cryptomator.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
- name: Generate license for dmg
run: >
mvn -B license:add-third-party
@@ -261,7 +251,7 @@ jobs:
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize || github.event_name == 'schedule'
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -283,7 +273,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |

View File

@@ -3,6 +3,8 @@ name: Build macOS .dmg for arm64
on:
release:
types: [published]
schedule:
- cron: '0 20 20 * *'
workflow_dispatch:
inputs:
version:
@@ -137,12 +139,14 @@ jobs:
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
run: |
mv appdir/Cryptomator.app Cryptomator.app
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
@@ -150,20 +154,6 @@ jobs:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Build and install DockTilePlugin
env:
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
run: |
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-destination "platform=macOS,arch=arm64" \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p Cryptomator.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
- name: Generate license for dmg
run: >
mvn -B license:add-third-party
@@ -260,7 +250,7 @@ jobs:
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize || github.event_name == 'schedule'
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -282,7 +272,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 14
days-before-close: 0

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 365
days-before-close: 90

View File

@@ -3,6 +3,8 @@ name: Build Windows Installer
on:
release:
types: [published]
schedule:
- cron: '0 19 20 * *'
workflow_dispatch:
inputs:
version:
@@ -157,6 +159,7 @@ jobs:
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
--java-options "-Dcryptomator.disableUpdateCheck=false"
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties"
@@ -192,7 +195,7 @@ jobs:
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
- name: Sign DLLs with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\appdir
@@ -201,17 +204,6 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign DLLs with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'appdir'
file-extensions: 'dll,exe,ps1'
recursive: true
sign-description: 'Cryptomator'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Replace DLLs inside jars with signed ones
shell: pwsh
run: |
@@ -261,7 +253,7 @@ jobs:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Sign MSI with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -280,7 +272,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: msi-${{ matrix.arch }}
path: |
@@ -311,7 +303,7 @@ jobs:
env:
WIX_VERSION: ${{ env.WIX_VERSION }}
- name: Download .msi
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: msi-${{ matrix.arch }}
path: dist/win/bundle/resources
@@ -369,7 +361,7 @@ jobs:
run: >
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\tmp
@@ -379,21 +371,11 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign burn engine with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'tmp'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Reattach signed burn engine to installer
run: >
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
- name: Sign EXE installer with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -403,16 +385,6 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign installer with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'installer'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
- name: Create detached GPG signature with key 615D449FE6E6A235
@@ -423,7 +395,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: exe-${{ matrix.executable-suffix }}
path: |
@@ -441,7 +413,7 @@ jobs:
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
steps:
- name: Download installers
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
merge-multiple: true
- name: Publish installers on GitHub Releases
@@ -479,7 +451,7 @@ jobs:
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -7,7 +7,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The changelog starts with version 1.19.0.
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.19.1...HEAD)
No changes yet.
## [1.19.2](https://github.com/cryptomator/cryptomator/releases/1.19.2) - 2026-03-20
### Security
* Cryptomamtor Hub Vaults: Additional patch for (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
## [1.19.1](https://github.com/cryptomator/cryptomator/releases/1.19.1) - 2026-03-12
### Security
* Cryptomamtor Hub Vaults: Fixed possible man-in-the-middle attack with tampered vault config (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
* Disallow unencrypted http connections to hub by default ([CVE-2026-32309](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-vv33-h7qx-c264))
* Disallow loading of masterkey file from arbitrary paths (#4180, [CVE-2026-32310](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-5phc-5pfx-hr52))
* Fixed not-configured plugin directory does not disable plugin search ([#4176](https://github.com/cryptomator/cryptomator/pull/4176))
### Added
* Trust on first use, adding new config properties `cryptomator.hub.allowedHosts` and `cryptomator.hub.enableTrustOnFirstUse` (#4179)
### Fixed
* Fixed Finder window opens twice when revealing vault on macOS ([#4177](https://github.com/cryptomator/cryptomator/pull/4177))
* Fixed app does not start due to secret service detection failure on Linux ([#4175](https://github.com/cryptomator/cryptomator/pull/4175))
### Changed
* Pin version of appimagetool([#4181](https://github.com/cryptomator/cryptomator/pull/4181))
* Updated translations
* Updated dependencies:
* `org.cryptomator:integrations-api` from 1.8.0-beta1 to 1.8.0
* `org.cryptomator:integrations-linux` from 1.7.0-beta4 to 1.7.0
* `org.cryptomator:integrations-mac` from 1.5.0-beta3 to 1.5.0
## [1.19.0](https://github.com/cryptomator/cryptomator/releases/tag/1.19.0) - 2026-03-09
### Added
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
@@ -20,31 +57,33 @@ Changes to prior versions can be found on the [Github release page](https://gith
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
* New keychain backend using [secret service API](https://specifications.freedesktop.org/secret-service/0.2) for Linux ([#4025](https://github.com/cryptomator/cryptomator/pull/4025))
* Liquid Glass icon for macOS ([#4166](https://github.com/cryptomator/cryptomator/pull/4166))
### Fixed
* Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120))
* Fixed restore vault config failed due to selecting a directory instead of file ([#4141](https://github.com/cryptomator/cryptomator/issues/4141))
* Fixed leaking of cleartext paths into application log ([GHSA-j83j-mwhc-rcgw](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-j83j-mwhc-rcgw))
### Changed
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
* Disable plugin loading by default ([#4136](https://github.com/cryptomator/cryptomator/4136))
* Use JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/pull/4031))
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145)))
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145))
* Updated translations
* Updated dependencies
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
* `ch.qos.logback:*` from 1.5.19 to 1.5.32
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.1
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.1
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
* `com.google.dagger:*` from 2.57.2 to 2.59.1
* `com.google.dagger:*` from 2.57.2 to 2.59.2
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.1
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1
* `org.cryptomator:webdav-nio-adapter-servlet` to 1.2.12

View File

@@ -99,6 +99,7 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--resource-dir ../resources
# transform AppDir
@@ -123,7 +124,7 @@ ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/meta
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage

View File

@@ -73,6 +73,7 @@
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
<url type="help">https://docs.cryptomator.org/</url>
<url type="translate">https://translate.cryptomator.org</url>
<url type="vcs-browser">https://github.com/cryptomator/cryptomator</url>
<developer id="de.skymatic">
<name>Skymatic GmbH</name>
@@ -83,6 +84,15 @@
</content_rating>
<releases>
<release date="2026-03-20" version="1.19.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.2</url>
</release>
<release date="2026-03-12" version="1.19.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.1</url>
</release>
<release date="2026-03-09" version="1.19.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.0</url>
</release>
<release date="2025-11-12" version="1.18.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
</release>

View File

@@ -64,6 +64,7 @@ override_dh_auto_build:
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \
--verbose

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# From: https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/build-aux/flatpak/fusermount-wrapper.sh
if [ -z "$_FUSE_COMMFD" ]; then
FD_ARGS=
else
FD_ARGS="--env=_FUSE_COMMFD=${_FUSE_COMMFD} --forward-fd=${_FUSE_COMMFD}"
fi
if [ -e /proc/self/fd/3 ] && [ 3 != "$_FUSE_COMMFD" ]; then
FD_ARGS="$FD_ARGS --forward-fd=3"
fi
exec flatpak-spawn --host --forward-fd=1 --forward-fd=2 $FD_ARGS fusermount3 "$@"

View File

@@ -0,0 +1,182 @@
app-id: org.cryptomator.Cryptomator
command: cryptomator
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
separate-locales: false
finish-args:
# Required for FUSE, see https://github.com/flathub/org.cryptomator.Cryptomator/pull/68#issuecomment-1935136502
- --device=all
# Set the PATH environment variable in the application, as flatpak is resetting the shell's PATH
- --env=PATH=/app/bin/:/usr/bin/
# Allow filesystem access to the user's home dir
# Needed to manage vaults there
- --filesystem=home
# Reading system certificates
- --filesystem=host-etc:ro
# Allow access to the XDG data directory
# Needed to connect to KeePassXC's UNIX domain socket
- --filesystem=xdg-run/org.keepassxc.KeePassXC.BrowserServer
- --filesystem=xdg-run/app/org.keepassxc.KeePassXC/
# Share IPC namespace with the host, without it the X11 shared memory extension will not work
- --share=ipc
# Allow access to the network
- --share=network
# Show windows using X11
- --socket=x11
# Needed to reveal encrypted files
- --talk-name=org.freedesktop.FileManager1
# Run any command on the host
# Needed to spawn fusermount on the host
- --talk-name=org.freedesktop.Flatpak
# Allow desktop notifications
- --talk-name=org.freedesktop.Notifications
# Allow access to the GNOME secret service API and to talk to the GNOME keyring daemon
- --talk-name=org.freedesktop.secrets
- --talk-name=org.gnome.keyring
# Allow to talk to the KDE kwallet daemon
- --talk-name=org.kde.kwalletd5
- --talk-name=org.kde.kwalletd6
# Needed to talk to the gvfs daemons over D-Bus and list mounts using the GIO APIs
- --talk-name=org.gtk.vfs.*
# Allow access to appindicator icons
- --talk-name=org.ayatana
# Allow access to appindicator icons on KDE
- --talk-name=org.kde.StatusNotifierWatcher
cleanup:
- /include
- /lib/pkgconfig
modules:
- shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json
- name: libfuse
buildsystem: meson
config-opts:
- -Dexamples=false
- -Dinitscriptdir=
- -Duseroot=false
- -Dtests=false
# don't install rules on the host
- -Dudevrulesdir=/tmp/
sources:
- type: archive
url: https://github.com/libfuse/libfuse/releases/download/fuse-3.16.2/fuse-3.16.2.tar.gz
sha256: f797055d9296b275e981f5f62d4e32e089614fc253d1ef2985851025b8a0ce87
x-checker-data:
type: anitya
project-id: 861
url-template: https://github.com/libfuse/libfuse/releases/download/fuse-$version/fuse-$version.tar.gz
versions: {<: '3.17.0'}
- name: host-command-wrapper
buildsystem: simple
build-commands:
- install fusermount-wrapper.sh /app/bin/fusermount3
sources:
- type: file
path: build-aux/fusermount-wrapper.sh
- name: cryptomator
buildsystem: simple
build-options:
build-args:
- --share=network
env:
PATH: /app/bin:/usr/bin
MAVEN_OPTS: -Dmaven.repo.local=.m2/repository
JAVA_HOME: jdk
JMODS_PATH: jmods
VERSION: $FLATPAK_VERSION
REVISION_NO: '$FLATPAK_REVISION'
build-commands:
# Setup Java
- tar xvfz jdk.tar.gz --transform 's!^[^/]*!jdk!'
- mkdir jmods
- unzip -j openjfx.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods
# Setup Maven
- mkdir maven
- tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven
# Build project
- maven/bin/mvn clean package -DskipTests -P"linux-$(uname -m)"
- cp target/cryptomator-*.jar target/mods
- cd target
- $JAVA_HOME/bin/jlink
--output runtime
--module-path $JMODS_PATH
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
--no-header-files
--no-man-pages
--strip-debug
--compress=zip-0
- $JAVA_HOME/bin/jpackage
--type app-image
--runtime-image runtime
--input target/libs
--module-path target/mods
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
--dest .
--name Cryptomator
--vendor 'Skymatic GmbH'
--copyright '(C) 2016 - 2025 Skymatic GmbH'
--java-options '--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator'
--java-options "--sun-misc-unsafe-memory-access=allow"
--java-options '-Xss5m'
--java-options '-Xmx256m'
--java-options '-Dfile.encoding='utf-8''
--java-options '-Djava.net.useSystemProxies=true'
--java-options "-Dcryptomator.appVersion='${VERSION}'"
--java-options "-Dcryptomator.buildNumber='flatpak-${REVISION_NO}'"
--java-options '-Dcryptomator.ipcSocketPath='@{userhome}/.config/Cryptomator/ipc.socket''
--java-options '-Dcryptomator.adminConfigPath='/run/host/etc/cryptomator/config.properties''
--java-options '-Dcryptomator.logDir='@{userhome}/.local/share/Cryptomator/logs''
--java-options '-Dcryptomator.mountPointsDir='@{userhome}/.local/share/Cryptomator/mnt''
--java-options '-Dcryptomator.pluginDir='@{userhome}/.local/share/Cryptomator/plugins''
--java-options '-Dcryptomator.p12Path='@{userhome}/.config/Cryptomator/key.p12''
--java-options '-Dcryptomator.settingsPath='@{userhome}/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json''
--java-options '-Dcryptomator.showTrayIcon=true'
--java-options '-Dcryptomator.updateMechanism=org.cryptomator.linux.update.FlatpakUpdater'
--java-options '-Dcryptomator.networking.truststore.p12Path='/run/host/etc/cryptomator/certs.p12''
--java-options '-Dcryptomator.hub.enableTrustOnFirstUse=true'
--app-version "${VERSION}.${REVISION_NO}"
--verbose
- cp -R Cryptomator /app/
- ln -s /app/Cryptomator/bin/Cryptomator /app/bin/cryptomator
- cp -R /app/lib/* /app/Cryptomator/lib/app/
- install -D -m0644 -t /app/share/applications/ dist/linux/common/org.cryptomator.Cryptomator.desktop
- install -D -m0644 -t /app/share/icons/hicolor/scalable/apps/ dist/linux/common/org.cryptomator.Cryptomator.svg
- install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
- install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
- install -D -m0644 -t /app/share/metainfo/ dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml
sources:
- $CRYPTOMATOR_SOURCE
- type: file
dest-filename: jdk.tar.gz
only-arches:
- x86_64
url: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_x64_linux_hotspot_25.0.2_10.tar.gz
sha512: 29043fde119a031c2ca8d57aed445fedd9e7f74608fcdc7a809076ba84cfd1c31f08de2ecccf352e159fdcd1cae172395ed46363007552ff242057826c81ab3a
- type: file
dest-filename: jdk.tar.gz
only-arches:
- aarch64
url: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.2_10.tar.gz
sha512: f1d3ccec3e1f1bed9d632f14b9223709d6e5c2e0d922125d068870dd3016492a2ca8f08924d4a9d0dc5eb2159fa09efee366a748fd0093475baf29e5c70c781a
- type: file
dest-filename: openjfx.zip
only-arches:
- x86_64
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip
sha512: 21f550217101c513f9eb1d7947eba30cb79618238e6539ce770e54e84b01574cdaeba40af602391145f163dd8e43e3794395467413152f13ffffeff948b0ca1b
- type: file
dest-filename: openjfx.zip
only-arches:
- aarch64
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip
sha512: a9268409b3803e386490bf1319d0f0a14173cebe862c12254cd51b430ee0a297437d9e38d5ebeae0da8899be898b312b103330d09dcfd3e63c1e7d15f2f14311
- type: file
dest-filename: maven.tar.gz
url: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.13/apache-maven-3.9.13-bin.tar.gz
sha512: d9ccd44ba2991586e359c29eb86780ae8ff4ec1b88b0b8af3af074803472690cf2017782a9c4401343c62cbcd056231db9612e1e551cbd9747c21746d732c015
x-checker-data:
type: anitya
project-id: 1894
stable-only: true
url-template: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/$version/apache-maven-$version-bin.tar.gz
versions: {<: '4.0'}

View File

@@ -94,6 +94,7 @@ build() {
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--app-version "${pkgver//_*/}" \
--verbose
}

1
dist/mac/.gitignore vendored
View File

@@ -1,2 +1 @@
embedded.provisionprofile
xcuserdata/

View File

@@ -1,19 +0,0 @@
//
// CryptomatorDockTilePlugin.swift
// Integrations
//
// Created by Tobias Hagemann on 22.09.25.
// Copyright © 2025 Cryptomator. All rights reserved.
//
import AppKit
class CryptomatorDockTilePlugin: NSObject, NSDockTilePlugIn {
func setDockTile(_ dockTile: NSDockTile?) {
guard let dockTile = dockTile, let image = Bundle(for: Self.self).image(forResource: "Cryptomator") else {
return
}
dockTile.contentView = NSImageView(image: image)
dockTile.display()
}
}

View File

@@ -1,314 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */; };
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */ = {isa = PBXBuildFile; fileRef = 74E08DEC2E858532007E665C /* Cryptomator.icns */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cryptomator.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptomatorDockTilePlugin.swift; sourceTree = "<group>"; };
74E08DEC2E858532007E665C /* Cryptomator.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Cryptomator.icns; path = ../resources/Cryptomator.icns; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
74E08DD62E858467007E665C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
74E08DD02E858467007E665C = {
isa = PBXGroup;
children = (
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */,
74E08DEC2E858532007E665C /* Cryptomator.icns */,
74E08DDA2E858467007E665C /* Products */,
);
sourceTree = "<group>";
};
74E08DDA2E858467007E665C /* Products */ = {
isa = PBXGroup;
children = (
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
74E08DD82E858467007E665C /* DockTilePlugin */ = {
isa = PBXNativeTarget;
buildConfigurationList = 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */;
buildPhases = (
74E08DD52E858467007E665C /* Sources */,
74E08DD62E858467007E665C /* Frameworks */,
74E08DD72E858467007E665C /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = DockTilePlugin;
packageProductDependencies = (
);
productName = DockTilePlugin;
productReference = 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
74E08DD12E858467007E665C /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 2600;
ORGANIZATIONNAME = Cryptomator;
TargetAttributes = {
74E08DD82E858467007E665C = {
CreatedOnToolsVersion = 26.0.1;
};
};
};
buildConfigurationList = 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 74E08DD02E858467007E665C;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 74E08DDA2E858467007E665C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
74E08DD82E858467007E665C /* DockTilePlugin */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
74E08DD72E858467007E665C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
74E08DD52E858467007E665C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
74E08DDB2E858467007E665C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = YZQJQUHA3L;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 11.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
74E08DDC2E858467007E665C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = YZQJQUHA3L;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 11.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
74E08DDE2E858467007E665C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
PRODUCT_NAME = Cryptomator;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = Debug;
};
74E08DDF2E858467007E665C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
PRODUCT_NAME = Cryptomator;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
74E08DDB2E858467007E665C /* Debug */,
74E08DDC2E858467007E665C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
74E08DDE2E858467007E665C /* Debug */,
74E08DDF2E858467007E665C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 74E08DD12E858467007E665C /* Project object */;
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "74E08DD82E858467007E665C"
BuildableName = "Cryptomator.docktileplugin"
BlueprintName = "DockTilePlugin"
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "74E08DD82E858467007E665C"
BuildableName = "Cryptomator.docktileplugin"
BlueprintName = "DockTilePlugin"
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

15
dist/mac/dmg/build.sh vendored
View File

@@ -125,28 +125,17 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
# transform app dir
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
cp ../resources/Assets.car ${APP_NAME}.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
# build and install dock tile plugin
echo "Building and installing Cryptomator.docktileplugin..."
DERIVED_DATA_PATH=../DockTilePlugin/build
xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \
-scheme DockTilePlugin \
-configuration Release \
-derivedDataPath ${DERIVED_DATA_PATH} \
-quiet \
clean build
mkdir -p ${APP_NAME}.app/Contents/PlugIns
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/
rm -rf ${DERIVED_DATA_PATH}
# generate license
mvn -B -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \

BIN
dist/mac/resources/Assets.car vendored Normal file

Binary file not shown.

View File

@@ -12,6 +12,8 @@
<string>Cryptomator</string>
<key>CFBundleIconFile</key>
<string>Cryptomator.icns</string>
<key>CFBundleIconName</key>
<string>Cryptomator</string>
<key>CFBundleIdentifier</key>
<string>org.cryptomator</string>
<key>CFBundleInfoDictionaryVersion</key>
@@ -117,8 +119,5 @@
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<!-- register dock tile plugin -->
<key>NSDockTilePlugIn</key>
<string>Cryptomator.docktileplugin</string>
</dict>
</plist>

1
dist/win/build.ps1 vendored
View File

@@ -167,6 +167,7 @@ $javaOptions = @(
"--java-options", "-Dcryptomator.showTrayIcon=true"
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
"--java-options", "-Dcryptomator.hub.enableTrustOnFirstUse=true"
)

View File

@@ -109,7 +109,7 @@
</ns0:CreateFolder>
</ns0:Component>
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\config.properties" Name="config.properties" KeyPath="yes">
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Administrators" GenericAll="yes"/>
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>

96
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.19.0-SNAPSHOT</version>
<version>1.20.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -33,45 +33,46 @@
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.10.0</cryptomator.cryptofs.version>
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.version>1.8.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
<!-- 3rd party dependencies -->
<caffeine.version>3.2.3</caffeine.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<dagger.version>2.59.1</dagger.version>
<dagger.version>2.59.2</dagger.version>
<easybind.version>2.2</easybind.version>
<jackson.version>2.21.0</jackson.version>
<jackson.version>2.21.1</jackson.version>
<javafx.version>25.0.2</javafx.version>
<jwt.version>4.5.0</jwt.version>
<jwt.version>4.5.1</jwt.version>
<nimbus-jose.version>10.5</nimbus-jose.version>
<logback.version>1.5.31</logback.version>
<logback.version>1.5.32</logback.version>
<slf4j.version>2.0.17</slf4j.version>
<tinyoauth2.version>0.8.1</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>5.13.4</junit.jupiter.version>
<mockito.version>5.20.0</mockito.version>
<junit.jupiter.version>6.0.3</junit.jupiter.version>
<mockito.version>5.22.0</mockito.version>
<hamcrest.version>3.0</hamcrest.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
<dependency-check.version>12.1.5</dependency-check.version>
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
<dependency-check.version>12.2.0</dependency-check.version>
<jacoco.version>0.8.14</jacoco.version>
<license-generator.version>2.7.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
<mvn-compiler.version>3.14.1</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.8.1</mvn-dependency.version>
<mvn-surefire.version>3.5.4</mvn-surefire.version>
<mvn-jar.version>3.4.2</mvn-jar.version>
<license-generator.version>2.7.1</license-generator.version>
<junit-tree-reporter.version>1.5.1</junit-tree-reporter.version>
<mvn-compiler.version>3.15.0</mvn-compiler.version>
<mvn-resources.version>3.5.0</mvn-resources.version>
<mvn-dependency.version>3.10.0</mvn-dependency.version>
<mvn-surefire.version>3.5.3</mvn-surefire.version>
<mvn-jar.version>3.5.0</mvn-jar.version>
<!-- Property used by surefire to determine jacoco engine -->
<surefire.jacoco.args></surefire.jacoco.args>
@@ -93,6 +94,11 @@
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter-servlet</artifactId>
<version>${cryptomator.webdav-servlet.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
@@ -521,11 +527,57 @@
</profile>
<profile>
<id>linux</id>
<id>linux-aarch64</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
<arch>aarch64</arch>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-linux</artifactId>
<version>${cryptomator.integrations.linux.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
</dependencies>
</profile>
<profile>
<id>linux-x86_64</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
<arch>amd64</arch>
</os>
<property>
<name>idea.version</name>

View File

@@ -13,6 +13,7 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.logging.LogbackConfiguratorFactory;
@@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.ui.fxapp.JfxRevealPathService;
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
@@ -64,6 +66,7 @@ open module org.cryptomator.desktop {
uses org.cryptomator.event.NotificationHandler;
provides UiAppearanceProvider with JfxUiAppearanceProvider;
provides RevealPathService with JfxRevealPathService;
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;

View File

@@ -22,8 +22,6 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
@@ -76,8 +74,8 @@ public abstract class CommonsModule {
@Provides
@Singleton
static Optional<RevealPathService> provideRevealPathService() {
return RevealPathService.get().findFirst();
static RevealPathService provideRevealPathService() {
return RevealPathService.get().findFirst().orElseThrow();
}

View File

@@ -9,10 +9,13 @@ import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -20,20 +23,22 @@ public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final int DEFAULT_MIN_PW_LENGTH = 8;
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
public static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
public static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
public static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
public static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
public static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
public static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
public static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
public static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
public static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
public static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
public static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
public static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
public static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
public static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
public static final String HUB_ALLOWED_HOSTS_PROP_NAME = "cryptomator.hub.allowedHosts";
public static final String HUB_TOFU_PROP_NAME = "cryptomator.hub.enableTrustOnFirstUse";
private Environment() {}
@@ -57,6 +62,8 @@ public class Environment {
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
logCryptomatorSystemProperty(HUB_ALLOWED_HOSTS_PROP_NAME);
logCryptomatorSystemProperty(HUB_TOFU_PROP_NAME);
}
public static Environment getInstance() {
@@ -145,6 +152,18 @@ public class Environment {
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
}
public Set<String> hubAllowedHosts() {
var allowedHubHostsString = System.getProperty(HUB_ALLOWED_HOSTS_PROP_NAME, "");
return Arrays.stream(allowedHubHostsString.split(","))
.map(String::trim)
.filter(Predicate.not(String::isEmpty))
.collect(Collectors.toUnmodifiableSet());
}
public boolean hubTrustOnFirstUse() {
return Boolean.getBoolean(HUB_TOFU_PROP_NAME);
}
private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);

View File

@@ -1,7 +1,7 @@
package org.cryptomator.common;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import java.util.Map;
import java.util.Properties;
@@ -13,10 +13,12 @@ public class SubstitutingProperties extends PropertiesDecorator {
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
private final Map<String, String> env;
private final Logger logger;
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment) {
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment, Logger logger) {
super(props);
this.env = systemEnvironment;
this.logger = logger;
}
@Override
@@ -44,7 +46,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
case "userhome" -> resolveFrom("user.home", Source.PROPS);
default -> {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value);
logger.warn("Unknown variable {} in property value {}.", match.group(), value);
yield match.group();
}
});
@@ -56,7 +58,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
case PROPS -> delegate.getProperty(key);
};
if (val == null) {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
logger.warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
return "";
} else {
return Matcher.quoteReplacement(val);

View File

@@ -24,9 +24,12 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.geometry.NodeOrientation;
import java.nio.file.Path;
import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
public class Settings {
@@ -78,6 +81,7 @@ public class Settings {
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
public final StringProperty lastUpdateAttemptedByVersion;
public final ObservableSet<String> trustedHosts;
public static Settings create(SettingsProvider provider, Environment env) {
var defaults = new SettingsJson();
@@ -118,6 +122,7 @@ public class Settings {
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
this.trustedHosts = FXCollections.observableSet(json.trustedHosts);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -149,6 +154,7 @@ public class Settings {
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
trustedHosts.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -207,6 +213,7 @@ public class Settings {
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
json.trustedHosts = Set.copyOf(trustedHosts);
return json;
}

View File

@@ -4,17 +4,23 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class SettingsJson {
@JsonProperty("directories")
List<VaultSettingsJson> directories = List.of();
@JsonSetter(nulls = Nulls.AS_EMPTY)
List<VaultSettingsJson> directories = new ArrayList<>();
@JsonProperty("writtenByVersion")
String writtenByVersion;
@@ -99,4 +105,8 @@ class SettingsJson {
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
@JsonProperty("trustedHosts")
@JsonSetter(nulls = Nulls.AS_EMPTY)
Set<String> trustedHosts = new HashSet<>();
}

View File

@@ -27,6 +27,8 @@ import java.util.Set;
* <li>cryptomator.p12Path</li>
* <li>cryptomator.mountPointsDir</li>
* <li>cryptomator.disableUpdateCheck</li>
* <li>cryptomator.hub.allowedHosts</li>
* <li>cryptomator.hub.enableTrustOnFirstUse</li>
* </ul>
*
* @see Properties
@@ -42,7 +44,9 @@ class AdminPropertiesFactory {
"cryptomator.pluginDir", //
"cryptomator.p12Path", //
"cryptomator.mountPointsDir", //
"cryptomator.disableUpdateCheck");
"cryptomator.disableUpdateCheck", //
"cryptomator.hub.allowedHosts", //
"cryptomator.hub.enableTrustOnFirstUse");
/**

View File

@@ -36,7 +36,7 @@ public class Cryptomator {
static {
var adminProps = AdminPropertiesFactory.create();
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv(), EventualLogger.INSTANCE);
System.setProperties(lazyProcessedProps);
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
LOG = LoggerFactory.getLogger(Cryptomator.class);

View File

@@ -19,6 +19,7 @@ public enum FxmlFile {
HEALTH_START("/fxml/health_start.fxml"), //
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
HUB_CHECK_HOST_TRUST("/fxml/hub_check_host_trust.fxml"), //
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
@@ -29,6 +30,7 @@ public enum FxmlFile {
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
HUB_UNTRUSTED_HOST("/fxml/hub_untrusted_host.fxml"), //
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //

View File

@@ -1,17 +1,16 @@
package org.cryptomator.ui.common;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.application.HostServices;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import java.io.IOException;
@@ -28,12 +27,12 @@ public class VaultService {
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
private final Lazy<Application> application;
private final RevealPathService revealPathService;
private final ExecutorService executorService;
@Inject
public VaultService(Lazy<Application> application, ExecutorService executorService) {
this.application = application;
public VaultService(RevealPathService revealPathService, ExecutorService executorService) {
this.revealPathService = revealPathService;
this.executorService = executorService;
}
@@ -47,9 +46,9 @@ public class VaultService {
* @param vault The vault to reveal
*/
public Task<Vault> createRevealTask(Vault vault) {
Task<Vault> task = new RevealVaultTask(vault, application.get().getHostServices());
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
Task<Vault> task = new RevealVaultTask(vault, revealPathService);
task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName()));
task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException()));
return task;
}
@@ -110,19 +109,18 @@ public class VaultService {
private static class RevealVaultTask extends Task<Vault> {
private final Vault vault;
private final HostServices hostServices;
private final RevealPathService rs;
public RevealVaultTask(Vault vault, HostServices hostServices) {
public RevealVaultTask(Vault vault, RevealPathService revealPathService) {
this.vault = vault;
this.hostServices = hostServices;
setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException()));
this.rs = revealPathService;
}
@Override
protected Vault call() {
protected Vault call() throws RevealFailedException {
switch (vault.getMountPoint()) {
case null -> LOG.warn("Not currently mounted");
case Mountpoint.WithPath m -> hostServices.showDocument(m.uri().toString());
case Mountpoint.WithPath m -> rs.reveal(m.path());
case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI?
}
return vault;

View File

@@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.cryptofs.CryptoPath;
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.FxController;
@@ -46,7 +45,6 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
@@ -57,7 +55,6 @@ public class EventListCellController implements FxController {
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
private final FileSystemEventAggregator fileSystemEventAggregator;
@Nullable
private final RevealPathService revealService;
private final ResourceBundle resourceBundle;
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
@@ -82,15 +79,17 @@ public class EventListCellController implements FxController {
Button eventActionsButton;
@Inject
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator,
RevealPathService revealService,
ResourceBundle resourceBundle) {
this.fileSystemEventAggregator = fileSystemEventAggregator;
this.revealService = revealService.orElseGet(() -> null);
this.revealService = revealService;
this.resourceBundle = resourceBundle;
this.eventEntry = new SimpleObjectProperty<>(null);
this.eventMessage = new SimpleStringProperty();
this.eventDescription = new SimpleStringProperty();
this.eventIcon = new SimpleObjectProperty<>();
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1 ? "" : "(" + e.getValue().count() + ")", "");
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
@@ -136,13 +135,8 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
if (revealService != null) {
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
var user = userAndDevice[0];
@@ -156,11 +150,7 @@ public class EventListCellController implements FxController {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
}
@@ -168,46 +158,29 @@ public class EventListCellController implements FxController {
eventIcon.setValue(FontAwesome5Icon.CHECK);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else {
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
}
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
}
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
} else {
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
}
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
}
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
eventIcon.setValue(FontAwesome5Icon.BAN);
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
}
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
}
private void addLocalizedAction(String localizationKey, Runnable action) {
@@ -270,7 +243,7 @@ public class EventListCellController implements FxController {
}
var mountPoint = v.getMountPoint().uri().getPath();
if(SystemUtils.IS_OS_WINDOWS) {
if (SystemUtils.IS_OS_WINDOWS) {
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
}
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute

View File

@@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.application.Platform;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@FxApplicationScoped
public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
static final AtomicReference<Application> INSTANCE = new AtomicReference<>();
private final long startupTime;
private final Environment environment;
private final Settings settings;
@@ -33,7 +37,8 @@ public class FxApplication {
private final FxNotificationManager notificationManager;
@Inject
FxApplication(@Named("startupTime") long startupTime, //
FxApplication(Application fxApp,
@Named("startupTime") long startupTime, //
Environment environment, //
Settings settings, //
AppLaunchEventHandler launchEventHandler, //
@@ -55,6 +60,8 @@ public class FxApplication {
this.autoUnlocker = autoUnlocker;
this.fxFSEventList = fxFSEventList;
this.notificationManager = notificationManager;
INSTANCE.set(fxApp);
}
public void start() {

View File

@@ -64,17 +64,6 @@ abstract class FxApplicationModule {
return builder.build();
}
@Provides
@FxApplicationScoped
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
@@ -88,10 +77,4 @@ abstract class FxApplicationModule {
return factory.create();
}
@Provides
@FxApplicationScoped
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
return factory.create();
}
}

View File

@@ -39,6 +39,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
@FxApplicationScoped
public class FxApplicationWindows {
@@ -47,15 +48,15 @@ public class FxApplicationWindows {
private final Stage primaryStage;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> preferencesWindow;
private final CachedLazy<MainWindowComponent> mainWindow;
private final CachedLazy<PreferencesComponent> preferencesWindow;
private final QuitComponent.Builder quitWindowBuilder;
private final UnlockComponent.Factory unlockWorkflowFactory;
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final Lazy<EventViewComponent> eventViewWindow;
private final Lazy<NotificationComponent> notificationWindow;
private final CachedLazy<EventViewComponent> eventViewWindow;
private final CachedLazy<NotificationComponent> notificationWindow;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
@@ -65,8 +66,8 @@ public class FxApplicationWindows {
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
Optional<TrayIntegrationProvider> trayIntegration, //
Lazy<MainWindowComponent> mainWindow, //
Lazy<PreferencesComponent> preferencesWindow, //
MainWindowComponent.Builder mainWindowBuilder, //
PreferencesComponent.Builder preferencesWindowBuilder, //
QuitComponent.Builder quitWindowBuilder, //
UnlockComponent.Factory unlockWorkflowFactory, //
UpdateReminderComponent.Factory updateReminderWindowFactory, //
@@ -74,21 +75,21 @@ public class FxApplicationWindows {
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, //
Lazy<NotificationComponent> notificationWindow,
EventViewComponent.Factory eventViewWindowFactory, //
NotificationComponent.Factory notificationWindowFactory, //
ExecutorService executor, //
Dialogs dialogs) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.mainWindow = new CachedLazy<>(mainWindowBuilder::build);
this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build);
this.quitWindowBuilder = quitWindowBuilder;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.updateReminderWindowFactory = updateReminderWindowFactory;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create);
this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create);
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
@@ -218,4 +219,29 @@ public class FxApplicationWindows {
LOG.error("Failed to display stage", error);
}
}
private static class CachedLazy<T> implements Lazy<T> {
private final Supplier<T> supplier;
private volatile T instance = null;
public CachedLazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public T get() {
T value = instance;
if (value == null) {
synchronized (this) {
value = instance;
if (value == null) {
value = supplier.get();
instance = value;
}
}
}
return instance;
}
}
}

View File

@@ -0,0 +1,37 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.integrations.common.DisplayName;
import org.cryptomator.integrations.common.OperatingSystem;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import java.nio.file.Path;
/**
* A {@link RevealPathService} service implementation using the JavaFX {@link javafx.application.HostServices#showDocument(String)} to reveal documents.
* <p>
* Internally the HostServices class uses GTK on Linux.
*
* @implNote {@link #reveal(Path)} only succeeds when the class {@link FxApplication} is initialized.
*/
@DisplayName("JavaFX HostServices (GTK)")
@OperatingSystem(OperatingSystem.Value.LINUX)
@Priority(10)
public class JfxRevealPathService implements RevealPathService {
@Override
public void reveal(Path p) throws RevealFailedException {
var fxApp = FxApplication.INSTANCE.get();
if (fxApp != null) {
fxApp.getHostServices().showDocument(p.toUri().toString());
} else {
throw new RevealFailedException("JavaFX Application not initialized");
}
}
@Override
public boolean isSupported() {
return true;
}
}

View File

@@ -4,9 +4,12 @@ import com.google.common.base.Throwables;
import org.cryptomator.common.Environment;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
@@ -25,6 +28,7 @@ import java.util.stream.Collectors;
@HealthCheckScoped
public class ReportWriter {
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
private static final String REPORT_HEADER = """
*******************************************
* Cryptomator Vault Health Report *
@@ -43,14 +47,14 @@ public class ReportWriter {
private final Vault vault;
private final VaultConfig vaultConfig;
private final Application application;
private final RevealPathService revealPathService;
private final Path exportDestination;
@Inject
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, Application application, Environment env) {
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, RevealPathService revealPathService, Environment env) {
this.vault = vault;
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
this.application = application;
this.revealPathService = revealPathService;
this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log");
}
@@ -92,7 +96,11 @@ public class ReportWriter {
}
private void reveal() {
application.getHostServices().showDocument(exportDestination.getParent().toUri().toString());
try {
revealPathService.reveal(exportDestination.getParent());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal export destination location of report", e);
}
}
}

View File

@@ -1,6 +1,5 @@
package org.cryptomator.ui.keyloading.hub;
import com.nimbusds.jose.JWEObject;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
@@ -12,8 +11,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.WorkerStateEvent;

View File

@@ -0,0 +1,179 @@
package org.cryptomator.ui.keyloading.hub;
import dagger.Lazy;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import java.net.URI;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
public class CheckHostTrustController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CheckHostTrustController.class);
private static final String CHECK_KEY = "hub.checkHostTrust.message.check";
private static final String ASK_SINGULAR_KEY = "hub.checkHostTrust.message.ask";
private static final String ASK_PLURAL_KEY = "hub.checkHostTrust.message.ask.plural";
private static final String TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN = ".cryptomator.cloud";
private final Stage window;
private final HubConfig hubConfig;
private final URI canonicalHubUri;
private final URI canonicalAuthUri;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> untrustedHostScene;
private final CompletableFuture<ReceivedKey> result;
private final Settings settings;
private final Environment env;
private final ResourceBundle resourceBundle;
private final SortedSet<String> hostnames;
private final StringProperty messageLabel;
@FXML
private TextFlow hostnamesFlow;
@Inject
public CheckHostTrustController(@KeyLoading Stage window, //
HubConfig hubConfig, //
@FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, //
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST) Lazy<Scene> untrustedHostScene, //
CompletableFuture<ReceivedKey> result, //
Settings settings, //
Environment env, //
ResourceBundle resourceBundle) {
this.window = window;
this.hubConfig = hubConfig;
this.canonicalHubUri = hubConfig.getApiBaseUrl();
this.canonicalAuthUri = URI.create(hubConfig.authEndpoint);
this.authFlowScene = authFlowScene;
this.untrustedHostScene = untrustedHostScene;
this.result = result;
this.settings = settings;
this.env = env;
this.resourceBundle = resourceBundle;
this.hostnames = new TreeSet<>();
this.messageLabel = new SimpleStringProperty(resourceBundle.getString(CHECK_KEY));
}
@FXML
public void initialize() {
if (!isConsistentHubConfig()) {
LOG.warn("Inconsistent hub config detected. Denying access to protect the user.");
deny();
} else if (isAllCryptomatorCloud() && !isAnyHttpHost()) {
trust(); // trust *.cryptomator.cloud by default, domain is owned by Cryptomator maintainers
} else if (containsAllowedHosts(env.hubAllowedHosts())) {
trust(); // trust hosts explicitly allowlisted via system property
} else if (isAnyHttpHost() && !isAllLocalhost()) {
LOG.warn("Denying attempt to connect to hub instance via unencrypted HTTP.");
deny(); // never trust http hosts except for local testing
} else if (env.hubTrustOnFirstUse() && containsAllowedHosts(settings.trustedHosts)) {
trust(); // trust hosts previously allowlisted by the user
} else if (env.hubTrustOnFirstUse()) {
hostnames.add(getAuthority(canonicalHubUri));
hostnames.add(getAuthority(canonicalAuthUri));
renderHostnames(); // ask user whether to trust these hosts
} else {
LOG.warn("Cryptomator is not allowed to connect to {}. Check your {} config.", getAuthority(canonicalHubUri), Environment.HUB_ALLOWED_HOSTS_PROP_NAME);
deny();
}
}
@FXML
public void trust() {
settings.trustedHosts.addAll(hostnames);
Platform.runLater(() -> {
window.setScene(authFlowScene.get());
});
}
@FXML
public void deny() {
result.cancel(true);
Platform.runLater(() -> {
window.setScene(untrustedHostScene.get());
});
}
private void renderHostnames() {
hostnamesFlow.getChildren().clear();
for (var hostname : hostnames) {
hostnamesFlow.getChildren().add(new Text(hostname + System.lineSeparator()));
}
var messageKey = hostnames.size() > 1 ? ASK_PLURAL_KEY : ASK_SINGULAR_KEY;
messageLabel.set(resourceBundle.getString(messageKey));
}
private boolean isConsistentHubConfig() {
var canonicalHubAuthority = getAuthority(canonicalHubUri);
var canonicalAuthAuthority = getAuthority(canonicalAuthUri);
// apiBaseURL.host == deviceUrl.host == authSuccessUrl.host == authErrorUrl.host
return (hubConfig.apiBaseUrl == null || getAuthority(hubConfig.apiBaseUrl).equals(canonicalHubAuthority)) //
&& (hubConfig.devicesResourceUrl == null || getAuthority(hubConfig.devicesResourceUrl).equals(canonicalHubAuthority)) //
&& getAuthority(hubConfig.authSuccessUrl).equals(canonicalHubAuthority) //
&& getAuthority(hubConfig.authErrorUrl).equals(canonicalHubAuthority) //
// authUrl.host == tokenUrl.host:
&& getAuthority(hubConfig.tokenEndpoint).equals(canonicalAuthAuthority);
}
private boolean isAllCryptomatorCloud() {
return canonicalHubUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN) && canonicalAuthUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN);
}
private boolean isAnyHttpHost() {
return "http".equalsIgnoreCase(canonicalHubUri.getScheme()) || "http".equalsIgnoreCase(canonicalAuthUri.getScheme());
}
private boolean isAllLocalhost() {
return "localhost".equalsIgnoreCase(canonicalHubUri.getHost()) && "localhost".equalsIgnoreCase(canonicalAuthUri.getHost());
}
@VisibleForTesting
boolean containsAllowedHosts(Set<String> allowedHubHosts) {
return allowedHubHosts.contains(getAuthority(canonicalHubUri)) && allowedHubHosts.contains(getAuthority(canonicalAuthUri));
}
public static String getAuthority(String string) {
return getAuthority(URI.create(string));
}
public static String getAuthority(URI uri) {
if (uri.getPort() == -1) {
return "%s://%s".formatted(uri.getScheme(), uri.getHost());
} else {
return "%s://%s:%s".formatted(uri.getScheme(), uri.getHost(), uri.getPort());
}
}
//--- JavaFX property getter & setter
public StringProperty messageLabelProperty() {
return messageLabel;
}
public String getMessageLabel() {
return messageLabel.get();
}
}

View File

@@ -98,6 +98,13 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_NO_KEYCHAIN);
}
@Provides
@FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST)
@KeyLoadingScoped
static Scene provideHubCheckHostTrustScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_CHECK_HOST_TRUST);
}
@Provides
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
@KeyLoadingScoped
@@ -168,6 +175,13 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST)
@KeyLoadingScoped
static Scene provideHubUntrustedHostScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_UNTRUSTED_HOST);
}
@Provides
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
@KeyLoadingScoped
@@ -180,6 +194,11 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(NoKeychainController.class)
abstract FxController bindNoKeychainController(NoKeychainController controller);
@Binds
@IntoMap
@FxControllerKey(CheckHostTrustController.class)
abstract FxController bindCheckHostAuthenticityController(CheckHostTrustController controller);
@Binds
@IntoMap
@FxControllerKey(AuthFlowController.class)
@@ -225,6 +244,11 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(UnauthorizedDeviceController.class)
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(UntrustedHostController.class)
abstract FxController bindUnauthorizedHostController(UntrustedHostController controller);
@Binds
@IntoMap
@FxControllerKey(RequireAccountInitController.class)

View File

@@ -36,19 +36,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
private final Stage window;
private final KeychainManager keychainManager;
private final AtomicReference<String> fsOwnerId;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> checkHostTrustScene;
private final Lazy<Scene> noKeychainScene;
private final CompletableFuture<ReceivedKey> result;
private final DeviceKey deviceKey;
@Inject
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST) Lazy<Scene> checkHostTrustScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
this.window = window;
this.keychainManager = keychainManager;
this.fsOwnerId = fsOwnerId;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.authFlowScene = authFlowScene;
this.checkHostTrustScene = checkHostTrustScene;
this.noKeychainScene = noKeychainScene;
this.result = result;
this.deviceKey = deviceKey;
@@ -62,7 +62,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
throw new NoKeychainAccessProviderException();
}
var keypair = deviceKey.get();
showWindow(authFlowScene);
showWindow(checkHostTrustScene);
var jwe = result.get();
return jwe.decryptMasterkey(keypair.getPrivate());
} catch (NoKeychainAccessProviderException e) {

View File

@@ -0,0 +1,34 @@
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;
import javafx.stage.WindowEvent;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
public class UntrustedHostController implements FxController {
private final Stage window;
private final CompletableFuture<ReceivedKey> result;
@Inject
public UntrustedHostController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
this.window = window;
this.result = result;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@FXML
public void close() {
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
}

View File

@@ -1,6 +1,7 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import com.google.common.base.Preconditions;
import org.cryptomator.common.Constants;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
@@ -63,16 +64,21 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
window.setTitle(resourceBundle.getString("unlock.title").formatted(vault.getDisplayName()));
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
if (!Constants.MASTERKEY_FILENAME.equals(keyId.getSchemeSpecificPart())) {
LOG.warn("unsupported masterkey path found in vault.cryptomator: {}", keyId.getSchemeSpecificPart());
}
try {
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
// determine masterkey file path:
Path filePath = vault.getPath().resolve(Constants.MASTERKEY_FILENAME);
if (!Files.exists(filePath)) {
filePath = askUserForMasterkeyFilePath();
}
// unlock:
if (passphrase == null) {
askForPassphrase();
}
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
//backup
// backup on successful unlock:
if (filePath.startsWith(vault.getPath())) {
try {
BackupHelper.attemptBackup(filePath);

View File

@@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.AutoAnimator;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
@@ -19,10 +22,12 @@ import javafx.fxml.FXML;
@MainWindowScoped
public class VaultDetailController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class);
private final ReadOnlyObjectProperty<Vault> vault;
private final Application application;
private final ObservableValue<FontAwesome5Icon> glyph;
private final BooleanBinding anyVaultSelected;
private final RevealPathService revealPathService;
private AutoAnimator spinAnimation;
@@ -31,11 +36,11 @@ public class VaultDetailController implements FxController {
@Inject
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
VaultDetailController(ObjectProperty<Vault> vault, RevealPathService revealPathService) {
this.vault = vault;
this.application = application;
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
this.anyVaultSelected = vault.isNotNull();
this.revealPathService = revealPathService;
}
public void initialize() {
@@ -61,7 +66,11 @@ public class VaultDetailController implements FxController {
@FXML
public void revealStorageLocation() {
application.getHostServices().showDocument(vault.get().getPath().toUri().toString());
try {
revealPathService.reveal(vault.get().getPath());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal vault storage location", e);
}
}
/* Observable Properties */

View File

@@ -21,7 +21,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
@@ -31,7 +30,6 @@ import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.TransferMode;
import javafx.stage.FileChooser;
@@ -40,12 +38,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -60,7 +54,7 @@ public class VaultDetailUnlockedController implements FxController {
private final VaultService vaultService;
private final WrongFileAlertComponent.Builder wrongFileAlert;
private final Stage mainWindow;
private final Optional<RevealPathService> revealPathService;
private final RevealPathService revealPathService;
private final DecryptNameComponent.Factory decryptNameWindowFactory;
private final ResourceBundle resourceBundle;
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
@@ -85,7 +79,7 @@ public class VaultDetailUnlockedController implements FxController {
VaultStatisticsComponent.Builder vaultStatsBuilder, //
WrongFileAlertComponent.Builder wrongFileAlert, //
@MainWindow Stage mainWindow, //
Optional<RevealPathService> revealPathService, //
RevealPathService revealPathService, //
DecryptNameComponent.Factory decryptNameWindowFactory, //
ResourceBundle resourceBundle) {
this.vault = vault;
@@ -113,7 +107,7 @@ public class VaultDetailUnlockedController implements FxController {
public void initialize() {
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths));
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealPaths));
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
@@ -158,7 +152,7 @@ public class VaultDetailUnlockedController implements FxController {
if (cleartextFile != null) {
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
if (ciphertextPath != null) {
revealOrCopyPaths(List.of(ciphertextPath));
revealPaths(List.of(ciphertextPath));
}
}
}
@@ -190,34 +184,18 @@ public class VaultDetailUnlockedController implements FxController {
}
}
private void revealOrCopyPaths(List<Path> paths) {
revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> {
LOG.warn("No service provider to reveal files found.");
copyPathsToClipboard(paths);
});
}
private void revealPaths(RevealPathService service, List<Path> paths) {
private void revealPaths(List<Path> paths) {
paths.forEach(path -> {
try {
LOG.debug("Revealing {}", path);
service.reveal(path);
revealPathService.reveal(path);
} catch (RevealFailedException e) {
//TODO: show popup in ui
LOG.error("Revealing ciphertext file failed.", e);
}
});
}
private void copyPathsToClipboard(List<Path> paths) {
StringBuilder clipboardString = new StringBuilder();
paths.forEach(p -> clipboardString.append(p.toString()).append("\n"));
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, clipboardString.toString()));
ciphertextPathsCopied.setValue(true);
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
ciphertextPathsCopied.set(false);
});
}
private VaultStatisticsComponent buildVaultStats(Vault vault) {
return vaultStatsBuilder.vault(vault).build();
}

View File

@@ -10,16 +10,18 @@ import org.cryptomator.integrations.common.NamedServiceProvider;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ToggleGroup;
@@ -41,7 +43,7 @@ public class GeneralPreferencesController implements FxController {
private final Settings settings;
private final Optional<AutoStartProvider> autoStartProvider;
private final List<QuickAccessService> quickAccessServices;
private final Application application;
private final RevealPathService revealPathService;
private final Environment environment;
private final List<KeychainAccessProvider> keychainAccessProviders;
private final KeychainManager keychain;
@@ -55,14 +57,21 @@ public class GeneralPreferencesController implements FxController {
public CheckBox autoCloseVaultsCheckbox;
public CheckBox debugModeCheckbox;
public CheckBox autoStartCheckbox;
public Button resetTrustedHostsButton;
public ToggleGroup nodeOrientation;
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
@Inject
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, //
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
GeneralPreferencesController(@PreferencesWindow Stage window, //
Settings settings, //
Optional<AutoStartProvider> autoStartProvider, //
List<KeychainAccessProvider> keychainAccessProviders, //
KeychainManager keychain, //
RevealPathService revealPathService, //
Environment environment, //
FxApplicationWindows appWindows, //
ExecutorService backgroundExecutor) {
this.window = window;
this.settings = settings;
this.autoStartProvider = autoStartProvider;
@@ -70,7 +79,7 @@ public class GeneralPreferencesController implements FxController {
this.keychain = keychain;
this.backgroundExecutor = backgroundExecutor;
this.quickAccessServices = QuickAccessService.get().toList();
this.application = application;
this.revealPathService = revealPathService;
this.environment = environment;
this.appWindows = appWindows;
}
@@ -98,6 +107,9 @@ public class GeneralPreferencesController implements FxController {
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
if (resetTrustedHostsButton != null) {
resetTrustedHostsButton.disableProperty().bind(Bindings.isEmpty(settings.trustedHosts));
}
}
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
@@ -124,6 +136,10 @@ public class GeneralPreferencesController implements FxController {
return autoStartProvider.isPresent();
}
public boolean isHubTrustOnFirstUseEnabled() {
return environment.hubTrustOnFirstUse();
}
@FXML
public void toggleAutoStart() {
autoStartProvider.ifPresent(autoStart -> {
@@ -146,9 +162,18 @@ public class GeneralPreferencesController implements FxController {
return !quickAccessServices.isEmpty();
}
@FXML
public void resetTrustedHosts() {
settings.trustedHosts.clear();
}
@FXML
public void showLogfileDirectory() {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
try {
revealPathService.reveal(environment.getLogDir().orElseThrow());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal log files directory.", e);
}
}
/* Helper classes */
@@ -196,4 +221,5 @@ public class GeneralPreferencesController implements FxController {
}
}
}
}

View File

@@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.integrations.update.UpdateStep;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.updater.FallbackUpdateInfo;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.updater.UpdateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
@@ -49,7 +50,7 @@ public class UpdatesPreferencesController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
private final Application application;
private final RevealPathService revealPathService;
private final Environment environment;
private final ResourceBundle resourceBundle;
private final Settings settings;
@@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController {
public CheckBox checkForUpdatesCheckbox;
@Inject
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
this.application = application;
UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
this.revealPathService = revealPathService;
this.environment = environment;
this.resourceBundle = resourceBundle;
this.settings = settings;
@@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController {
updateService.setOnFailed(this::updateFailed);
}
@FXML
public void showLogfileDirectory() {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
try {
revealPathService.reveal(environment.getLogDir().orElseThrow());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal log files directory.", e);
}
}
@FXML
@@ -254,7 +260,7 @@ public class UpdatesPreferencesController implements FxController {
public boolean isProhibitUpdateWhileUnlocked() {
// If the result of the last update check was from the fallback mechanism, we don't need to show the warning
return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
return !unlockedVaults.isEmpty() && updateChecker.isUpdateAvailable() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
}
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.text.TextFlow?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.CheckHostTrustController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT"
accessibleRole="DIALOG">
<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="QUESTION" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="${controller.messageLabel}" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<TextFlow fx:id="hostnamesFlow" styleClass="text-flow" minHeight="60"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons>
<Button text="%hub.checkHostTrust.denyBtn" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#deny"/>
<Button text="%hub.checkHostTrust.trustBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#trust"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.Group?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?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.UntrustedHostController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT"
accessibleRole="DIALOG">
<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.untrustedHost.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%hub.untrustedHost.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>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Hyperlink?>
@@ -34,6 +35,8 @@
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
</HBox>
<Button fx:id="resetTrustedHostsButton" text="%preferences.general.resetTrustedHosts" visible="${controller.hubTrustOnFirstUseEnabled}" managed="${controller.hubTrustOnFirstUseEnabled}" onAction="#resetTrustedHosts"/>
<Region VBox.vgrow="ALWAYS"/>
<HBox spacing="12" alignment="CENTER_LEFT">

View File

@@ -162,6 +162,12 @@ unlock.error.title=Unlock "%s" failed
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.
hub.noKeychain.openBtn=Open Preferences
### Check Host Authenticity
hub.checkHostTrust.message.check=Checking Configuration…
hub.checkHostTrust.message.ask=Trust this host?
hub.checkHostTrust.message.ask.plural=Trust these hosts?
hub.checkHostTrust.trustBtn=Trust
hub.checkHostTrust.denyBtn=Deny
### Waiting
hub.auth.message=Waiting for authentication…
hub.auth.description=You should automatically be redirected to the login page.
@@ -193,6 +199,9 @@ hub.archived.description=This vault has been archived and is no longer accessibl
### Unauthorized
hub.unauthorized.message=Access denied
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
### Untrusted Host
hub.untrustedHost.message=Host not trusted
hub.untrustedHost.description=Connection to Hub was blocked for your security. If you believe the Hub host is safe, contact your Hub administrator or try again.
### Requires Account Initialization
hub.requireAccountInit.message=Action required
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
@@ -306,6 +315,7 @@ preferences.general.debugDirectory=Reveal log files
preferences.general.autoStart=Launch Cryptomator on system start
preferences.general.keychainBackend=Store passwords with
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
preferences.general.resetTrustedHosts=Reset trusted hosts
## Interface
preferences.interface=Interface
preferences.interface.theme=Look & Feel
@@ -696,27 +706,19 @@ eventView.cell.actionsButton.tooltip=Event actions
eventView.entry.vaultLocked.description=Unlock "%s" for details
eventView.entry.conflictResolved.message=Resolved conflict
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
eventView.entry.conflict.message=Conflict resolution failed
eventView.entry.conflict.showDecrypted=Show decrypted, original file
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
eventView.entry.decryptionFailed.message=Decryption failed
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
eventView.entry.brokenDirFile.message=Broken directory link
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
eventView.entry.brokenFileNode.message=Broken filesystem node
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
eventView.entry.inUse.message=File in use
eventView.entry.inUse.showDecrypted=Show decrypted file
eventView.entry.inUse.copyDecrypted=Copy decrypted path
eventView.entry.inUse.showEncrypted=Show encrypted file
eventView.entry.inUse.copyEncrypted=Copy encrypted path
eventView.entry.inUse.copyUserAndDevice=Copy locking user and device name
eventView.entry.inUse.ignoreLock=Ignore use status
@@ -725,4 +727,4 @@ eventView.entry.inUse.ignoreLock=Ignore use status
## FileIsInUse Notification
notification.inUse.message=File is in use on another device
notification.inUse.description=The file is open by %s on %s. Ask them to close the file and let synchronization finish. You can ignore the status to open it now, but this may cause conflicts or overwrite newer changes.
notification.inUse.action=Ignore Use Status
notification.inUse.action=Ignore Use Status

View File

@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=تفريغ القائمة
eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل
eventView.entry.conflictResolved.message=تم حل التضارب
eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر
eventView.entry.conflictResolved.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.conflict.message=فشل حل التضارب
eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي
eventView.entry.conflict.copyDecrypted=نسخ المسار غير المشفر والأصلي
eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر
eventView.entry.conflict.copyEncrypted=نسخ مسار التشفير المتعارض
eventView.entry.decryptionFailed.message=فشل فك التشفير
eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر
eventView.entry.decryptionFailed.copyEncrypted=نسخ مسار المشفر
eventView.entry.brokenDirFile.message=رابط الدليل المكسور
eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر
eventView.entry.brokenDirFile.copyEncrypted=نسخ مسار الرابط المكسور
eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة
eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة
eventView.entry.brokenFileNode.copyEncrypted=نسخ مسار العقدة المشفّرة التافلة
eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.inUse.showDecrypted=إظهار الملف غير المشفر
eventView.entry.inUse.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
eventView.entry.inUse.copyEncrypted=نسخ مسار المشفر
# Notifications

View File

@@ -18,6 +18,12 @@ generic.button.print=Отпечатване
generic.button.remove=Премахване
## Vault state
vault.state.locked=Заключено
vault.state.unlocked=Отключено
vault.state.missing=Липсващ
vault.state.migrationNeeded=Необходима е миграция
vault.state.processing=Обработване
vault.state.error=Грешка
# Error
error.message=Възникна грешка
@@ -98,6 +104,7 @@ addvault.new.readme.accessLocation.4=При желание можете да п
## Existing
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
addvaultwizard.existing.restore=Възстановяване…
addvaultwizard.existing.chooseBtn=Избиране…
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
@@ -145,6 +152,7 @@ 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.restartRequired.message=Хранилището не може да бъде отключено
unlock.error.title=Неуспешно отключване на „%s“
## Hub
hub.noKeychain.message=Няма достъп до ключа на устройството
@@ -369,6 +377,7 @@ main.vaultlist.contextMenu.unlock=Отключване…
main.vaultlist.contextMenu.unlockNow=Отключване сега
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
main.vaultlist.contextMenu.reveal=Разкриване на диска
main.vaultlist.contextMenu.share=Споделяне…
main.vaultlist.addVaultButton.tooltip=Добавяне на хранилище
##Notification
## Vault Detail

View File

@@ -269,6 +269,8 @@ health.check.detail.checkFinishedAndFound=Kontrolproceduren er kørt færdig. Ge
health.check.detail.checkFailed=Kontrolproceduren blev afbrudt af en fejl.
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
health.check.detail.listFilters.label=Filter
health.check.detail.filterSeverity=Filtrér efter sværhedsgrad
health.check.detail.filterFixState=Filtrér efter fix status
health.check.detail.fixAllSpecificBtn=Løs alle af type
health.check.exportBtn=Eksportér rapport
## Result view
@@ -358,6 +360,7 @@ preferences.contribute.promptText=Indsæt koden for supporter-certifikatet her
preferences.contribute.thankYou=Tak fordi du støtter Cryptomators open source-udvikling!
preferences.contribute.donate=Donér
preferences.contribute.sponsor=Sponsor
preferences.contribute.removeCert.tooltip=Fjern certifikat
### Remove License Key Dialog
removeCert.title=Fjern certifikat
@@ -367,6 +370,7 @@ removeCert.description=Cryptomators kernefunktioner påvirkes ikke af dette. Hve
## About
preferences.about=Om
preferences.about.thirdPartyLicenses=Tredjepartslicenser
# Vault Statistics
stats.title=Statistik for %s
@@ -406,6 +410,7 @@ stats.access.total=Samlede adgang: %d
# Main Window
## Vault List
main.vaultlist=Bokse
main.vaultlist.listEntry=Boks %s (%s)
main.vaultlist.emptyList.onboardingInstruction=Klik her for at tilføje en boks
main.vaultlist.contextMenu.remove=Fjern…
main.vaultlist.contextMenu.lock=Lås
@@ -419,12 +424,15 @@ main.vaultlist.addVaultBtn.menuItemExisting=Åbn eksisterende boks…
main.vaultlist.addVaultBtn.menuItemRecover=Genopret eksisterende boks…
main.vaultlist.addVaultButton.tooltip=Tilføj boks
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
main.vaultlist.showPreferencesButton.tooltip=Vis Indstillinger
##Notification
main.notification.updateAvailable=Opdatering er tilgængelig.
main.notification.support=Støt Cryptomator.
main.notification.closeButton.tooltip=Luk infobjælke
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
main.vaultDetail.storageLocation=Placering af boks
### Locked
main.vaultDetail.lockedStatus=LÅST
main.vaultDetail.unlockBtn=Lås op…
@@ -482,6 +490,7 @@ vaultOptions.general=Generelt
vaultOptions.general.vaultName=Boks-navn
vaultOptions.general.autoLock.lockAfterTimePart1=Lås efter inaktivitet i
vaultOptions.general.autoLock.lockAfterTimePart2=minutter
vaultOptions.general.autoLock.accessibleText=Lås timeout i minutter
vaultOptions.general.unlockAfterStartup=Lås boksen op når Cryptomator starter
vaultOptions.general.actionAfterUnlock=Efter oplåsning af boks
vaultOptions.general.actionAfterUnlock.ignore=Gør intet
@@ -512,6 +521,7 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Glem gemt adgangskode
vaultOptions.masterkey.recoveryKeyExplanation=En gendannelsesnøgle er den eneste måde du kan få adgang til din boks på, hvis du har glemt dit password.
vaultOptions.masterkey.showRecoveryKeyBtn=Vis gendannelsesnøgle
vaultOptions.masterkey.recoverPasswordBtn=Nulstil adgangskode
vaultOptions.masterkey.missingMasterkeyFile=Disse tilvalg er kun tilgængelige hvis masterkeyfilen er til stede i boksmappen.
## Hub
vaultOptions.hub=Gendannelse
vaultOptions.hub.convertInfo=Du kan bruge gendannelsesnøglen til at konvertere denne Hub-boks til en adgangskode-baseret boks i en nødsituation.
@@ -665,6 +675,8 @@ decryptNames.filePicker.title=Vælg krypteret fil
decryptNames.filePicker.extensionDescription=Cryptomator krypteret fil
decryptNames.copyTable.tooltip=Kopiér tabel
decryptNames.clearTable.tooltip=Ryd tabel
decryptNames.column.encrypted=Krypteret
decryptNames.column.decrypted=Dekrypteret
decryptNames.copyHint=Kopiér celleindhold med %s
decryptNames.dropZone.message=Slip filer eller klik for at vælge
decryptNames.dropZone.error.vaultInternalFiles=Boks interne filer med intet dekryptérbart navn valgt
@@ -677,34 +689,31 @@ decryptNames.dropZone.error.generic=Kunne ikke dekryptere filnavne
eventView.title=Begivenheder
eventView.filter.allVaults=Alle
eventView.clearListButton.tooltip=Ryd liste
eventView.filterVaults=Filtrér efter boks
eventView.cell.actionsButton.tooltip=Begivenhedshandlinger
## event list entries
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
eventView.entry.conflictResolved.message=Løst konflikt
eventView.entry.conflictResolved.showDecrypted=Vis dekrypteret fil
eventView.entry.conflictResolved.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.conflict.message=Konfliktløsning mislykkedes
eventView.entry.conflict.showDecrypted=Vis dekrypteret, original fil
eventView.entry.conflict.copyDecrypted=Kopier dekrypteret, original sti
eventView.entry.conflict.showEncrypted=Vis modstridende, krypteret fil
eventView.entry.conflict.copyEncrypted=Kopier modstridende, krypteret sti
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
eventView.entry.decryptionFailed.copyEncrypted=Kopiér krypteret sti
eventView.entry.brokenDirFile.message=Brudt mappelink
eventView.entry.brokenDirFile.showEncrypted=Vis brudt, krypteret link
eventView.entry.brokenDirFile.copyEncrypted=Kopiér sti til brudt link
eventView.entry.brokenFileNode.message=Brudt filsystem-node
eventView.entry.brokenFileNode.showEncrypted=Vis brudt, krypteret node
eventView.entry.brokenFileNode.copyEncrypted=Kopiér sti af brudt, krypteret node
eventView.entry.brokenFileNode.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.inUse.message=Fil i brug
eventView.entry.inUse.showDecrypted=Vis dekrypteret fil
eventView.entry.inUse.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.inUse.showEncrypted=Vis krypteret fil
eventView.entry.inUse.copyEncrypted=Kopiér krypteret sti
eventView.entry.inUse.copyUserAndDevice=Kopiér låsebruger og enhedsnavn
eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
# Notifications
## FileIsInUse Notification
notification.inUse.message=Filen er låst af en anden enhed
notification.inUse.description=Filen åbnes af %s på enheden %s. Bed brugeren om at lukke filen og synkronisere igen. Ellers kan du ignorere låsen og åbne den alligevel.
notification.inUse.action=Ignorér s
notification.inUse.message=Filen er i brug på en anden enhed
notification.inUse.description=Filen er åbnet af %s på %s. Bed dem om at lukke filen og lade synkroniseringen afslutte. Du kan ignorere status for at åbne den nu, men det kan forårsage konflikter eller overskrive nyere ændringer.
notification.inUse.action=Ignorér anvendelsesstatus

View File

@@ -684,32 +684,21 @@ eventView.cell.actionsButton.tooltip=Ereignisaktionen
eventView.entry.vaultLocked.description=Entsperre "%s" für Details
eventView.entry.conflictResolved.message=Konflikt gelöst
eventView.entry.conflictResolved.showDecrypted=Entschlüsselte Datei anzeigen
eventView.entry.conflictResolved.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.conflict.message=Konfliktlösung fehlgeschlagen
eventView.entry.conflict.showDecrypted=Entschlüsselte, ursprüngliche Datei anzeigen
eventView.entry.conflict.copyDecrypted=Entschlüsselten, ursprünglicher Pfad kopieren
eventView.entry.conflict.showEncrypted=Zeige verschlüsselte Konfliktdatei
eventView.entry.conflict.copyEncrypted=Verschlüsselten Konfliktpfad kopieren
eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen
eventView.entry.decryptionFailed.showEncrypted=Verschlüsselte Datei anzeigen
eventView.entry.decryptionFailed.copyEncrypted=Pfad der verschlüsselten Datei kopieren
eventView.entry.brokenDirFile.message=Ungültiger Verzeichnislink
eventView.entry.brokenDirFile.showEncrypted=Defekten, verschlüsselten Link anzeigen
eventView.entry.brokenDirFile.copyEncrypted=Pfad des ungültigen Links kopieren
eventView.entry.brokenFileNode.message=Beschädigter Dateisystemknoten
eventView.entry.brokenFileNode.showEncrypted=Beschädigten, verschlüsselten Knoten anzeigen
eventView.entry.brokenFileNode.copyEncrypted=Pfad des beschädigten, verschlüsselten Knotens kopieren
eventView.entry.brokenFileNode.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.inUse.message=Gesperrte Datei
eventView.entry.inUse.showDecrypted=Entschlüsselte Datei anzeigen
eventView.entry.inUse.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.inUse.showEncrypted=Verschlüsselte Datei anzeigen
eventView.entry.inUse.copyEncrypted=Pfad der verschlüsselten Datei kopieren
eventView.entry.inUse.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre kopieren
eventView.entry.inUse.ignoreLock=Sperre ignorieren
# Notifications
## FileIsInUse Notification
notification.inUse.message=Die Datei ist über ein anderes Gerät gesperrt
notification.inUse.action=Sperre ignorieren
notification.inUse.action=Verwendungsstatus ignorieren

View File

@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=Εκκαθάριση λίστας
eventView.entry.vaultLocked.description=Ξεκλείδωμα "%s" για λεπτομέρειες
eventView.entry.conflictResolved.message=Επίλυση σύγκρουσης
eventView.entry.conflictResolved.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
eventView.entry.conflictResolved.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.conflict.message=Η επίλυση συγκρούσεων απέτυχε
eventView.entry.conflict.showDecrypted=Εμφάνιση αποκρυπτογραφημένων, αρχικό αρχείο
eventView.entry.conflict.copyDecrypted=Αντιγραφή αποκρυπτογραφημένων, αρχική διαδρομή
eventView.entry.conflict.showEncrypted=Εμφάνιση αντικρουόμενου, κρυπτογραφημένο αρχείο
eventView.entry.conflict.copyEncrypted=Αντιγραφή σύγκρουσης, κρυπτογραφημένη διαδρομή
eventView.entry.decryptionFailed.message=Η αποκρυπτογράφηση απέτυχε
eventView.entry.decryptionFailed.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
eventView.entry.decryptionFailed.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
eventView.entry.brokenDirFile.message=Μη λειτουργικός σύνδεσμος καταλόγου
eventView.entry.brokenDirFile.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου συνδέσμου
eventView.entry.brokenDirFile.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού συνδέσμου
eventView.entry.brokenFileNode.message=Μη λειτουργικός κόμβος συστήματος αρχείων
eventView.entry.brokenFileNode.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου κόμβου
eventView.entry.brokenFileNode.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού, κρυπτογραφημένου κόμβου
eventView.entry.brokenFileNode.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.inUse.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
eventView.entry.inUse.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
eventView.entry.inUse.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
# Notifications

View File

@@ -684,27 +684,24 @@ eventView.cell.actionsButton.tooltip=Acciones del evento
eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles
eventView.entry.conflictResolved.message=Conflicto resuelto
eventView.entry.conflictResolved.showDecrypted=Mostrar archivo descifrado
eventView.entry.conflictResolved.copyDecrypted=Copiar ruta descifrada
eventView.entry.conflict.message=Resolución de conflictos fallida
eventView.entry.conflict.showDecrypted=Mostrar archivo descifrado, original
eventView.entry.conflict.copyDecrypted=Copiar ruta descifrada, original
eventView.entry.conflict.showEncrypted=Mostrar archivo en conflicto, cifrado
eventView.entry.conflict.copyEncrypted=Copiar ruta en conflicto, cifrada
eventView.entry.decryptionFailed.message=Desencriptación fallida
eventView.entry.decryptionFailed.showEncrypted=Mostrar archivo cifrado
eventView.entry.decryptionFailed.copyEncrypted=Copiar ruta cifrada
eventView.entry.brokenDirFile.message=Enlace de directorio roto
eventView.entry.brokenDirFile.showEncrypted=Mostrar enlace roto, cifrado
eventView.entry.brokenDirFile.copyEncrypted=Copiar ruta del enlace roto
eventView.entry.brokenFileNode.message=Nodo de sistema de archivos roto
eventView.entry.brokenFileNode.showEncrypted=Mostrar nodo roto, cifrado
eventView.entry.brokenFileNode.copyEncrypted=Copiar ruta del enlace roto, encriptado
eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada
eventView.entry.inUse.message=Fichero en uso
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
eventView.entry.inUse.copyDecrypted=Copiar ruta descifrada
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
eventView.entry.inUse.copyEncrypted=Copiar ruta cifrada
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
notification.inUse.message=El archivo está en uso en otro dispositivo
notification.inUse.description=El archivo está abierto por %s en %s. Solicítale que cierre el archivo y deje que finalice la sincronización. Puede ignorar el estado para abrirlo ahora, pero esto puede causar conflictos o sobrescribir cambios más recientes.
notification.inUse.action=Ignorar el Estado de Uso

View File

@@ -683,26 +683,18 @@ eventView.cell.actionsButton.tooltip=Toiminnot
eventView.entry.vaultLocked.description=Avaa "%s" lisätietoja varten
eventView.entry.conflictResolved.message=Ratkaistu ristiriita
eventView.entry.conflictResolved.showDecrypted=Näytä purettu tiedosto
eventView.entry.conflictResolved.copyDecrypted=Kopioi purettu polku
eventView.entry.conflict.message=Ristiriidan ratkaisu epäonnistui
eventView.entry.conflict.showDecrypted=Näytä purettu, alkuperäinen tiedosto
eventView.entry.conflict.copyDecrypted=Näytä purettu, alkuperäinen polku
eventView.entry.conflict.showEncrypted=Näytä ristiriitainen, salattu tiedosto
eventView.entry.conflict.copyEncrypted=Kopioi ristiriitainen, salattu polku
eventView.entry.decryptionFailed.message=Salauksen purku epäonnistui
eventView.entry.decryptionFailed.showEncrypted=Näytä salattu tiedosto
eventView.entry.decryptionFailed.copyEncrypted=Kopioi salattu polku
eventView.entry.brokenDirFile.message=Virheellinen hakemiston linkki
eventView.entry.brokenDirFile.showEncrypted=Näytä virheellinen, salattu linkki
eventView.entry.brokenDirFile.copyEncrypted=Kopioi virheellisen polun linkki
eventView.entry.brokenFileNode.message=Virheellinen tiedostojärjestelmän solmu
eventView.entry.brokenFileNode.showEncrypted=Näytä virheellinen, salattu solmu
eventView.entry.brokenFileNode.copyEncrypted=Kopioi virheellisen, salatun solmun polku
eventView.entry.brokenFileNode.copyDecrypted=Kopioi purettu polku
eventView.entry.inUse.showDecrypted=Näytä purettu tiedosto
eventView.entry.inUse.copyDecrypted=Kopioi purettu polku
eventView.entry.inUse.showEncrypted=Näytä salattu tiedosto
eventView.entry.inUse.copyEncrypted=Kopioi salattu polku
# Notifications

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Actions d'évènement
eventView.entry.vaultLocked.description=Déverrouillez "%s" pour plus de détails
eventView.entry.conflictResolved.message=Conflit résolu
eventView.entry.conflictResolved.showDecrypted=Afficher le fichier déchiffré
eventView.entry.conflictResolved.copyDecrypted=Copier le chemin déchiffré
eventView.entry.conflict.message=La résolution des conflits a échoué
eventView.entry.conflict.showDecrypted=Afficher le fichier original déchiffré
eventView.entry.conflict.copyDecrypted=Copier le chemin original déchiffré
eventView.entry.conflict.showEncrypted=Afficher le fichier chiffré en conflit
eventView.entry.conflict.copyEncrypted=Copier le chemin chiffré en conflit
eventView.entry.decryptionFailed.message=Le déchiffrement a échoué
eventView.entry.decryptionFailed.showEncrypted=Afficher le fichier chiffré
eventView.entry.decryptionFailed.copyEncrypted=Copier le chemin chiffré
eventView.entry.brokenDirFile.message=Lien de répertoire cassé
eventView.entry.brokenDirFile.showEncrypted=Afficher le lien chiffré cassé
eventView.entry.brokenDirFile.copyEncrypted=Copier le chemin du lien cassé
eventView.entry.brokenFileNode.message=Nœud de système de fichiers cassé
eventView.entry.brokenFileNode.showEncrypted=Afficher le nœud chiffré cassé
eventView.entry.brokenFileNode.copyEncrypted=Copier le chemin du nœud chiffré cassé
eventView.entry.brokenFileNode.copyDecrypted=Copier le chemin déchiffré
eventView.entry.inUse.message=Fichier verrouillé
eventView.entry.inUse.message=Fichier en cours dutilisation
eventView.entry.inUse.showDecrypted=Afficher le fichier déchiffré
eventView.entry.inUse.copyDecrypted=Copier le chemin déchiffré
eventView.entry.inUse.showEncrypted=Afficher le fichier chiffré
eventView.entry.inUse.copyEncrypted=Copier le chemin chiffré
eventView.entry.inUse.copyUserAndDevice=Copier le verrouillage de l'utilisateur et le nom de l'appareil
eventView.entry.inUse.ignoreLock=Ignorer le verrou
eventView.entry.inUse.ignoreLock=Ignorer le statut dutilisation
# Notifications
## FileIsInUse Notification
notification.inUse.message=Le fichier est verrouillé par un autre appareil
notification.inUse.description=Le fichier est ouvert par %s sur lappareil %s. Demandez à lutilisateur de fermer le fichier et synchronisez à nouveau. Autrement vous pouvez ignorer le verrouillage et ouvrir quand même le fichier.
notification.inUse.action=Ignorer le verrou
notification.inUse.message=Le fichier est utilisé sur un autre appareil
notification.inUse.description=Le fichier est ouvert par %s sur %s. Demandez-lui à fermer le fichier et laissez la synchronisation se terminer. Vous pouvez ignorer le statut pour louvrir maintenant, mais cela pourrait entraîner des conflits ou écraser des modifications récentes.
notification.inUse.action=Ignorer le statut dutilisation

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Azioni dell'evento
eventView.entry.vaultLocked.description=Sblocca "%s" per i dettagli
eventView.entry.conflictResolved.message=Conflitto risolto
eventView.entry.conflictResolved.showDecrypted=Mostra file decifrato
eventView.entry.conflictResolved.copyDecrypted=Copia percorso decriptato
eventView.entry.conflict.message=Risoluzione dei conflitti fallita
eventView.entry.conflict.showDecrypted=Mostra il file originale decifrato
eventView.entry.conflict.copyDecrypted=Copia il percorso originale decifrato
eventView.entry.conflict.showEncrypted=Mostra file cifrato in conflitto
eventView.entry.conflict.copyEncrypted=Copia il percorso cifrato in conflitto
eventView.entry.decryptionFailed.message=Decifratura fallita
eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato
eventView.entry.decryptionFailed.copyEncrypted=Copia percorso cifrato
eventView.entry.brokenDirFile.message=Collegamento directory errato
eventView.entry.brokenDirFile.showEncrypted=Visualizza il link cifrato errato
eventView.entry.brokenDirFile.copyEncrypted=Copia il percorso errato del file cifrato
eventView.entry.brokenFileNode.message=Nodo file errato
eventView.entry.brokenFileNode.showEncrypted=Visualizza il nodo file cifrato errato
eventView.entry.brokenFileNode.copyEncrypted=Copia il percorso errato del nodo file cifrato
eventView.entry.brokenFileNode.copyDecrypted=Copia il percorso decifrato
eventView.entry.inUse.message=File bloccato
eventView.entry.inUse.message=File in uso
eventView.entry.inUse.showDecrypted=Mostra file decifrato
eventView.entry.inUse.copyDecrypted=Copia percorso decriptato
eventView.entry.inUse.showEncrypted=Mostra file cifrato
eventView.entry.inUse.copyEncrypted=Copia percorso cifrato
eventView.entry.inUse.copyUserAndDevice=Copia il nome del dispositivo e dell'utente che sta bloccando
eventView.entry.inUse.ignoreLock=Ignora blocco
eventView.entry.inUse.ignoreLock=Ignora lo stato di utilizzo
# Notifications
## FileIsInUse Notification
notification.inUse.message=Il file è bloccato da un altro dispositivo
notification.inUse.description=Il file è aperto da %s sul dispositivo %s. Chiedi all'utente di chiudere il file e sincronizzalo di nuovo. In caso contrario, puoi ignorare il blocco e aprirlo comunque.
notification.inUse.action=Ignora blocco
notification.inUse.message=Il file è in uso su un altro dispositivo
notification.inUse.description=Il file è aperto da %s su %s. Chiedigli di chiudere il file e permettere che la sincronizzazione termini. È possibile ignorare lo stato ed aprirlo ora, ma questo può causare conflitti o la sovrascrittura delle modifiche più recenti.
notification.inUse.action=Ignora lo Stato di Utilizzo

View File

@@ -626,16 +626,10 @@ decryptNames.dropZone.error.generic=ファイル名の復号化に失敗しま
# Event View
## event list entries
eventView.entry.inUse.message=ロックされたファイル
eventView.entry.inUse.showDecrypted=復号化されたファイルを表示
eventView.entry.inUse.copyDecrypted=復号化されたパスをコピー
eventView.entry.inUse.showEncrypted=暗号化されたファイルを表示
eventView.entry.inUse.copyEncrypted=暗号化されたパスをコピー
eventView.entry.inUse.copyUserAndDevice=ロックしているユーザーとデバイス名をコピー
eventView.entry.inUse.ignoreLock=ロックを無視する
# Notifications
## FileIsInUse Notification
notification.inUse.message=ファイルは別のデバイスによってロックされています
notification.inUse.action=ロックを無視する
## FileIsInUse Notification

View File

@@ -42,7 +42,7 @@ defaults.vault.vaultName=Vault
# Tray Menu
traymenu.showMainWindow=보기
traymenu.showPreferencesWindow=환경설정
traymenu.showPreferencesWindow=환경 설정
traymenu.lockAllVaults=모두 잠그기
traymenu.quitApplication=종료
traymenu.vault.unlock=잠금 해제
@@ -114,7 +114,7 @@ addvaultwizard.success.nextStepsInstructions="%s" Vault가 추가되었습니다
addvaultwizard.success.unlockNow=지금 잠금 해제
# Remove Vault
removeVault.title=Vault 제거
removeVault.title=Vault "%s" 제거
removeVault.message=Vault를 삭제하시겠습니까?
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
@@ -132,18 +132,18 @@ forgetPassword.confirmBtn=비밀번호 삭제
# Unlock
unlock.title="%s" 잠금 해제
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
unlock.savePassword=비밀번호 기억
unlock.savePassword=비밀번호 기억하기
unlock.unlockBtn=잠금 해제
## Select
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
unlock.chooseMasterkey.description=이 Vault의 Masterkey를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시오.
unlock.chooseMasterkey.description= "%s" Vault의 마스터키를 찾지 못했습니다. 마스터키 위치를 수동으로 선택하여 주십시오.
unlock.chooseMasterkey.restoreInstead=대신 마스터키 파일 복구
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
## Success
unlock.success.message=잠금 해제 성공
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음
unlock.success.rememberChoice=선택 기억하 다시 묻지 않음
unlock.success.revealBtn=드라이브 표시
## Failure
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
@@ -166,7 +166,7 @@ hub.auth.message=인증 대기 중…
hub.auth.description=자동으로 로그인 페이지로 리다이렉트 될 것입니다.
hub.auth.loginLink=수동으로 열려면 클릭하십시오.
### Receive Key
hub.receive.message=응답 처리중…
hub.receive.message=응답 처리 중…
hub.receive.description=Hub로부터 응답을 처리하고 있습니다. 잠시만 기다려 주십시오.
### Register Device
hub.register.message=새 기기
@@ -181,7 +181,7 @@ hub.register.legacy.description=이 기기로부터 첫번째 Hub 접근입니
hub.registerSuccess.message=기기 등록됨
hub.registerSuccess.description=등록에 성공하였습니다. Vault를 잠금 해제할 수 있습니다.
hub.registerSuccess.unlockBtn=잠금 해제
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유가 추가적으로 허가해야 합니다.
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유가 추가적으로 허가해야 합니다.
### Registration Failed
hub.registerFailed.message=기기 등록 실패
hub.registerFailed.description.generic=등록 중에 오류가 발생했습니다. 앱 로그에서 자세한 정보를 확인할 수 있습니다.
@@ -209,18 +209,18 @@ lock.forced.retryBtn=재시도
lock.forced.forceBtn=강제 잠금
## Failure
lock.fail.message=Vault 잠금에 실패하였습니다.
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시오.
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료하십시오.
# Migration
migration.title=Vault 업그레이드
## Start
migration.start.header=Vault 업그레이드
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 Vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
migration.start.remarkUndone=이 업그레이드는 되돌릴 수 없습니다.
migration.start.remarkVersions=과거 버전의 Cryptomator는 업그레이드된 Vault를 열 수 없습니다.
migration.start.remarkCanRun=이 Vault를 열 때 사용하는 모든 기기가 현재 버전의 Cryptomator를 실행할 수 있는지 확인해야 합니다.
migration.start.remarkSynced=업그레이드하기 전에 해당 Vault가 모든 기기에 정상적으로 동기화되어야 합니다.
migration.start.confirm=나는 위 정보를 읽고 정말 이해했습니다.
migration.start.confirm=위 내용을 충분히 숙지하였음을 확인합니다.
## Run
migration.run.enterPassword="%s"의 비밀번호를 입력하십시오.
migration.run.startMigrationBtn=Vault 마이그레이션
@@ -231,8 +231,8 @@ migration.success.unlockNow=지금 잠금 해제
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일 시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=너무 긴 파일 이름을 파일 시스템에서 지원하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=너무 긴 경로를 파일 시스템에서 지원하지 않습니다.
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
@@ -267,7 +267,7 @@ health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
health.check.detail.checkFailed=오류로 인해 검사가 종료되었습니다.
health.check.detail.checkCancelled=검사가 취소되었습니다
health.check.detail.checkCancelled=검사가 취소되었습니다.
health.check.detail.listFilters.label=필터
health.check.detail.filterSeverity=중요도로 정렬
health.check.detail.filterFixState=해결 상태로 정렬
@@ -286,7 +286,7 @@ health.result.severityTip.crit=상태: 심각\nVault 구조가 손상되었습
health.result.fixStateFilter.all=모든 문제 해결 상태
health.result.fixStateFilter.fixable=문제 해결 가능
health.result.fixStateFilter.notFixable=문제 해결 불가
health.result.fixStateFilter.fixing=문제 해결중…
health.result.fixStateFilter.fixing=문제 해결 중…
health.result.fixStateFilter.fixed=문제 해결됨
health.result.fixStateFilter.fixFailed=문제 해결 실패
## Fix Application
@@ -295,7 +295,7 @@ health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오.
# Preferences
preferences.title=환경설정
preferences.title=환경 설정
## General
preferences.general=일반
preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김
@@ -309,9 +309,9 @@ preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에
preferences.interface=인터페이스
preferences.interface.theme=테마
preferences.interface.theme.automatic=자동
preferences.interface.theme.dark=어둡게
preferences.interface.theme.light=밝게
preferences.interface.unlockThemes=다크모드 해제
preferences.interface.theme.dark=다크 모드
preferences.interface.theme.light=라이트 모드
preferences.interface.unlockThemes=다크 모드 사용 권한을 얻어보세요!
preferences.interface.language=언어 (재시작 필요)
preferences.interface.language.auto=시스템 기본 설정
preferences.interface.interfaceOrientation=인터페이스 방향
@@ -356,7 +356,7 @@ preferences.contribute=후원하기
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
preferences.contribute.promptText=후원자 인증 코드를 여기에 붙여넣기
preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다!
preferences.contribute.donate=후원하기
preferences.contribute.sponsor=스폰서
@@ -364,7 +364,7 @@ preferences.contribute.removeCert.tooltip=인증서 제거
### Remove License Key Dialog
removeCert.title=인증서 제거
removeCert.message=서포터 인증서를 제거하시겠습니까?
removeCert.message=후원자 인증서를 제거하시겠습니까?
removeCert.description=Cryptomator의 핵심 기능은 영향을 받지 않습니다. Vault에 대한 접근이 제한되거나 보안이 약화되지 않습니다.
#<-- Add entries for donations and code/translation/documentation contribution -->
@@ -392,7 +392,7 @@ stats.read.accessCount=총 읽기 횟수: %d
stats.write.throughput.idle=쓰기: 대기 중
stats.write.throughput.kibs=쓰기: %.2f KiB/s
stats.write.throughput.mibs=쓰기: %.2f MiB/s
stats.write.total.data.none=데이터 기록됨: -
stats.write.total.data.none=데이터 기: -
stats.write.total.data.kib=데이터 쓰기: %.1f KiB
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
@@ -427,11 +427,11 @@ main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기
main.vaultlist.showPreferencesButton.tooltip=환경 설정 표시
##Notification
main.notification.updateAvailable=업데이트가 있습니다.
main.notification.support=Cryptomator 지원하기.
main.notification.support=Cryptomator 지원해 주세요
main.notification.closeButton.tooltip=정보 표시줄 닫기
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=파일 보호하기 위해 Cryptomator를 선택해주셔서 감사합니다. 만약 다른 도움이 필요하시면, 시작 안내서를 참조하시기 바랍니다.
main.vaultDetail.welcomeOnboarding=파일 보호 위해 Cryptomator를 선택해 주셔서 감사합니다. 도움이 필요하시면 시작 가이드를 확인해 주십시오:
main.vaultDetail.storageLocation=Vault 저장 위치
### Locked
main.vaultDetail.lockedStatus=잠김
@@ -695,32 +695,25 @@ eventView.cell.actionsButton.tooltip=이벤트별 동작
eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기
eventView.entry.conflictResolved.message=해결된 충돌
eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기
eventView.entry.conflictResolved.copyDecrypted=복호화된 경로 복사하기
eventView.entry.conflict.message=충돌 해결 실패
eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기
eventView.entry.conflict.copyDecrypted=복호화된 원본 경로 복사하기
eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기
eventView.entry.conflict.copyEncrypted=충돌하는 암호화된 경로 복사하기
eventView.entry.decryptionFailed.message=복호화 실패
eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기
eventView.entry.decryptionFailed.copyEncrypted=암호화된 경로 복사하기
eventView.entry.brokenDirFile.message=망가진 디렉터리 링크
eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기
eventView.entry.brokenDirFile.copyEncrypted=망가진 링크의 경로 복사하리
eventView.entry.brokenFileNode.message=망가진 파일시스템 노드
eventView.entry.brokenFileNode.message=망가진 파일 시스템 노드
eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기
eventView.entry.brokenFileNode.copyEncrypted=망가진 암호화된 노드의 경로 복사하기
eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기
eventView.entry.inUse.message=잠긴 파일
eventView.entry.inUse.message=파일 사용 중
eventView.entry.inUse.showDecrypted=복호화된 파일 보기
eventView.entry.inUse.copyDecrypted=복호화된 경로 복사하기
eventView.entry.inUse.showEncrypted=암호화된 파일 보기
eventView.entry.inUse.copyEncrypted=암호화된 경로 복사하기
eventView.entry.inUse.copyUserAndDevice=파일 잠금 사용자 및 기기 이름 복사
eventView.entry.inUse.ignoreLock=잠금 무시
eventView.entry.inUse.ignoreLock=사용 여부 상태 무시
# Notifications
## FileIsInUse Notification
notification.inUse.message=다른 기기에서 파일이 잠겼습니다.
notification.inUse.action=잠금 무시
notification.inUse.message=다른 기기에서 파일 사용 중
notification.inUse.description=해당 파일은 %s이(가) %s의 컴퓨터에서 열어 놓은 상태입니다. 파일을 닫고 동기화가 완료될 때까지 기다리도록 요청하세요. 상태를 무시하고 지금 파일을 열 수도 있지만, 이렇게 하면 충돌이 발생하거나 최신 변경 사항이 덮어쓰여질 수 있습니다.
notification.inUse.action=사용 여부 상태 무시

View File

@@ -694,31 +694,25 @@ eventView.cell.actionsButton.tooltip=Notikumu darbības
eventView.entry.vaultLocked.description=Atslēgt "%s", lai redzētu informāciju
eventView.entry.conflictResolved.message=Atrisināta nesaderība
eventView.entry.conflictResolved.showDecrypted=Parādīt atšifrēto datni
eventView.entry.conflictResolved.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.conflict.message=Nesaderības atrisināšana neizdevās
eventView.entry.conflict.showDecrypted=Parādīt atšifrēto, sākotnējo datni
eventView.entry.conflict.copyDecrypted=Ievietot starpliktuvē atšifrēto, sākotnējo datni
eventView.entry.conflict.showEncrypted=Parādīt nesaderīgo, šifrēto datni
eventView.entry.conflict.copyEncrypted=Ievietot starpliktuvē nesaderīgo, šifrēto ceļu
eventView.entry.decryptionFailed.message=Atšifrēšana neizdevās
eventView.entry.decryptionFailed.showEncrypted=Parādīt šifrēto datni
eventView.entry.decryptionFailed.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
eventView.entry.brokenDirFile.message=Bojāta mapes saite
eventView.entry.brokenDirFile.showEncrypted=Parādīt bojāto, šifrēto saiti
eventView.entry.brokenDirFile.copyEncrypted=Ievietot starpliktuvē bojātās saites ceļu
eventView.entry.brokenFileNode.message=Bojāts datņu sistēmas mezgls
eventView.entry.brokenFileNode.showEncrypted=Parādīt bojāto, šifrēto mezglu
eventView.entry.brokenFileNode.copyEncrypted=Ievietot starpliktuvē botjātā, šifrētā mezgla ceļu
eventView.entry.brokenFileNode.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.inUse.message=Aizslēgta datne
eventView.entry.inUse.message=Datne tiek izmantota
eventView.entry.inUse.showDecrypted=Parādīt atšifrēto datni
eventView.entry.inUse.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.inUse.showEncrypted=Parādīt šifrēto datni
eventView.entry.inUse.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
eventView.entry.inUse.copyUserAndDevice=Ievietot starpliktuvē lietotāju un ierīces, kurā aizslēgta, nosaukumu
eventView.entry.inUse.ignoreLock=Neņemt vērā slēgu
eventView.entry.inUse.ignoreLock=Neņemt vēra izmantošanas stāvokli
# Notifications
## FileIsInUse Notification
notification.inUse.action=Neņemt vērā slēgu
notification.inUse.message=Datne tiek izmantota citā ierīcē
notification.inUse.description=Datni atvēra %s ierīcē %s. Jāvaicā aizvērt datni un ļaut pabeigties sinhronizēšanai. Stāvokli tagad var neņemt vērā, lai atvērtu datni, bet tas var radīt nesaderības vai pārrakstīt jaunākas izmaiņas.
notification.inUse.action=Neņemt vēra izmantošanas stāvokli

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Acties op Gebeurtenissen
eventView.entry.vaultLocked.description=Ontgrendel "%s" voor details
eventView.entry.conflictResolved.message=Opgelost conflict
eventView.entry.conflictResolved.showDecrypted=Toon gedecodeerd bestand
eventView.entry.conflictResolved.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.conflict.message=Conflictoplossing is mislukt
eventView.entry.conflict.showDecrypted=Toon gedecodeerd, origineel bestand
eventView.entry.conflict.copyDecrypted=Kopieer gedecodeerd, origineel pad
eventView.entry.conflict.showEncrypted=Conflicterend, versleuteld bestand weergeven
eventView.entry.conflict.copyEncrypted=Conflicterend, versleuteld pad kopiëren
eventView.entry.decryptionFailed.message=Decodering mislukt
eventView.entry.decryptionFailed.showEncrypted=Toon gedecodeerd bestand
eventView.entry.decryptionFailed.copyEncrypted=Kopieer gedecodeerd pad
eventView.entry.brokenDirFile.message=Verbroken directory link
eventView.entry.brokenDirFile.showEncrypted=Toon verbroken gecodeerde link
eventView.entry.brokenDirFile.copyEncrypted=Kopieer pad van verbroken link
eventView.entry.brokenFileNode.message=Kapot bestandssysteemknooppunt
eventView.entry.brokenFileNode.showEncrypted=Toon verbroken gecodeerde link
eventView.entry.brokenFileNode.copyEncrypted=Kopieer pad van verbroken, versleuteld knooppunt
eventView.entry.brokenFileNode.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.message=Vergrendeld Bestand
eventView.entry.inUse.message=Bestand in gebruik
eventView.entry.inUse.showDecrypted=Toon gedecodeerd bestand
eventView.entry.inUse.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.showEncrypted=Toon gedecodeerd bestand
eventView.entry.inUse.copyEncrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.copyUserAndDevice=Kopieer vergrendeling gebruikersnaam en apparaatnaam
eventView.entry.inUse.ignoreLock=Vergrendeling negeren
eventView.entry.inUse.ignoreLock=Gebruiksstatus negeren
# Notifications
## FileIsInUse Notification
notification.inUse.message=Bestand is vergrendeld door een ander apparaat
notification.inUse.description=Het bestand wordt geopend door %s op apparaat %s. Vraag de gebruiker om het bestand te sluiten en opnieuw te synchroniseren. Anders kunt u de vergrendeling negeren en het toch openen.
notification.inUse.action=Vergrendeling negeren
notification.inUse.message=Bestand is op een ander apparaat in gebruik
notification.inUse.description=Het bestand is geopend door %s op %s. Vraag ze om het bestand te sluiten en laat de synchronisatie eindigen. Je kunt de status negeren om het nu te openen, maar dit kan conflicten veroorzaken of nieuwere wijzigingen overschrijven.
notification.inUse.action=Gebruiksstatus negeren

View File

@@ -573,17 +573,13 @@ eventView.clearListButton.tooltip=ਸੂਚੀ ਨੂੰ ਮਿਟਾਓ
## event list entries
eventView.entry.vaultLocked.description=ਵੇਰਵਿਆਂ ਲਈ "%s" ਨੂੰ ਅਣ-ਲਾਕ ਕਰੋ
eventView.entry.conflictResolved.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.conflictResolved.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.decryptionFailed.message=ਡਿਕ੍ਰਿਪਟ ਕਰਨਾ ਅਸਫ਼ਲ ਹੈ
eventView.entry.decryptionFailed.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.decryptionFailed.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.brokenDirFile.message=ਡਾਇਰੈਕਟਰੀ ਲਿੰਕ ਖ਼ਰਾਬ ਹੈ
eventView.entry.brokenDirFile.showEncrypted=ਖ਼ਰਾਬ, ਇੰੰਕ੍ਰਿਪਟ ਕੀਤੇ ਲਿੰਕ ਨੂੰ ਵੇਖਾਓ
eventView.entry.brokenFileNode.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.inUse.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.inUse.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.inUse.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.inUse.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
# Notifications

View File

@@ -695,31 +695,20 @@ eventView.cell.actionsButton.tooltip=Akcje zdarzeń
eventView.entry.vaultLocked.description=Odblokuj "%s" po szczegóły
eventView.entry.conflictResolved.message=Konflikt rozwiązany
eventView.entry.conflictResolved.showDecrypted=Pokaż odszyfrowany plik
eventView.entry.conflictResolved.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.conflict.message=Rozwiązywanie konfliktu nie powiodło się
eventView.entry.conflict.showDecrypted=Pokaż odszyfrowany, oryginalny plik
eventView.entry.conflict.copyDecrypted=Kopiuj odszyfrowaną, oryginalną ścieżkę
eventView.entry.conflict.showEncrypted=Pokaż sprzeczny, zaszyfrowany plik
eventView.entry.conflict.copyEncrypted=Kopiuj sprzeczną, zaszyfrowaną ścieżkę
eventView.entry.decryptionFailed.message=Odszyfrowywanie nie powiodło się
eventView.entry.decryptionFailed.showEncrypted=Pokaż zaszyfrowany plik
eventView.entry.decryptionFailed.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
eventView.entry.brokenDirFile.message=Uszkodzony link do katalogu
eventView.entry.brokenDirFile.showEncrypted=Pokaż uszkodzony, zaszyfrowany link
eventView.entry.brokenDirFile.copyEncrypted=Kopiuj ścieżkę uszkodzonego linku
eventView.entry.brokenFileNode.message=Uszkodzony węzeł systemu plików
eventView.entry.brokenFileNode.showEncrypted=Pokaż uszkodzony, zaszyfrowany węzeł
eventView.entry.brokenFileNode.copyEncrypted=Kopiuj ścieżkę uszkodzonego, zaszyfrowanego węzła
eventView.entry.brokenFileNode.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.inUse.message=Zablokowany plik
eventView.entry.inUse.showDecrypted=Pokaż odszyfrowany plik
eventView.entry.inUse.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.inUse.showEncrypted=Pokaż zaszyfrowany plik
eventView.entry.inUse.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
eventView.entry.inUse.copyUserAndDevice=Kopiowanie blokady użytkownika i nazwy urządzenia
eventView.entry.inUse.ignoreLock=Ignoruj blokadę
# Notifications
## FileIsInUse Notification
notification.inUse.action=Ignoruj blokadę
## FileIsInUse Notification

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Ações dos eventos
eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes
eventView.entry.conflictResolved.message=Conflito resolvido
eventView.entry.conflictResolved.showDecrypted=Mostrar o ficheiro desencriptado
eventView.entry.conflictResolved.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.conflict.message=A resolução do conflito falhou
eventView.entry.conflict.showDecrypted=Mostrar o ficheiro original desencriptado
eventView.entry.conflict.copyDecrypted=Copie o caminho original desencriptado
eventView.entry.conflict.showEncrypted=Mostrar o ficheiro encriptado em conflito
eventView.entry.conflict.copyEncrypted=Copiar o caminho encriptado em conflito
eventView.entry.decryptionFailed.message=A desencriptação falhou
eventView.entry.decryptionFailed.showEncrypted=Mostrar ficheiro encriptado
eventView.entry.decryptionFailed.copyEncrypted=Copiar o caminho encriptado
eventView.entry.brokenDirFile.message=Link de diretório quebrado
eventView.entry.brokenDirFile.showEncrypted=Mostrar o link quebrado e encriptado
eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado
eventView.entry.brokenFileNode.message=Nó do sistema de ficheiros avariado
eventView.entry.brokenFileNode.showEncrypted=Mostrar nó encriptado e danificado
eventView.entry.brokenFileNode.copyEncrypted=Copiar o caminho do nó encriptado e danificado
eventView.entry.brokenFileNode.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.inUse.message=Ficheiro bloqueado
eventView.entry.inUse.message=Ficheiro em utilização
eventView.entry.inUse.showDecrypted=Mostrar o ficheiro desencriptado
eventView.entry.inUse.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.inUse.showEncrypted=Mostrar ficheiro encriptado
eventView.entry.inUse.copyEncrypted=Copiar o caminho encriptado
eventView.entry.inUse.copyUserAndDevice=Copiar nome do utilizador e dispositivo bloqueado
eventView.entry.inUse.ignoreLock=Ignorar bloqueio
eventView.entry.inUse.ignoreLock=Ignorar estado de utilização
# Notifications
## FileIsInUse Notification
notification.inUse.message=O ficheiro está bloqueado por outro dispositivo
notification.inUse.description=O ficheiro foi aberto por %s no dispositivo %s. Peça ao utilizador para fechar o ficheiro e sincronizar novamente. Caso contrário, pode ignorar o bloqueio e abri-lo na mesma.
notification.inUse.action=Ignorar bloqueio
notification.inUse.message=O ficheiro está em utilização noutro dispositivo
notification.inUse.description=O ficheiro está aberto por %s em %s. Peça-lhes que fechem o ficheiro e deixem a sincronização terminar. Pode ignorar o estado para o abrir agora, mas isso pode causar conflitos ou sobrescrever alterações mais recentes.
notification.inUse.action=Ignorar estado de utilização

View File

@@ -27,15 +27,15 @@ vault.state.error=Erro
# Error
error.message=Ocorreu um erro
error.description=O Cryptomator não esperava que isso acontecesse. Você pode procurar soluções existentes para este erro. Ou, se ainda não foi relatado, sinta-se à vontade para fazê-lo.
error.hyperlink.lookup=Procurar este erro
error.description=O Cryptomator não esperava que isso acontecesse. Você pode buscar soluções existentes para esse erro. Se ainda não o relatou, sinta-se à vontade para fazê-lo.
error.hyperlink.lookup=Buscar este erro
error.hyperlink.report=Relatar este erro
error.technicalDetails=Detalhes:
error.existingSolutionDescription=O Cryptomator encontrou um erro inesperado, mas há uma solução pré-existente disponível no seguinte link.
error.hyperlink.solution=Procure a solução
error.lookupPermissionMessage=O Cryptomator pode procurar uma solução para este problema online. Isso enviará uma solicitação para nosso banco de dados de problemas a partir do seu endereço IP.
error.existingSolutionDescription=O Cryptomator não esperava que isso acontecesse. Encontramos uma solução existente para esse erro. Acesse o link a seguir.
error.hyperlink.solution=Busque a solução
error.lookupPermissionMessage=O Cryptomator pode buscar uma solução para esse problema on-line. Isso enviará uma solicitação para nosso banco de dados de problemas a partir do seu endereço IP.
error.dismiss=Ignorar
error.lookUpSolution=Procurar solução
error.lookUpSolution=Buscar solução
# Defaults
defaults.vault.vaultName=Cofre
@@ -66,7 +66,7 @@ addvaultwizard.new.directoryPickerButton=Escolher…
addvaultwizard.new.directoryPickerTitle=Selecionar diretório
addvaultwizard.new.fileAlreadyExists=Já existe um arquivo ou diretório com esse nome
addvaultwizard.new.locationDoesNotExist=Um diretório no caminho especificado não existe ou não pode ser acessado
addvaultwizard.new.locationIsNotWritable=Sem acesso de gravação no caminho especificado
addvaultwizard.new.locationIsNotWritable=Nenhum acesso de gravação no caminho especificado
addvaultwizard.new.locationIsOk=Local adequado para o seu cofre
addvaultwizard.new.invalidName=Nome do cofre inválido
addvaultwizard.new.validName=Nome do cofre válido
@@ -82,7 +82,7 @@ addvaultwizard.new.expertSettings.shorteningThreshold.title=Comprimento máximo
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Válido
### Password
addvaultwizard.new.createVaultBtn=Criar cofre
addvaultwizard.new.generateRecoveryKeyChoice=Você não será capaz de acessar seus dados sem sua senha. Você quer uma chave de recuperação para o caso de perder sua senha?
addvaultwizard.new.generateRecoveryKeyChoice=Você será incapaz de acessar os seus dados sem a sua senha. Deseja uma chave de recuperação para caso perca a sua senha?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sim, por favor. Melhor prevenir do que remediar
addvaultwizard.new.generateRecoveryKeyChoice.no=Não, obrigado. Eu não perderei minha senha
### Information
@@ -100,33 +100,33 @@ addvault.new.readme.storageLocation.10=Se precisar de ajuda, visite a documenta
addvault.new.readme.accessLocation.fileName=BEM-VINDO(A).rtf
addvault.new.readme.accessLocation.1=🔐️ VOLUME ENCRIPTADO 🔐️
addvault.new.readme.accessLocation.2=Este é o local de acesso ao seu cofre.
addvault.new.readme.accessLocation.3=Todos os arquivos adicionados a este volume serão criptografados de forma transparente pelo Cryptomator. Você pode trabalhar nele como se fosse qualquer unidade/pasta. Esta é apenas uma visão não criptografada do seu conteúdo, mas seus arquivos permanecem criptografados na sua unidade o tempo todo.
addvault.new.readme.accessLocation.3=Qualquer arquivo adicionado a este volume será criptografado pelo Cryptomator. Você pode trabalhar nele como em qualquer outro disco ou pasta. Esta é apenas uma visualização descriptografada do conteúdo, seus arquivos permanecem criptografados no seu disco rígido o tempo todo.
addvault.new.readme.accessLocation.4=Fique à vontade para apagar este arquivo.
## Existing
addvaultwizard.existing.title=Adicionar cofre existente
addvaultwizard.existing.instruction=Escolha o arquivo "vault.cryptomator" do seu cofre existente. Caso exista apenas um arquivo chamado "masterkey.cryptomator", selecione outro.
addvaultwizard.existing.restore=Restaurar…
addvaultwizard.existing.chooseBtn=Selecionar…
addvaultwizard.existing.filePickerTitle=Selecionar arquivo do Cofre
addvaultwizard.existing.chooseBtn=Escolher…
addvaultwizard.existing.filePickerTitle=Selecionar arquivo do cofre
addvaultwizard.existing.filePickerMimeDesc=Cofre Cryptomator
## Success
addvaultwizard.success.nextStepsInstructions=Cofre "%s" adicionado.\nVocê precisa desbloquear este cofre para acessar ou adicionar conteúdo. Você também pode desbloqueá-lo a qualquer momento.
addvaultwizard.success.nextStepsInstructions=Cofre "%s" adicionado.\nVocê precisa desbloqueá-lo para acessar ou adicionar conteúdo. O desbloqueio dele pode ser feito a qualquer momento.
addvaultwizard.success.unlockNow=Desbloquear agora
# Remove Vault
removeVault.title=Remover o cofre "%s"
removeVault.message=Remover o cofre?
removeVault.description=Isso só fará com que o Cryptomator esqueça esse cofre. Você pode adicioná-lo novamente. Nenhum arquivo criptografado será excluído do seu disco rígido.
removeVault.description=Isto só fará com que o Cryptomator esqueça este cofre. Você pode adicioná-lo novamente. Nenhum arquivo criptografado será apagado do seu disco rígido.
# Change Password
changepassword.title=Alterar senha
changepassword.enterOldPassword=Digite a senha atual para o cofre "%s"
changepassword.finalConfirmation=Eu entendo que não poderei acessar os meus dados caso esqueça a minha senha
changepassword.finalConfirmation=Estou ciente que não conseguirei acessar os meus dados caso esqueça a minha senha
# Forget Password
forgetPassword.title=Esqueci a senha
forgetPassword.message=Esqueceu sua senha?
forgetPassword.description=Isso excluirá a senha que você salvou deste cofre, do chaveiro de senhas do sistema.
forgetPassword.description=Isto apagará a senha salva deste cofre do chaveiro do seu sistema.
forgetPassword.confirmBtn=Esqueci a senha
# Unlock
@@ -137,28 +137,29 @@ unlock.unlockBtn=Desbloquear
## Select
unlock.chooseMasterkey.message=O arquivo Masterkey foi não encontrado
unlock.chooseMasterkey.description=Não foi possível encontrar o arquivo masterkey para o cofre "%s". Selecione manualmente o arquivo contendo a chave.
unlock.chooseMasterkey.restoreInstead=Em vez disso, restaure o arquivo masterkey
unlock.chooseMasterkey.filePickerTitle=Selecione o arquivo Masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Masterkey do Cryptomator
## Success
unlock.success.message=Desbloqueado com sucesso
unlock.success.description="%s" desbloqueado com êxito! Seu cofre agora está acessível na unidade virtual.
unlock.success.rememberChoice=Lembrar opção escolhida, não mostrar isto novamente
unlock.success.description=O conteúdo no cofre "%s" agora está acessível no seu ponto de montagem.
unlock.success.rememberChoice=Lembrar opção escolhida. Não perguntar novamente
unlock.success.revealBtn=Revelar volume
## Failure
unlock.error.customPath.message=Não foi possível iniciar o cofre no caminho personalizado
unlock.error.customPath.description.notSupported=Se você deseja continuar usando o caminho personalizado, por favor vá para as preferências e selecione um tipo de volume que o suporte. Caso contrário, vá para as opções de cofre e escolha um ponto de montagem suportado.
unlock.error.customPath.description.notExists=O caminho de montagem personalizado não existe. Crie o caminho em seu sistema de arquivos local, ou o altere nas opções de cofre.
unlock.error.customPath.description.inUse=A letra da unidade ou o caminho de montagem "%s" já está em uso.
unlock.error.customPath.description.hideawayNotDir=O arquivo oculto e temporário "%3$s" usado para desbloquear não pôde ser removido. Exclua-o manualmente.
unlock.error.customPath.description.couldNotBeCleaned=Seu cofre não pôde ser montado no caminho "%s". Tente novamente ou escolha um caminho diferente.
unlock.error.customPath.description.notEmptyDir=O caminho de montagem escolhido "%s" não é uma pasta vazia. Escolha uma pasta vazia e tente novamente.
unlock.error.customPath.description.notSupported=Se você deseja continuar usando o caminho personalizado, acesse as preferências e selecione um tipo de volume que seja compatível. Caso contrário, vá para as opções do cofre e escolha um ponto de montagem suportado.
unlock.error.customPath.description.notExists=O caminho de montagem personalizado não existe. Crie o caminho em seu sistema de arquivos local ou o altere nas opções do cofre.
unlock.error.customPath.description.inUse=A letra de unidade ou o caminho de montagem "%s" já está em uso.
unlock.error.customPath.description.hideawayNotDir=Não foi possível remover o arquivo oculto e temporário "%3$s". Verifique-o e depois apague ele manualmente.
unlock.error.customPath.description.couldNotBeCleaned=Não foi possível montar seu cofre no caminho "%s". Tente novamente ou escolha um caminho diferente.
unlock.error.customPath.description.notEmptyDir=O caminho de montagem personalizado escolhido "%s" não é uma pasta vazia. Escolha uma pasta vazia e tente novamente.
unlock.error.customPath.description.generic=Você selecionou um caminho de montagem personalizado para este cofre, mas falhou com a mensagem: %2$s
unlock.error.restartRequired.message=Não foi possível desbloquear o cofre
unlock.error.restartRequired.description=Altere o tipo de volume nas opções do cofre ou reinicie o Cryptomator.
unlock.error.title=Falha ao desbloquear "%s"
## Hub
hub.noKeychain.message=Não foi possível acessar a chave do dispositivo
hub.noKeychain.description=Para desbloquear os cofres centrais, é necessário a chave do dispositivo, protegida por um chaveiro. Para proceder, ative "%s" e selecione um chaveiro nas preferências.
hub.noKeychain.description=Para desbloquear os cofres do Hub, você precisa de uma chave do dispositivo que fica protegida usando um chaveiro. Para continuar, ative "%s" e escolha um chaveiro nas preferências.
hub.noKeychain.openBtn=Abrir preferências
### Waiting
hub.auth.message=Aguardando autenticação…
@@ -166,34 +167,34 @@ hub.auth.description=Você será redirecionado automaticamente para a página de
hub.auth.loginLink=Não foi redirecionado? Clique aqui para abrir.
### Receive Key
hub.receive.message=Processando resposta…
hub.receive.description=Cryptomator está recebendo e processando a resposta do Hub. Por favor, aguarde.
hub.receive.description=O Cryptomator está recebendo e processando a resposta do Hub. Aguarde.
### Register Device
hub.register.message=Novo dispositivo
hub.register.description=Este é o primeiro acesso do Hub neste dispositivo. Por favor, registre-o usando sua Chave de Conta.
hub.register.description=Este é o primeiro acesso do Hub neste dispositivo. Registre-o usando sua Chave de Conta.
hub.register.nameLabel=Nome do dispositivo
hub.register.invalidAccountKeyLabel=Chave da conta inválida
hub.register.registerBtn=Registrar
### Register Device Legacy
hub.register.legacy.occupiedMsg=Esse nome já está em uso
hub.register.legacy.description=Este é o primeiro acesso do Hub neste dispositivo. Por favor, registre-o.
hub.register.legacy.description=Este é o primeiro acesso do Hub neste dispositivo. Registre-o.
### Registration Success
hub.registerSuccess.message=Dispositivo registrado
hub.registerSuccess.description=Seu dispositivo foi registrado com sucesso. Agora você pode continuar a desbloquear o cofre.
hub.registerSuccess.description=Seu dispositivo foi registrado com sucesso. Prossiga com o desbloqueio do cofre.
hub.registerSuccess.unlockBtn=Desbloquear
hub.registerSuccess.legacy.description=Para acessar o cofre, seu dispositivo precisa ser autorizado pelo proprietário do cofre.
### Registration Failed
hub.registerFailed.message=Falha ao registrar o dispositivo
hub.registerFailed.description.generic=Um erro ocorreu no processo de registro. Para mais detalhes, verifique o log da aplicação.
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use um dispositivo diferente.
hub.registerFailed.description.generic=Ocorreu um erro no processo de registro. Para mais detalhes, verifique o registro da aplicação.
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use outro dispositivo.
### Archived
hub.archived.message=Cofre arquivado
hub.archived.description=Este cofre foi arquivo e está inacessível. Contate o proprietário dele.
### Unauthorized
hub.unauthorized.message=Acesso negado
hub.unauthorized.description=Você não está autorizado a abrir este cofre. Entre em contato com o proprietário do cofre para solicitar acesso.
hub.unauthorized.description=Você não tem autorização para abrir este cofre. Entre em contato com o proprietário dele para solicitar acesso.
### Requires Account Initialization
hub.requireAccountInit.message=Ação necessária
hub.requireAccountInit.description.0=Para prosseguir, por favor, complete os passos necessários
hub.requireAccountInit.description.0=Para prosseguir, complete as etapas obrigatórias no seu
hub.requireAccountInit.description.1=Perfil de usuário do Hub
hub.requireAccountInit.description.2=.
### License Exceeded
@@ -203,65 +204,65 @@ hub.invalidLicense.description=Sua instância do Cryptomator Hub tem uma licenç
# Lock
## Force
lock.forced.message=Bloqueio falhou
lock.forced.description=Trancar "%s" foi bloqueado por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio deste cofre, no entanto, a interrupção pode resultar em perda de dados não salvos.
lock.forced.description=O fechamento do cofre "%s" foi impedido por arquivos abertos ou tarefas em andamento. Você pode forçar o fechamento, mas interromper a gravação de dados pode causar a perda de arquivos não salvos.
lock.forced.retryBtn=Tentar novamente
lock.forced.forceBtn=Forçar bloqueio
## Failure
lock.fail.message=O bloqueio do cofre falhou.
lock.fail.description=Cofre "%s" não pôde ser bloqueado. Certifique-se de que o trabalho não salvo está salvo em outro lugar e que operações de Leitura/Escrita importantes sejam concluídas. Para fechar o cofre, encerre o processo do Cryptomator.
lock.fail.description=Não foi possível bloquear o cofre "%s". Certifique-se de que o trabalho não salvo foi armazenado em outro local e que as operações importantes de leitura/gravação foram concluídas. Para fechar o cofre, encerre o processo do Cryptomator.
# Migration
migration.title=Atualizar cofre
## Start
migration.start.header=Atualizar cofre
migration.start.text=Para abrir o seu cofre "%s" nesta nova versão do Cryptomator, o cofre precisa ser atualizado para um formato mais recente. Antes de fazer isso, você deve saber o seguinte:
migration.start.text=Para abrir seu cofre "%s" nesta nova versão do Cryptomator, o cofre precisa ser atualizado para um formato mais recente. Antes de realizar esse procedimento, esteja ciente do seguinte:
migration.start.remarkUndone=Esta melhoria não pode ser desfeita.
migration.start.remarkVersions=Versões mais antigas do Cryptomator não conseguirão abrir o cofre na versão atualizada.
migration.start.remarkCanRun=Você deve ter certeza de que todos os dispositivos a partir dos quais você acessar o cofre podem executar esta versão do Cryptomator.
migration.start.remarkSynced=Você deve ter certeza de que o seu cofre está totalmente sincronizado neste dispositivo e em outros dispositivos, antes de atualizá-lo.
migration.start.confirm=Eu li e entendi a informação acima
migration.start.remarkCanRun=Garanta que todos os dispositivos que você usa para acessar o cofre consigam executar esta versão do Cryptomator.
migration.start.remarkSynced=Antes de atualizá-lo, tenha certeza de que seu cofre está totalmente sincronizado neste e em outros dispositivos.
migration.start.confirm=Li e estou ciente das informações acima
## Run
migration.run.enterPassword=Digite a senha para "%s"
migration.run.startMigrationBtn=Migrar cofre
migration.run.progressHint=Isso pode levar algum tempo…
## Success
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nVocê agora pode desbloquear este cofre.
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nAgora você pode desbloquear este cofre.
migration.success.unlockNow=Desbloquear agora
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=Sistema de arquivos incompatível
migration.error.missingFileSystemCapabilities.description=A migração não foi iniciada porque o seu cofre está localizado em um sistema de arquivos inadequado.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=O sistema de arquivos não suporta arquivos com nome longo.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=O sistema de arquivos não suporta arquivos com nomes longos.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=O sistema de arquivos não suporta caminhos longos.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=O sistema de arquivos não permite leitura.
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=O sistema de arquivos não permite ser gravado.
## Impossible
migration.impossible.heading=Não é possível migrar o cofre
migration.impossible.reason=O cofre não pode ser migrado automaticamente porque sua localização de armazenamento ou o ponto de acesso não é compatível.
migration.impossible.reason=O cofre não pode ser migrado automaticamente porque sua localização de armazenamento ou o ponto de acesso é incompatível.
migration.impossible.moreInfo=O cofre ainda pode ser aberto com uma versão mais antiga. Para instruções sobre como migrar um cofre manualmente, visite
# Health Check
## Start
health.title=Verificação de saúde de "%s"
health.intro.header=Verificação de Saúde
health.intro.text=A Verificação de Saúde é um grupo de verificações que detectam e tentam corrigir problemas na estrutura interna do cofre. Lembre-se de:
health.intro.remarkSync=Certifique-se de que todos os dispositivos estejam completamente sincronizados, isso resolve a maioria dos problemas.
health.intro.remarkFix=Nem todos os problemas podem ser corrigidos.
health.intro.remarkBackup=Se os dados estiverem corrompidos, apenas um backup poderá ajudar.
health.intro.affirmation=Eu li e entendi a informação acima
health.intro.text=A verificação de saúde é um grupo de verificações que detectam e tentam corrigir problemas na estrutura interna do cofre. Lembre-se de:
health.intro.remarkSync=Certifique-se de que todos os dispositivos estejam completamente sincronizados. Isso resolve a maioria dos problemas.
health.intro.remarkFix=A maioria dos problema podem ser corrigidos.
health.intro.remarkBackup=Caso os dados corrompam, apenas um backup poderá ajudar.
health.intro.affirmation=Li e estou ciente das informações acima
## Start Failure
health.fail.header=Erro ao carregar configuração do Cofre
health.fail.header=Erro ao carregar a configuração do cofre
health.fail.ioError=Ocorreu um erro ao acessar e ler o arquivo de configuração.
health.fail.parseError=Ocorreu um erro ao analisar a configuração do cofre.
health.fail.moreInfo=Mais informações
## Check Selection
health.checkList.description=Selecione as verificações na lista esquerda ou use os botões abaixo.
health.checkList.description=Selecione as verificações na lista à esquerda ou use os botões abaixo.
health.checkList.selectAllButton=Selecionar todas as verificações
health.checkList.deselectAllButton=Desmarcar todas as verificações
health.check.runBatchBtn=Executar Verificações
health.check.runBatchBtn=Executar verificações selecionadas
## Detail view
health.check.detail.noSelectedCheck=Para ver os resultados, selecione uma verificação na lista à esquerda.
health.check.detail.noSelectedCheck=Para ver os resultados, selecione uma verificação de saúde na lista à esquerda.
health.check.detail.checkScheduled=A verificação está programada.
health.check.detail.checkRunning=A verificação está rodando…
health.check.detail.checkRunning=A verificação está em execução…
health.check.detail.checkSkipped=A verificação não foi selecionada para execução.
health.check.detail.checkFinished=A verificação terminou com sucesso.
health.check.detail.checkFinishedAndFound=A verificação terminou. Verifique os resultados.
@@ -270,21 +271,21 @@ health.check.detail.checkCancelled=A verificação foi cancelada.
health.check.detail.listFilters.label=Filtro
health.check.detail.filterSeverity=Filtrar por severidade
health.check.detail.filterFixState=Filtrar por estado de correção
health.check.detail.fixAllSpecificBtn=Reparar todos deste tipo
health.check.exportBtn=Exportar Relatório
health.check.detail.fixAllSpecificBtn=Corrigir todos deste tipo
health.check.exportBtn=Exportar relatório
## Result view
health.result.severityFilter.all=Gravidade - Todas
health.result.severityFilter.good=Bom
health.result.severityFilter.all=Gravidade Todas
health.result.severityFilter.good=Boa
health.result.severityFilter.info=Informação
health.result.severityFilter.warn=Aviso
health.result.severityFilter.crit=Crítico
health.result.severityFilter.warn=Alerta
health.result.severityFilter.crit=Crítica
health.result.severityTip.good=Severidade: Boa\nEstrutura do cofre normal.
health.result.severityTip.info=Gravidade: Info\nEstrutura de cofre intacta, correção sugerida.
health.result.severityTip.warn=Gravidade: Aviso\nEstrutura de cofre corrompida, correção altamente aconselhada.
health.result.severityTip.crit=Gravidade: Crítica\nEstrutura do cofre corrompida, haverá perda de dados.
health.result.fixStateFilter.all=Estado de reparo - Todos
health.result.fixStateFilter.fixable=Reparável
health.result.fixStateFilter.notFixable=Não reparável
health.result.severityTip.info=Gravidade: Informação\nEstrutura de cofre intacta. Recomenda-se correção.
health.result.severityTip.warn=Gravidade: Alerta\nEstrutura de cofre corrompida. É altamente recomendado realizar a correção.
health.result.severityTip.crit=Gravidade: Crítica\nEstrutura do cofre corrompida. Os dados poderão ser perdidos.
health.result.fixStateFilter.all=Estado de reparo Todos
health.result.fixStateFilter.fixable=Corrigível
health.result.fixStateFilter.notFixable=Incorrigível
health.result.fixStateFilter.fixing=Corrigindo…
health.result.fixStateFilter.fixed=Corrigido
health.result.fixStateFilter.fixFailed=Correção falhou
@@ -301,13 +302,13 @@ preferences.general.startHidden=Ocultar janela ao iniciar o Cryptomator
preferences.general.autoCloseVaults=Bloquear cofres sem perguntar ao sair do aplicativo
preferences.general.debugLogging=Ativar registro de depuração
preferences.general.debugDirectory=Mostrar arquivos de registro
preferences.general.autoStart=Iniciar o Cryptomator ao inicializar o sistema
preferences.general.autoStart=Iniciar o Cryptomator com o sistema
preferences.general.keychainBackend=Armazenar senhas com
preferences.general.quickAccessService=Adicione cofres desbloqueados à área de acesso rápido
## Interface
preferences.interface=Interface
preferences.interface.theme=Aparência
preferences.interface.theme.automatic=Automático
preferences.interface.theme.automatic=Automática
preferences.interface.theme.dark=Escura
preferences.interface.theme.light=Clara
preferences.interface.unlockThemes=Desbloquear o modo escuro
@@ -317,35 +318,35 @@ preferences.interface.interfaceOrientation=Orientação da interface
preferences.interface.interfaceOrientation.ltr=Da esquerda para a direita
preferences.interface.interfaceOrientation.rtl=Da direita para a esquerda
preferences.interface.showTrayIcon=Mostrar ícone na barra do sistema (requer reinicialização)
preferences.interface.compactMode=Habilitar lista de cofres compactos
preferences.interface.compactMode=Ativar lista de cofres compacta
## Volume
preferences.volume=Volume Virtual
preferences.volume=Volume virtual
preferences.volume.type=Tipo de volume padrão
preferences.volume.type.automatic=Automático
preferences.volume.docsTooltip=Abra a documentação para saber mais sobre os diferentes tipos de volumes.
preferences.volume.fuseRestartRequired=Para aplicar as mudanças, o Cryptomator precisa ser reiniciado.
preferences.volume.tcp.port=Porta TCP Padrão
preferences.volume.docsTooltip=Acesse a documentação para saber mais sobre os diferentes tipos de volumes.
preferences.volume.fuseRestartRequired=Para aplicar as alterações, o Cryptomator precisa ser reiniciado.
preferences.volume.tcp.port=Porta TCP padrão
preferences.volume.supportedFeatures=O tipo de volume escolhido suporta os seguintes recursos:
preferences.volume.feature.mountAuto=Seleção automática de ponto de montagem
preferences.volume.feature.mountToDir=Diretório personalizado como ponto de montagem
preferences.volume.feature.mountToDriveLetter=Cartaz como ponto de montagem
preferences.volume.feature.mountFlags=Opções customizadas
preferences.volume.feature.readOnly=Montaria somente leitura
preferences.volume.feature.mountToDriveLetter=Letra de unidade como ponto de montagem
preferences.volume.feature.mountFlags=Opções personalizadas de montagem
preferences.volume.feature.readOnly=Montagem de somente leitura
## Updates
preferences.updates=Atualizações
preferences.updates.currentVersion=Versão atual: %s
preferences.updates.autoUpdateCheck=Buscar atualizações automaticamente
preferences.updates.checkNowBtn=Buscar Agora
preferences.updates.checkNowBtn=Verificar agora
preferences.updates.updateAvailable=Atualizar para versão %s disponível.
preferences.updates.lastUpdateCheck=Última verificação: %s
preferences.updates.lastUpdateCheck.never=nunca
preferences.updates.lastUpdateCheck.recently=recentemente
preferences.updates.lastUpdateCheck.daysAgo=%s dia atrás
preferences.updates.lastUpdateCheck.hoursAgo=%s horas atrás
preferences.updates.lastUpdateCheck.daysAgo=%s dias
preferences.updates.lastUpdateCheck.hoursAgo=%s horas
preferences.updates.prohibitedDueToUnlockedVaults.1=Por favor
preferences.updates.prohibitedDueToUnlockedVaults.2=bloqueie seus cofres
preferences.updates.prohibitedDueToUnlockedVaults.3=para instalar a atualização.
preferences.updates.checkFailed=Procura por atualizações falhou. Por favor, verifique sua conexão com a internet ou tente novamente mais tarde.
preferences.updates.checkFailed=A busca por atualizações falhou. Verifique sua conexão à rede ou tente novamente mais tarde.
preferences.updates.updateFailed=Falha na atualização. Instale-a manualmente.
preferences.updates.upToDate=O Cryptomator está atualizado.
preferences.updates.visitDownloadPage=Visite a página de download
@@ -354,16 +355,16 @@ preferences.updates.visitDownloadPage=Visite a página de download
preferences.contribute=Apoie-nos
preferences.contribute.registeredFor=Certificado de apoiador registrado para %s
preferences.contribute.noCertificate=Apoie o Cryptomator e receba um certificado de apoiador. Ele é como uma chave de licença para pessoas maravilhosas que usam software livre. ;-)
preferences.contribute.getCertificate=Não tem um ainda? Saiba como obtê-lo.
preferences.contribute.getCertificate=Ainda não tem um? Saiba como obtê-lo.
preferences.contribute.promptText=Cole o código do certificado de apoiador aqui
preferences.contribute.thankYou=Obrigado por apoiar o desenvolvimento open-source do Cryptomator!
preferences.contribute.thankYou=Obrigado por apoiar o desenvolvimento de código aberto do Cryptomator!
preferences.contribute.donate=Doar
preferences.contribute.sponsor=Patrocinador
preferences.contribute.sponsor=Apoiador
preferences.contribute.removeCert.tooltip=Remover certificado
### Remove License Key Dialog
removeCert.title=Excluir Certificado
removeCert.message=Excluir certificado de apoiador?
removeCert.title=Remover certificado
removeCert.message=Remover o certificado de apoiador?
removeCert.description=Os principais recursos do Cryptomador não são afetados por isso. Nem o acesso aos seus cofres é restrito, nem o nível de segurança é reduzido.
#<-- Add entries for donations and code/translation/documentation contribution -->
@@ -373,13 +374,13 @@ preferences.about.thirdPartyLicenses=Licenças de terceiros
# Vault Statistics
stats.title=Estatísticas para %s
stats.cacheHitRate=Taxa de Utilização do Cache
stats.cacheHitRate=Taxa de uso do cache
## Read
stats.read.throughput.idle=Leitura: ociosa
stats.read.throughput.kibs=Leitura: %.2f MiB/s
stats.read.throughput.kibs=Leitura: %.2f KiB/s
stats.read.throughput.mibs=Leitura: %.2f MiB/s
stats.read.total.data.none=Dados lidos: -
stats.read.total.data.kib=Dados lidos: %.1f GiB
stats.read.total.data.kib=Dados lidos: %.1f KiB
stats.read.total.data.mib=Dados lidos: %.1f MiB
stats.read.total.data.gib=Dados lidos: %.1f GiB
stats.decr.total.data.none=Dados descriptografados: -
@@ -414,7 +415,7 @@ main.vaultlist.emptyList.onboardingInstruction=Clique aqui para adicionar um cof
main.vaultlist.contextMenu.remove=Remover…
main.vaultlist.contextMenu.lock=Bloquear
main.vaultlist.contextMenu.unlock=Desbloquear…
main.vaultlist.contextMenu.unlockNow=Desbloquear Agora
main.vaultlist.contextMenu.unlockNow=Desbloquear agora
main.vaultlist.contextMenu.vaultoptions=Mostrar opções do cofre
main.vaultlist.contextMenu.reveal=Revelar volume
main.vaultlist.contextMenu.share=Compartilhar…
@@ -435,7 +436,7 @@ main.vaultDetail.storageLocation=Local de armazenamento do cofre
### Locked
main.vaultDetail.lockedStatus=BLOQUEADO
main.vaultDetail.unlockBtn=Desbloquear…
main.vaultDetail.unlockNowBtn=Desbloquear Agora
main.vaultDetail.unlockNowBtn=Desbloquear agora
main.vaultDetail.optionsBtn=Opções do cofre
main.vaultDetail.passwordSavedInKeychain=Senha salva
main.vaultDetail.share=Compartilhar…
@@ -450,94 +451,98 @@ main.vaultDetail.bytesPerSecondWritten=Gravação:
main.vaultDetail.throughput.idle=ocioso
main.vaultDetail.throughput.kbps=%.1f KiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Estatísticas do Cofre
main.vaultDetail.locateEncryptedFileBtn=Localizar Arquivo Criptografado
main.vaultDetail.stats=Estatísticas do cofre
main.vaultDetail.locateEncryptedFileBtn=Localizar arquivo criptografado
main.vaultDetail.locateEncryptedFileBtn.tooltip=Escolha um arquivo do seu cofre para localizar sua versão criptografada
main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a Área de Transferência!
main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar Arquivo no Cofre
main.vaultDetail.decryptName.buttonLabel=Descriptografar Nome do Arquivo
main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a área de transferência!
main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar arquivo no cofre
main.vaultDetail.decryptName.buttonLabel=Descriptografar nome do arquivo
main.vaultDetail.decryptName.tooltip=Escolha um arquivo criptografado do cofre para descriptografar seu nome
### Missing
main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre neste caminho.
main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre nesse caminho.
main.vaultDetail.missing.recheck=Verificar novamente
main.vaultDetail.missing.remove=Remover da lista de cofres…
main.vaultDetail.missing.changeLocation=Alterar Localização do Cofre…
main.vaultDetail.missing.changeLocation=Alterar localização do cofre…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=A configuração do cofre está faltando.
main.vaultDetail.missingVaultConfig.restore=Restaurar configuração do cofre
### Needs Migration
main.vaultDetail.migrateButton=Atualizar Cofre
main.vaultDetail.migratePrompt=Seu cofre precisa ser atualizado para um novo formato antes de poder acessá-lo
main.vaultDetail.migrateButton=Atualizar cofre
main.vaultDetail.migratePrompt=Seu cofre precisa ser atualizado para um novo formato antes de conseguir acessá-lo
### Error
main.vaultDetail.error.info=Ocorreu um erro ao carregar o cofre do disco.
main.vaultDetail.error.reload=Recarregar
main.vaultDetail.error.windowTitle=Erro ao carregar cofre
main.vaultDetail.error.windowTitle=Erro ao carregar o cofre
# Wrong File Alert
wrongFileAlert.title=Como Encriptar Arquivos
wrongFileAlert.message=Você tentou encriptar esses arquivos?
wrongFileAlert.description=Para este propósito, o Cryptomator fornece um volume no seu gerenciador de arquivos do sistema.
wrongFileAlert.instruction.0=Para encriptar arquivos, siga estes passos:
wrongFileAlert.instruction.1=1. Desbloqueie o seu cofre.
wrongFileAlert.title=Como criptografar os arquivos
wrongFileAlert.message=Você tentou criptografar estes arquivos?
wrongFileAlert.description=Para esta finalidade, o Cryptomator fornece um volume no seu gerenciador de arquivos do sistema.
wrongFileAlert.instruction.0=Para criptografar os arquivos, siga as etapas abaixo:
wrongFileAlert.instruction.1=1. Desbloqueie o seu cofre;
wrongFileAlert.instruction.2=2. Clique em "Revelar" para abrir o volume no seu gerenciador de arquivos.
wrongFileAlert.instruction.3=3. Adicione seus arquivos a este volume.
wrongFileAlert.instruction.3=3. Adicione seus arquivos a esse volume;
wrongFileAlert.link=Para obter assistência, visite
# Vault Options
## General
vaultOptions.general=Geral
vaultOptions.general.vaultName=Nome do cofre
vaultOptions.general.autoLock.lockAfterTimePart1=Bloquear quando inativo por
vaultOptions.general.autoLock.lockAfterTimePart1=Bloquear quando inativo após
vaultOptions.general.autoLock.lockAfterTimePart2=minuto(s)
vaultOptions.general.unlockAfterStartup=Desbloqueie o cofre ao iniciar o Cryptomator
vaultOptions.general.autoLock.accessibleText=Bloquear tempo limite em minutos
vaultOptions.general.unlockAfterStartup=Desbloquear o cofre ao iniciar o Cryptomator
vaultOptions.general.actionAfterUnlock=Após desbloquear
vaultOptions.general.actionAfterUnlock.ignore=Não fazer nada
vaultOptions.general.actionAfterUnlock.reveal=Revelar volume
vaultOptions.general.actionAfterUnlock.ask=Perguntar
vaultOptions.general.startHealthCheckBtn=Iniciar Exame de Saúde
vaultOptions.general.startHealthCheckBtn=Iniciar verificação de saúde
## Mount
vaultOptions.mount=Montagem
vaultOptions.mount.info=Abra preferências de drive virtual para alterar as configurações padrão.
vaultOptions.mount.readonly=Somente Leitura
vaultOptions.mount.customMountFlags=Flags Personalizadas de Montagem
vaultOptions.mount.info=Abra preferências do volume virtual para alterar as configurações padrões.
vaultOptions.mount.readonly=Somente leitura
vaultOptions.mount.customMountFlags=Flags personalizadas de montagem
vaultOptions.mount.winDriveLetterOccupied=ocupado
vaultOptions.mount.mountPoint=Ponto de Montagem
vaultOptions.mount.mountPoint=Ponto de montagem
vaultOptions.mount.mountPoint.auto=Escolher automaticamente um local adequado
vaultOptions.mount.mountPoint.driveLetter=Usar letra de unidade atribuída
vaultOptions.mount.mountPoint.custom=Usar diretório escolhido
vaultOptions.mount.mountPoint.directoryPickerButton=Escolher…
vaultOptions.mount.mountPoint.directoryPickerTitle=Escolha um diretório
vaultOptions.mount.volumeType.default=Padrão (%s)
vaultOptions.mount.volumeType.restartRequired=Para usar esse tipo de volume, o Cryptomator precisa ser reiniciado.
vaultOptions.mount.volumeType.restartRequired=Para usar este tipo de volume, o Cryptomator precisa ser reiniciado.
vaultOptions.mount.volume.tcp.port=Porta TCP
vaultOptions.mount.volume.type=Tipo de Volume
vaultOptions.mount.volume.type=Tipo de volume
## Master Key
vaultOptions.masterkey=Senha
vaultOptions.masterkey.changePasswordBtn=Alterar senha
vaultOptions.masterkey.forgetSavedPasswordBtn=Esquecer a senha salva
vaultOptions.masterkey.recoveryKeyExplanation=Se você perder a sua senha, a única forma de restaurar acesso a um cofre é através de uma chave de recuperação.
vaultOptions.masterkey.recoveryKeyExplanation=Caso perca a sua senha, a única forma de restaurar acesso a um cofre é através de uma chave de recuperação.
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar chave de recuperação
vaultOptions.masterkey.recoverPasswordBtn=Redefinir senha
vaultOptions.masterkey.missingMasterkeyFile=Essas opções ficarão disponíveis se o arquivo masterkey estiver na pasta do cofre.
## Hub
vaultOptions.hub=Recuperação
vaultOptions.hub.convertInfo=Você pode usar a chave de recuperação para converter este cofre do Hub em um cofre protegido por senha de emergência.
vaultOptions.hub.convertBtn=Converter para Cofre protegido por senha
vaultOptions.hub.convertBtn=Converter para cofre protegido por senha
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=Mostrar Chave de Recuperação
recoveryKey.create.message=Senha obrigatória
recoveryKey.create.description=Digite sua senha para mostrar a chave de recuperação de "%s":
recoveryKey.create.message=Requer senha
recoveryKey.create.description=Digite a senha para mostrar a chave de recuperação de "%s":
recoveryKey.recover.description=Digite a senha para "%s" para recuperar a configuração do cofre.
recoveryKey.display.description=A seguinte chave de recuperação pode ser usada para restaurar o acesso a "%s":
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro, por exemplo:\n • Guarde num gerenciador de senhas\n • Grave num flash drive USB\n • Imprima, ou escreva, num papel
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro. Por exemplo:\n • Guarde em um gerenciador de senhas;\n • Grave em um pen drive;\n • Imprima ou escreva em um papel.
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=Redefinir Senha
recoveryKey.recover.title=Redefinir senha
recoveryKey.recover.prompt=Digite a chave de recuperação para "%s":
recoveryKey.recover.correctKey=Esta é uma chave de recuperação válida
recoveryKey.recover.wrongKey=Esta chave de recuperação pertence a um outro cofre
recoveryKey.recover.invalidKey=Esta chave de recuperação não é válida
recoveryKey.printout.heading=Chave de Recuperação do Cryptomator\n"%s"\n
recoveryKey.recover.correctKey=Essa é uma chave de recuperação válida
recoveryKey.recover.wrongKey=Essa chave de recuperação pertence a um outro cofre
recoveryKey.recover.invalidKey=Essa chave de recuperação é inválida
recoveryKey.printout.heading=Chave de Recuperação do Cryptomator\n"%s"
### Reset Password
recoveryKey.recover.resetBtn=Redefinir
recoveryKey.recover.recoverBtn=Recuperar
@@ -545,41 +550,61 @@ recoveryKey.recover.recoverBtn=Recuperar
recoveryKey.recover.resetSuccess.message=Senha redefinida com sucesso
recoveryKey.recover.resetSuccess.description=Você pode desbloquear o seu cofre com a nova senha.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Configuração do cofre recuperada
recoveryKey.recover.resetMasterkeyFileSuccess.message=Arquivo Masterkey recuperado
recoveryKey.recover.resetMasterkeyFileSuccess.description=Agora você pode desbloquear o seu cofre com a sua nova senha.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Cofre adicionado
recover.existing.message=O cofre foi adicionado com sucesso
recover.existing.description=O seu cofre "%s" foi adicionado à lista de cofre. Nenhum processo de recuperação foi necessário.
##Vault Already Exists - Dialog
recover.alreadyExists.title=O cofre já existe
recover.alreadyExists.message=Este cofre já foi adicionado
recover.alreadyExists.description=Seu cofre "%s" não foi adicionado novamente porque já está na sua lista de cofres.
##Invalid Selection - Dialog
recover.invalidSelection.title=Seleção inválida
recover.invalidSelection.message=Sua seleção não é um cofre
recover.invalidSelection.description=A pasta selecionada precisa ser um cofre válido do Cryptomator.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Cofre do Hub
contactHubVaultOwner.message=Este cofre foi criado com o Cryptomator Hub
contactHubVaultOwner.description=Entre em contato com o proprietário do cofre para restaurar o arquivo restante. Eles podem baixar o modelo de cofre no Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Recuperar a configuração do cofre
recover.recoverMasterkey.title=Recuperar Masterkey
## OnBoarding
recover.onBoarding.chooseMethod=Escolha o método de recuperação:
recover.onBoarding.useRecoveryKey=Usar chave de recuperação
recover.onBoarding.usePassword=Usar senha
recover.onBoarding.intro=Certifique-se de verificar o seguinte:
recover.onBoarding.pleaseConfirm=Antes de prosseguir, confirme que:
recover.onBoarding.otherwisePleaseConfirm=Caso contrário, confirme que:
recover.onBoarding.allMissing.intro=Se este cofre for gerenciado pelo Cryptomator Hub, o proprietário do cofre deve restaurá-lo para você.
recover.onBoarding.intro.ensure=Todos os arquivos estão sincronizados.
recover.onBoarding.affirmation=Li e estou ciente dos requisitos
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Você tem a chave de recuperação e sabe se as configurações avançadas foram usadas.
recover.onBoarding.intro.password=Você tem a senha do cofre e sabe se as configurações avançadas foram usadas.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Você tem a chave de recuperação do cofre.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Este valor deve corresponder ao usado antes da recuperação para garantir a compatibilidade com os dados criptografados anteriormente.
# Convert Vault
convertVault.title=Converter Cofre
convertVault.title=Converter cofre
convertVault.convert.convertBtn.before=Converter
convertVault.convert.convertBtn.processing=Convertendo...
convertVault.success.message=Conversão bem sucedida
convertVault.hubToPassword.success.description=Agora você pode desbloquear o cofre com a senha escolhida sem exigir acesso ao Hub.
convertVault.success.message=Conversão concluída
convertVault.hubToPassword.success.description=Agora você pode desbloquear o cofre com a senha escolhida sem solicitar acesso ao Hub.
# New Password
newPassword.promptText=Digite a nova senha
@@ -595,57 +620,57 @@ passwordStrength.messageLabel.4=Muito forte
# Quit
quit.title=Sair da aplicação
quit.message=Existem cofres desbloqueados
quit.description=Por favor, confirme que você deseja sair. O Cryptomador irá bloquear cuidadosamente todos os cofres abertos para evitar a perda de dados.
quit.lockAndQuitBtn=Bloquear e Sair
quit.message= cofres desbloqueados
quit.description=Confirme que deseja sair. O Cryptomador bloqueará cuidadosamente todos os cofres abertos para evitar a perda de dados.
quit.lockAndQuitBtn=Bloquear e sair
# Forced Quit
quit.forced.message=Alguns cofres não puderam ser trancados
quit.forced.description=O bloqueio dos cofres foi impedido por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio dos cofres restantes, no entanto, essa interrupção de E/S pode resultar na perda de dados não salvos nos cofres — “seus dados”.
quit.forced.forceAndQuitBtn=Forçar e Sair
quit.forced.message=Alguns cofres não foram trancados
quit.forced.description=O bloqueio dos cofres foi impedido por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio dos cofres restantes, mas essa interrupção de gravação de dados pode resultar na perda de arquivos não salvos nos cofres.
quit.forced.forceAndQuitBtn=Forçar e sair
# Update Reminder
updateReminder.title=Verificação de Atualizações
updateReminder.message=Verificar Atualizações?
updateReminder.description=Mantenha-se atualizado com novos recursos, correções de bugs e melhoramentos de segurança. Recomendamos verificar atualizações automaticamente.
updateReminder.notNow=Agora Não
updateReminder.title=Verificação de atualizações
updateReminder.message=Verificar atualizações?
updateReminder.description=Mantenha-se atualizado com novos recursos, correções de bugs e melhorias de segurança. Recomendamos verificar as atualizações automaticamente.
updateReminder.notNow=Agora não
updateReminder.yesOnce=Sim, uma vez
updateReminder.yesAutomatically=Sim, Automaticamente
updateReminder.yesAutomatically=Sim, automaticamente
#Dokany Support End
dokanySupportEnd.title=Aviso de descontinuação
dokanySupportEnd.message=Fim do suporte para o Dokany
dokanySupportEnd.description=O volume do tipo Dokany não é mais suportado pelo Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão agora. Você pode ver o tipo padrão nas preferências.
dokanySupportEnd.description=O volume do tipo Dokany não é mais compatível com o Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão. Veja o tipo padrão nas preferências.
dokanySupportEnd.preferencesBtn=Abrir preferências
#Retry If Readonly
retryIfReadonly.title=Acesso Restrito ao Cofre
retryIfReadonly.title=Acesso restrito ao cofre
retryIfReadonly.message=Sem acesso de gravação ao diretório do cofre
retryIfReadonly.description=O Cryptomator não pode escrever no diretório do cofre. Você pode alterar o cofre para somente leitura e tentar novamente. Esta opção pode ser desativada nas opções de cofre.
retryIfReadonly.retry=Alterar e Repetir
retryIfReadonly.description=O Cryptomator não pode gravar no diretório do cofre. Você pode alterar o cofre para somente leitura e tentar novamente. Esta opção pode ser desativada nas opções do cofre.
retryIfReadonly.retry=Alterar e repetir
# Share Vault
shareVault.title=Compartilhar Cofre
shareVault.title=Compartilhar cofre
shareVault.message=Gostaria de compartilhar o seu cofre com outras pessoas?
shareVault.description=Sempre tenha cuidado ao compartilhar seu cofre com outras pessoas. Em resumo, siga esses passos:
shareVault.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento na nuvem.
shareVault.description=Sempre tenha cuidado ao compartilhar seu cofre com terceiros. Em resumo, siga esses passos:
shareVault.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento em nuvem.
shareVault.instruction.2=2. Compartilhe a senha do cofre de forma segura.
shareVault.remarkBestPractices=Para mais informações, confira as sugestões de melhores práticas em nossos documentos.
shareVault.docsTooltip=Abra a documentação para aprender mais sobre compartilhamento de cofres.
shareVault.remarkBestPractices=Para mais informações, confira as sugestões de boas práticas em nossas documentações.
shareVault.docsTooltip=Abra a documentação para aprender mais sobre o compartilhamento de cofres.
shareVault.hubAd.description=A maneira segura de trabalhar em equipes
shareVault.hubAd.keyManagement=• Gerenciamento de chaves de Zero-Conhecimento
shareVault.hubAd.keyManagement=• Gerenciamento de chaves de conhecimento zero
shareVault.hubAd.authentication=• Autenticação forte
shareVault.hubAd.encryption=• Criptografia de ponta-a-ponta
shareVault.visitHub=Visite o Cryptomator Hub
shareVault.hub.message=Como compartilhar um cofre do Hub
shareVault.hub.description=Para compartilhar o conteúdo do cofre com outro membro da equipe, você deve realizar duas etapas:
shareVault.hub.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento na nuvem.
shareVault.hub.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento em nuvem.
shareVault.hub.instruction.2=2. Conceda acesso ao membro da equipe no Cryptomator Hub.
shareVault.hub.openHub=Abrir o Cryptomator Hub
# Decrypt File Names
decryptNames.title=Descriptografar Nomes de Arquivos
decryptNames.title=Descriptografar nomes de arquivos
decryptNames.filePicker.title=Selecione o arquivo criptografado
decryptNames.filePicker.extensionDescription=Arquivo criptografado do Cryptomator
decryptNames.copyTable.tooltip=Copiar tabela
@@ -653,10 +678,10 @@ decryptNames.clearTable.tooltip=Limpar tabela
decryptNames.column.encrypted=Criptografado
decryptNames.column.decrypted=Descriptografado
decryptNames.copyHint=Copiar conteúdo da célula com %s
decryptNames.dropZone.message=Arraste e solte arquivos ou clique para selecionar
decryptNames.dropZone.error.vaultInternalFiles=Selecionados arquivos internos do cofre sem nome descriptografável
decryptNames.dropZone.error.foreignFiles=Arquivos não pertencem ao cofre "%s"
decryptNames.dropZone.error.noDirIdBackup=Diretório dos arquivos selecionados não contém o arquivo dirId.c9r
decryptNames.dropZone.message=Arraste e solte os arquivos ou clique para selecionar
decryptNames.dropZone.error.vaultInternalFiles=Arquivos internos do cofre sem nome descriptografável selecionados
decryptNames.dropZone.error.foreignFiles=Os arquivos não pertencem ao cofre "%s"
decryptNames.dropZone.error.noDirIdBackup=O diretório dos arquivos selecionados não contém o arquivo dirId.c9r
decryptNames.dropZone.error.generic=Falha ao descriptografar os nomes de arquivos
@@ -667,35 +692,28 @@ eventView.clearListButton.tooltip=Limpar lista
eventView.filterVaults=Filtrar por cofre
eventView.cell.actionsButton.tooltip=Ações de evento
## event list entries
eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes
eventView.entry.vaultLocked.description=Desbloquear "%s" para mais detalhes
eventView.entry.conflictResolved.message=Conflito resolvido
eventView.entry.conflictResolved.showDecrypted=Mostrar arquivo descriptografado
eventView.entry.conflictResolved.copyDecrypted=Copiar caminho descriptografado
eventView.entry.conflict.message=Resolução de conflitos falhou
eventView.entry.conflict.message=Falha na resolução de conflitos
eventView.entry.conflict.showDecrypted=Mostrar arquivo original descriptografado
eventView.entry.conflict.copyDecrypted=Copiar caminho original descriptografado
eventView.entry.conflict.showEncrypted=Mostrar arquivo criptografado conflitante
eventView.entry.conflict.copyEncrypted=Copiar caminho criptografado conflitante
eventView.entry.decryptionFailed.message=Decriptação falhou
eventView.entry.decryptionFailed.message=Falha ao descriptografar
eventView.entry.decryptionFailed.showEncrypted=Mostrar arquivo criptografado
eventView.entry.decryptionFailed.copyEncrypted=Copiar caminho encriptado
eventView.entry.brokenDirFile.message=Link do diretório quebrado
eventView.entry.brokenDirFile.showEncrypted=Mostrar link quebrado e criptografado
eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado
eventView.entry.brokenFileNode.message=Nó de sistema de arquivos danificado
eventView.entry.brokenFileNode.showEncrypted=Mostrar nó criptografado com falha
eventView.entry.brokenFileNode.copyEncrypted=Copiar caminho do nó quebrado criptografado
eventView.entry.brokenFileNode.copyDecrypted=Copiar caminho descriptografado
eventView.entry.inUse.message=Arquivo bloqueado
eventView.entry.inUse.message=Arquivo em uso
eventView.entry.inUse.showDecrypted=Mostrar arquivo descriptografado
eventView.entry.inUse.copyDecrypted=Copiar caminho descriptografado
eventView.entry.inUse.showEncrypted=Mostrar arquivo criptografado
eventView.entry.inUse.copyEncrypted=Copiar caminho encriptado
eventView.entry.inUse.ignoreLock=Ignorar bloqueio
eventView.entry.inUse.copyUserAndDevice=Copiar usuário de bloqueio e nome do dispositivo
eventView.entry.inUse.ignoreLock=Ignorar status de uso
# Notifications
## FileIsInUse Notification
notification.inUse.message=O arquivo está bloqueado por outro dispositivo
notification.inUse.description=O arquivo é aberto por %s no dispositivo %s. Solicite ao usuário para fechá-lo e o sincronizar novamente. Caso contrário, você pode ignorar o bloqueio e abri-lo mesmo assim.
notification.inUse.action=Ignorar bloqueio
notification.inUse.message=O arquivo está sendo usado em outro dispositivo
notification.inUse.description=O arquivo é aberto por %s em %s. Solicite a eles para fechá-lo e deixar a sincronização finalizar. Você pode ignorar o status para abri-lo agora, mas isso pode causar conflitos ou substituir novas alterações.
notification.inUse.action=Ignorar status de uso

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Действия события
eventView.entry.vaultLocked.description=Разблокируйте "%s" для деталей
eventView.entry.conflictResolved.message=Решённый конфликт
eventView.entry.conflictResolved.showDecrypted=Показать расшифрованный файл
eventView.entry.conflictResolved.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.conflict.message=Не удалось решить конфликты
eventView.entry.conflict.showDecrypted=Показать расшифрованный исходный файл
eventView.entry.conflict.copyDecrypted=Скопировать расшифрованный исходный путь
eventView.entry.conflict.showEncrypted=Показать конфликтующий зашифрованный файл
eventView.entry.conflict.copyEncrypted=Скопировать конфликтующий зашифрованный путь
eventView.entry.decryptionFailed.message=Ошибка дешифрования
eventView.entry.decryptionFailed.showEncrypted=Показать зашифрованный файл
eventView.entry.decryptionFailed.copyEncrypted=Скопировать зашифрованный путь
eventView.entry.brokenDirFile.message=Повреждённая ссылка на папку
eventView.entry.brokenDirFile.showEncrypted=Показать повреждённую зашифрованную ссылку
eventView.entry.brokenDirFile.copyEncrypted=Скопировать путь повреждённой ссылки
eventView.entry.brokenFileNode.message=Повреждённый узел файловой системы
eventView.entry.brokenFileNode.showEncrypted=Показать повреждённый зашифрованный узел
eventView.entry.brokenFileNode.copyEncrypted=Скопировать путь повреждённого зашифрованного узла
eventView.entry.brokenFileNode.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.inUse.message=Заблокированный файл
eventView.entry.inUse.message=Файл используется
eventView.entry.inUse.showDecrypted=Показать расшифрованный файл
eventView.entry.inUse.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.inUse.showEncrypted=Показать зашифрованный файл
eventView.entry.inUse.copyEncrypted=Скопировать зашифрованный путь
eventView.entry.inUse.copyUserAndDevice=Скопировать блокирующее имя пользователя и устройства
eventView.entry.inUse.ignoreLock=Игнорировать блокировку
eventView.entry.inUse.ignoreLock=Игнорировать статус использования
# Notifications
## FileIsInUse Notification
notification.inUse.message=Файл заблокирован другим устройством
notification.inUse.description=Файл открыт %s на устройстве %s. Попросите пользователя закрыть файл и синхронизировать снова. В противном случае вы можете проигнорировать блокировку и всё равно открыть его.
notification.inUse.action=Игнорировать блокировку
notification.inUse.message=Файл используется на другом устройстве
notification.inUse.description=Файл открыт %s на %s. Попросите закрыть файл и завершить синхронизацию. Вы можете проигнорировать статус и открыть его, но есть риск конфликтов или перезаписи новых изменений.
notification.inUse.action=Игнорировать статус использования

View File

@@ -647,33 +647,22 @@ eventView.cell.actionsButton.tooltip=Akcie udalostí
eventView.entry.vaultLocked.description=Podrobnosti získate odomknutím „%s“
eventView.entry.conflictResolved.message=Vyriešený konflikt
eventView.entry.conflictResolved.showDecrypted=Zobraziť dešifrovaný súbor
eventView.entry.conflictResolved.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.conflict.message=Riešenie konfliktu zlyhalo
eventView.entry.conflict.showDecrypted=Zobraziť dešifrovaný pôvodný súbor
eventView.entry.conflict.copyDecrypted=Kopírovať dešifrovanú, pôvodnú cestu
eventView.entry.conflict.showEncrypted=Zobraziť konfliktný zašifrovaný súbor
eventView.entry.conflict.copyEncrypted=Kopírovať konfliktnú zašifrovanú cestu
eventView.entry.decryptionFailed.message=Dešifrovanie zlyhalo
eventView.entry.decryptionFailed.showEncrypted=Zobraziť zašifrovaný súbor
eventView.entry.decryptionFailed.copyEncrypted=Skopírujte zašifrovanú cestu
eventView.entry.brokenDirFile.message=Nefunkčný odkaz na adresár
eventView.entry.brokenDirFile.showEncrypted=Zobraziť nefunkčný, šifrovaný odkaz
eventView.entry.brokenDirFile.copyEncrypted=Kopírovať cestu nefunkčného odkazu
eventView.entry.brokenFileNode.message=Poškodený uzol súborového systému
eventView.entry.brokenFileNode.showEncrypted=Zobraziť nefunkčný, zašifrovaný uzol
eventView.entry.brokenFileNode.copyEncrypted=Kopírovať cestu nefunkčného, šifrovaného uzla
eventView.entry.brokenFileNode.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.inUse.message=Zamknutý súbor
eventView.entry.inUse.message=Súbor sa používa
eventView.entry.inUse.showDecrypted=Zobraziť dešifrovaný súbor
eventView.entry.inUse.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.inUse.showEncrypted=Zobraziť zašifrovaný súbor
eventView.entry.inUse.copyEncrypted=Skopírujte zašifrovanú cestu
eventView.entry.inUse.copyUserAndDevice=Kopírovať zamykaného užívateľa a meno zariadenia
eventView.entry.inUse.ignoreLock=Ignorovať zamknutie
# Notifications
## FileIsInUse Notification
notification.inUse.message=Súbor je blokovaný iným zariadením
notification.inUse.description=Súbor je otvorený %s na zariadení %s. Požiadajte užívateľa o zatvorenie súboru a synchronizujte znovu. Alebo môžte ignorovať uzamknutie a napriek tomu ho otvoriť.
notification.inUse.action=Ignorovať zamknutie
notification.inUse.message=Súbor je používaný iným zariadením

View File

@@ -678,31 +678,20 @@ eventView.filterVaults=Filtrera efter valv
eventView.entry.vaultLocked.description=Lås upp "%s" för detaljer
eventView.entry.conflictResolved.message=Löst konflikt
eventView.entry.conflictResolved.showDecrypted=Visa dekrypterad fil
eventView.entry.conflictResolved.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.conflict.message=Konfliktlösning misslyckades
eventView.entry.conflict.showDecrypted=Visa dekrypterad, originalfil
eventView.entry.conflict.copyDecrypted=Kopiera dekrypterad, ursprunglig sökväg
eventView.entry.conflict.showEncrypted=Visa krypterad fil som inte kunde synkroniseras
eventView.entry.conflict.copyEncrypted=Kopiera krypterad sökväg som inte kunde synkroniseras
eventView.entry.decryptionFailed.message=Dekryptering misslyckades
eventView.entry.decryptionFailed.showEncrypted=Visa krypterad fil
eventView.entry.decryptionFailed.copyEncrypted=Kopiera krypterad sökväg
eventView.entry.brokenDirFile.message=Trasig kataloglänk
eventView.entry.brokenDirFile.showEncrypted=Visa trasig, krypterad länk
eventView.entry.brokenDirFile.copyEncrypted=Kopiera sökväg för trasig länk
eventView.entry.brokenFileNode.message=Trasig filsystemsnod
eventView.entry.brokenFileNode.showEncrypted=Visa trasig krypterad nod
eventView.entry.brokenFileNode.copyEncrypted=Kopiera sökväg för trasig, krypterad nod
eventView.entry.brokenFileNode.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.inUse.message=Låst fil
eventView.entry.inUse.showDecrypted=Visa dekrypterad fil
eventView.entry.inUse.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.inUse.showEncrypted=Visa krypterad fil
eventView.entry.inUse.copyEncrypted=Kopiera krypterad sökväg
eventView.entry.inUse.copyUserAndDevice=Kopiera blockerande användare och enhet
eventView.entry.inUse.ignoreLock=Ignorera lås
# Notifications
## FileIsInUse Notification
notification.inUse.action=Ignorera lås
## FileIsInUse Notification

View File

@@ -18,6 +18,10 @@ generic.button.print=Yazdır
generic.button.remove=Kaldır
## Vault state
vault.state.locked=Kilitli
vault.state.unlocked=Kilidi açık
vault.state.missing=Kayıt
vault.state.processing=İşleniyor
vault.state.error=Hata
# Error
@@ -180,6 +184,7 @@ hub.registerFailed.message=Cihaz kaydı başarısız oldu
hub.registerFailed.description.generic=Kayıt işleminde bir hata oluştu. Daha fazla bilgi için uygulama günlüğüne bakın.
hub.registerFailed.description.deviceAlreadyExists=Bu cihaz zaten farklı bir kullanıcı için kayıtlı. Kullanıcı hesabını değiştirmeyi veya farklı bir cihaz kullanmayı deneyin.
### Archived
hub.archived.message=Kasa arşivlendi
### Unauthorized
hub.unauthorized.message=Erişim engellendi
hub.unauthorized.description=Bu kasayı açma yetkiniz yok. Erişim talebinde bulunmak için kasanın sahibiyle iletişime geçin.
@@ -332,6 +337,7 @@ preferences.updates.lastUpdateCheck.never=asla
preferences.updates.lastUpdateCheck.recently=az önce
preferences.updates.lastUpdateCheck.daysAgo=%s gün önce
preferences.updates.lastUpdateCheck.hoursAgo=%s saat önce
preferences.updates.prohibitedDueToUnlockedVaults.1=Lütfen
preferences.updates.checkFailed=Güncelleme kontrolü başarısız oldu. Lütfen internet bağlantınızı kontrol edin veya daha sonra tekrar deneyin.
preferences.updates.upToDate=Cryptomator güncel.
@@ -608,12 +614,24 @@ shareVault.hub.instruction.2=2. Cryptomator Hub'da ekip üyesine erişim izni ve
shareVault.hub.openHub=Cryptomator Hub'ı
# Decrypt File Names
decryptNames.copyTable.tooltip=Tabloyu kopyala
decryptNames.clearTable.tooltip=Tabloyu temizle
decryptNames.column.encrypted=Şifrelendi
decryptNames.column.decrypted=Şifresi çözüldü
# Event View
eventView.title=Etkinlikler
eventView.filter.allVaults=Tümü
eventView.clearListButton.tooltip=Listeyi temizle
eventView.filterVaults=Kasayı süz
## event list entries
eventView.entry.inUse.message=Kullanılan dosya
eventView.entry.inUse.ignoreLock=Kullanım durumunu yok say
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
notification.inUse.message=Dosya başka bir cihazda kullanılıyor
notification.inUse.description=Dosya %s tarafından %s tarihinde açıldı. Onlardan dosyayı kapatmalarını ve senkronizasyonun tamamlanmasını beklemelerini isteyin. Dosyayı şimdi açmak için durumu görmezden gelebilirsiniz, ancak bu çakışmalara veya yeni değişikliklerin üzerine yazılmasına neden olabilir.
notification.inUse.action=Kullanım Durumunu Yok Say

View File

@@ -696,33 +696,25 @@ eventView.cell.actionsButton.tooltip=Дії події
eventView.entry.vaultLocked.description=Розблокуйте «%s» для отримання деталей
eventView.entry.conflictResolved.message=Конфлікт вирішено
eventView.entry.conflictResolved.showDecrypted=Показати розшифрований файл
eventView.entry.conflictResolved.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.conflict.message=Не вдалося вирішити конфлікт
eventView.entry.conflict.showDecrypted=Показати розшифрований оригінальний файл
eventView.entry.conflict.copyDecrypted=Скопіювати розшифрований оригінальний шлях
eventView.entry.conflict.showEncrypted=Показати конфліктуючий зашифрований файл
eventView.entry.conflict.copyEncrypted=Скопіювати конфліктуючий зашифрований шлях
eventView.entry.decryptionFailed.message=Не вдалося розшифрувати
eventView.entry.decryptionFailed.showEncrypted=Показати зашифрований файл
eventView.entry.decryptionFailed.copyEncrypted=Копіювати зашифрований шлях
eventView.entry.brokenDirFile.message=Пошкоджене посилання на каталог
eventView.entry.brokenDirFile.showEncrypted=Показати пошкоджене зашифроване посилання
eventView.entry.brokenDirFile.copyEncrypted=Копіювати шлях пошкодженого посилання
eventView.entry.brokenFileNode.message=Пошкоджений вузол файлової системи
eventView.entry.brokenFileNode.showEncrypted=Показати пошкоджений зашифрований вузол
eventView.entry.brokenFileNode.copyEncrypted=Скопіювати шлях до пошкодженого зашифрованого вузла
eventView.entry.brokenFileNode.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.inUse.message=Заблокований файл
eventView.entry.inUse.message=Файл використовується
eventView.entry.inUse.showDecrypted=Показати розшифрований файл
eventView.entry.inUse.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.inUse.showEncrypted=Показати зашифрований файл
eventView.entry.inUse.copyEncrypted=Копіювати зашифрований шлях
eventView.entry.inUse.copyUserAndDevice=Копіювати ім'я користувача та пристрою, що блокують
eventView.entry.inUse.ignoreLock=Ігнорування блокування
eventView.entry.inUse.ignoreLock=Ігнорувати статус використання
# Notifications
## FileIsInUse Notification
notification.inUse.message=Файл заблоковано іншим пристроєм
notification.inUse.description=Файл відкрито в «%s» на пристрої «%s». Попросіть користувача закрити файл і повторити синхронізацію. В іншому випадку можна проігнорувати блокування і відкрити файл.
notification.inUse.action=Ігнорування блокування
notification.inUse.message=Файл використовується на іншому пристрої
notification.inUse.description=Файл відкритий користувачем %s на пристрої %s. Попросіть його закрити файл, щоб завершити синхронізацію. Ви можете ігнорувати цей статус і відкрити файл зараз, але це може призвести до конфліктів або перезапису новіших змін.
notification.inUse.action=Ігнорувати статус використання

View File

@@ -629,26 +629,18 @@ eventView.clearListButton.tooltip=Xóa danh sách
eventView.entry.vaultLocked.description=Mở khóa "%s" để xem chi tiết
eventView.entry.conflictResolved.message=Đã xử lý xung đột
eventView.entry.conflictResolved.showDecrypted=Xem tệp đã giải mã
eventView.entry.conflictResolved.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.conflict.message=Giải quyết xung đột thất bại
eventView.entry.conflict.showDecrypted=Xem tệp gốc đã giải mã
eventView.entry.conflict.copyDecrypted=Sao chép đường dẫn gốc đã giải mã
eventView.entry.conflict.showEncrypted=Hiển thị tệp bị xung đột, đã mã hóa
eventView.entry.conflict.copyEncrypted=Sao chép đường dẫn mã hóa bị xung đột
eventView.entry.decryptionFailed.message=Giải mã thất bại
eventView.entry.decryptionFailed.showEncrypted=Hiển thị tệp đã mã hóa
eventView.entry.decryptionFailed.copyEncrypted=Sao chép đường dẫn tệp đã mã hóa
eventView.entry.brokenDirFile.message=Liên kết thư mục hỏng
eventView.entry.brokenDirFile.showEncrypted=Hiển thị liên kết mã hóa bị hỏng
eventView.entry.brokenDirFile.copyEncrypted=Sao chép đường dẫn liên kết bị hỏng
eventView.entry.brokenFileNode.message=Nút tập tin bị hỏng
eventView.entry.brokenFileNode.showEncrypted=Hiển thị nút mã hóa bị hỏng
eventView.entry.brokenFileNode.copyEncrypted=Sao chép đường dẫn nút mã hóa bị hỏng
eventView.entry.brokenFileNode.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.inUse.showDecrypted=Xem tệp đã giải mã
eventView.entry.inUse.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.inUse.showEncrypted=Hiển thị tệp đã mã hóa
eventView.entry.inUse.copyEncrypted=Sao chép đường dẫn tệp đã mã hóa
# Notifications

View File

@@ -678,31 +678,20 @@ eventView.cell.actionsButton.tooltip=事件操作
eventView.entry.vaultLocked.description=解锁“%s”查看详情
eventView.entry.conflictResolved.message=已解决冲突
eventView.entry.conflictResolved.showDecrypted=显示解密文件
eventView.entry.conflictResolved.copyDecrypted=复制解密路径
eventView.entry.conflict.message=冲突解决失败
eventView.entry.conflict.showDecrypted=显示解密后的原始文件
eventView.entry.conflict.copyDecrypted=复制解密后的原始路径
eventView.entry.conflict.showEncrypted=显示冲突的加密文件
eventView.entry.conflict.copyEncrypted=复制冲突的加密路径
eventView.entry.decryptionFailed.message=解密失败
eventView.entry.decryptionFailed.showEncrypted=显示加密文件
eventView.entry.decryptionFailed.copyEncrypted=复制加密路径
eventView.entry.brokenDirFile.message=目录链接已损坏
eventView.entry.brokenDirFile.showEncrypted=显示损坏的加密链接
eventView.entry.brokenDirFile.copyEncrypted=复制损坏链接的路径
eventView.entry.brokenFileNode.message=损坏的文件系统节点
eventView.entry.brokenFileNode.showEncrypted=显示损坏的加密节点
eventView.entry.brokenFileNode.copyEncrypted=复制损坏的加密节点的路径
eventView.entry.brokenFileNode.copyDecrypted=复制解密路径
eventView.entry.inUse.message=已锁定的文件
eventView.entry.inUse.showDecrypted=显示解密文件
eventView.entry.inUse.copyDecrypted=复制解密路径
eventView.entry.inUse.showEncrypted=显示加密文件
eventView.entry.inUse.copyEncrypted=复制加密路径
eventView.entry.inUse.copyUserAndDevice=复制锁定用户和设备名称
eventView.entry.inUse.ignoreLock=忽略锁定
# Notifications
## FileIsInUse Notification
notification.inUse.action=忽略锁定
## FileIsInUse Notification

View File

@@ -695,31 +695,25 @@ eventView.cell.actionsButton.tooltip=事件動作
eventView.entry.vaultLocked.description=解鎖「%s」以顯示詳細資訊
eventView.entry.conflictResolved.message=以解決的衝突
eventView.entry.conflictResolved.showDecrypted=顯示解密的檔案
eventView.entry.conflictResolved.copyDecrypted=複製解密路徑
eventView.entry.conflict.message=解決衝突失敗
eventView.entry.conflict.showDecrypted=顯示解密、原始的檔案
eventView.entry.conflict.copyDecrypted=複製解密、原始的路徑
eventView.entry.conflict.showEncrypted=顯示衝突的加密檔案
eventView.entry.conflict.copyEncrypted=複製衝突的加密檔案
eventView.entry.decryptionFailed.message=解密失敗
eventView.entry.decryptionFailed.showEncrypted=顯示加密的檔案
eventView.entry.decryptionFailed.copyEncrypted=複製加密路徑
eventView.entry.brokenDirFile.message=損壞的目錄連結
eventView.entry.brokenDirFile.showEncrypted=顯示損壞的加密路徑
eventView.entry.brokenDirFile.copyEncrypted=複製損壞的路徑連結
eventView.entry.brokenFileNode.message=損壞的檔案系統節點
eventView.entry.brokenFileNode.showEncrypted=顯示損壞的加密節點
eventView.entry.brokenFileNode.copyEncrypted=複製損壞的加密節點路徑
eventView.entry.brokenFileNode.copyDecrypted=複製解密路徑
eventView.entry.inUse.message=鎖定的檔案
eventView.entry.inUse.message=檔案正在使用中
eventView.entry.inUse.showDecrypted=顯示解密的檔案
eventView.entry.inUse.copyDecrypted=複製解密路徑
eventView.entry.inUse.showEncrypted=顯示加密的檔案
eventView.entry.inUse.copyEncrypted=複製加密路徑
eventView.entry.inUse.copyUserAndDevice=複製鎖定的使用者和裝置名稱
eventView.entry.inUse.ignoreLock=忽略鎖定
eventView.entry.inUse.ignoreLock=忽略使用狀態
# Notifications
## FileIsInUse Notification
notification.inUse.action=忽略鎖定
notification.inUse.message=檔案正在被另一部裝置使用中
notification.inUse.description=這個檔案正在由 %s 在 %s 開啟中。告訴他們關閉檔案讓同步完成。您可以忽略這個狀態並且馬上開啟,但是可能會造成衝突或把新的變更覆蓋掉。
notification.inUse.action=忽略使用狀態

View File

@@ -1,6 +1,7 @@
package org.cryptomator.common;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -8,12 +9,14 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import org.slf4j.Logger;
import java.util.Map;
import java.util.Properties;
public class SubstitutingPropertiesTest {
Logger logger = Mockito.mock(Logger.class);
SubstitutingProperties inTest;
@Nested
@@ -28,7 +31,7 @@ public class SubstitutingPropertiesTest {
@{@{appdir}},@{foobar}
Replacing several @{appdir} with @{appdir}., Replacing several foobar with foobar.""")
public void test(String propertyValue, String expected) {
SubstitutingProperties inTest = new SubstitutingProperties(Mockito.mock(Properties.class), Map.of("APPDIR", "foobar"));
SubstitutingProperties inTest = new SubstitutingProperties(Mockito.mock(Properties.class), Map.of("APPDIR", "foobar"), logger);
var result = inTest.process(propertyValue);
Assertions.assertEquals(expected, result);
}
@@ -39,7 +42,7 @@ public class SubstitutingPropertiesTest {
var props = new Properties();
props.setProperty("user.home", "OneUponABit");
inTest = new SubstitutingProperties(props, Map.of());
inTest = new SubstitutingProperties(props, Map.of(), logger);
var result = inTest.process("@{userhome}");
Assertions.assertEquals("OneUponABit", result);
}
@@ -48,7 +51,7 @@ public class SubstitutingPropertiesTest {
@ParameterizedTest(name = "Token \"{0}\" replaced with content of {1}")
@CsvSource(value = {"appdir, APPDIR, foobar", "appdata, APPDATA, bazbaz", "localappdata, LOCALAPPDATA, boboAlice"})
public void testEnvSubstitutions(String token, String envName, String expected) {
inTest = new SubstitutingProperties(new Properties(), Map.of(envName, expected));
inTest = new SubstitutingProperties(new Properties(), Map.of(envName, expected), logger);
var result = inTest.process("@{" + token + "}");
Assertions.assertEquals(expected, result);
}
@@ -62,7 +65,7 @@ public class SubstitutingPropertiesTest {
@Test
@DisplayName("Undefined properties are not processed")
public void testNoProcessingOnNull() {
inTest = Mockito.spy(new SubstitutingProperties(new Properties(), Map.of()));
inTest = Mockito.spy(new SubstitutingProperties(new Properties(), Map.of(), logger));
var result = inTest.getProperty("some.prop");
Assertions.assertNull(result);
@@ -75,7 +78,7 @@ public class SubstitutingPropertiesTest {
public void testNoProcessingOnNotCryptomator(String propKey) {
var props = new Properties();
props.setProperty(propKey, "someValue");
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of()));
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of(), logger));
var result = inTest.getProperty("some.prop");
Assertions.assertNull(result);
@@ -87,7 +90,7 @@ public class SubstitutingPropertiesTest {
public void testProcessing() {
var props = new Properties();
props.setProperty("cryptomator.prop", "someValue");
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of()));
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of(), logger));
Mockito.doReturn("someValue").when(inTest).process(Mockito.anyString());
inTest.getProperty("cryptomator.prop");
@@ -99,7 +102,7 @@ public class SubstitutingPropertiesTest {
public void testNoProcessingDefault() {
var props = Mockito.mock(Properties.class);
Mockito.when(props.getProperty("cryptomator.prop")).thenReturn(null);
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of()));
inTest = Mockito.spy(new SubstitutingProperties(props, Map.of(), logger));
Mockito.doReturn("someValue").when(inTest).process(Mockito.anyString());
var result = inTest.getProperty("cryptomator.prop", "a default");
@@ -120,7 +123,7 @@ public class SubstitutingPropertiesTest {
var delegate = Mockito.mock(Properties.class);
Mockito.doReturn("/home").when(delegate).getProperty("user.home");
Mockito.doReturn(raw).when(delegate).getProperty(key);
var inTest = new SubstitutingProperties(delegate, Map.of());
var inTest = new SubstitutingProperties(delegate, Map.of(), logger);
var result = inTest.getProperty(key);
@@ -142,7 +145,7 @@ public class SubstitutingPropertiesTest {
var env = Map.of("APPDATA", "C:\\Users\\JimFang\\AppData\\Roaming", //
"LOCALAPPDATA", "C:\\Users\\JimFang\\AppData\\Local", //
"APPDIR", "/squashfs1337/usr");
var inTest = new SubstitutingProperties(delegate, env);
var inTest = new SubstitutingProperties(delegate, env, logger);
var result = inTest.getProperty(key);

View File

@@ -29,7 +29,8 @@ public class SettingsJsonTest {
"checkForUpdatesEnabled": true,
"port": 8080,
"language": "de-DE",
"numTrayNotifications": 42
"numTrayNotifications": 42,
"trustedHosts": null
}
""";
@@ -44,6 +45,7 @@ public class SettingsJsonTest {
Assertions.assertTrue(jsonObj.autoCloseVaults);
Assertions.assertEquals("de-DE", jsonObj.language);
Assertions.assertEquals(42, jsonObj.numTrayNotifications);
Assertions.assertEquals(0, jsonObj.trustedHosts.size());
}
@SuppressWarnings("SpellCheckingInspection")

View File

@@ -0,0 +1,47 @@
package org.cryptomator.ui.keyloading.hub;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.Mockito;
import java.util.Set;
class CheckHostTrustControllerTest {
@ParameterizedTest
@CsvSource({
"https://auth.example.com, https://hub.example.com, true",
"https://hub.example.com, https://hub.example.com, true",
"https://auth.example.com, https://auth.example.com, true",
"https://auth.example.com, https://wrong.example.com, false",
"https://wrong.example.com, https://wrong.example.com, false"
})
void testContainsAllowedHosts(String apiBase, String authEndpoint, boolean expectedResult) {
var hubConfig = new HubConfig();
hubConfig.apiBaseUrl = apiBase;
hubConfig.authEndpoint = authEndpoint;
var controller = new CheckHostTrustController(Mockito.mock(), hubConfig, Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock(), Mockito.mock());
var actualResult = controller.containsAllowedHosts(Set.of("https://auth.example.com", "https://hub.example.com"));
Assertions.assertEquals(expectedResult, actualResult);
}
@ParameterizedTest
@CsvSource({
"https://example.com, https://example.com",
"https://example.com/foo/bar, https://example.com",
"https://example.com:8080, https://example.com:8080",
"https://user@example.com:8080/foo/bar, https://example.com:8080",
"https://user@example.com:443/foo/bar, https://example.com:443",
"http://user@example.com:80/foo/bar?foo=bar, http://example.com:80",
"http://user@example.com:8080/foo/bar?foo=bar, http://example.com:8080"
})
void testGetAuthority(String input, String expected) {
var actual = CheckHostTrustController.getAuthority(input);
Assertions.assertEquals(expected, actual);
}
}

View File

@@ -99,5 +99,14 @@
<packageUrl regex="true">^pkg:maven/org\.purejava/flatpak-update-portal@.*$</packageUrl>
<cpe>cpe:/a:flatpak:flatpak</cpe>
</suppress>
<suppress>
<notes><![CDATA[
Jetty is used as a local server only.
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.eclipse\.jetty/jetty-.*$</packageUrl>
<vulnerabilityName>CVE-2025-11143</vulnerabilityName>
<cve>CVE-2025-11143</cve>
</suppress>
</suppressions>